Downloading Files with ui-template

Hello all,

I've developed an app on Node-Red that creates some excel files and powerpoint files in the server. I want the client to be able to download the created files to client filesystem. So far I've used the below code and was able to download the testfile.txt.

<template>
    <div>
        <a href="/testfile.txt"
            Download="/testfile.txt" id="myLink">
            <v-btn type = "button"> Test Dynamic Download </v-btn>
        </a>
    </div>
</template>

But in the html code the path is hardcoded. Is there a way to change this path dynamically with javascript? Or is there another way to download files from server to client filesystem?

Thanks,

As with most things vue, you could probably bind them to computed variables (never tried with anchor attributes, but worth a go)

<template>
    <div>
        <a :href="fileURL" :download="filename">
            <v-btn type = "button"> Download {{ filename }}</v-btn>
        </a>
    </div>
</template>

Hi Steve,
Thanks for the quick reply but I could not understand how to pass the fileURL and filename with message to UI template node. Could you elaborate?

Send the template a msg, via a change or function node (where you set the msg.fileURL and msg.filename to whatever you please).

Hi Steve,
Just tried your solution in the flow below, but I was not able to download the file. I think the problem is with passing the fileURL and filename with msg.

[
    {
        "id": "628636866bf8780c",
        "type": "ui-template",
        "z": "47bb55aaa94ef2da",
        "group": "46b7a9fdbd833857",
        "page": "",
        "ui": "",
        "name": "",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n    <div>\n        <a :href=\"fileURL\" :download=\"filename\">\n            <v-btn type=\"button\"> Download {{ filename }}</v-btn>\n        </a>\n    </div>\n</template>\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                count: 0\n            }\n        },\n        watch: {\n            // watch for any changes of \"count\"\n            count: function () {\n                if (this.count % 5 === 0) {\n                    this.send({payload: 'Multiple of 5'})\n                }\n            }\n        },\n        computed: {\n            // automatically compute this variable\n            // whenever VueJS deems appropriate\n            formattedCount: function () {\n                return this.count + 'Apples'\n            }\n        },\n        methods: {\n            // expose a method to our <template> and Vue Application\n            increase: function () {\n                this.count++\n            }\n        },\n        mounted() {\n            // code here when the component is first loaded\n        },\n        unmounted() {\n            // code here when the component is removed from the Dashboard\n            // i.e. when the user navigates away from the page\n        }\n    }\n</script>\n<style>\n    /* define any styles here - supports raw CSS */\n    .my-class {\n        color: red;\n    }\n</style>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 560,
        "y": 600,
        "wires": [
            []
        ]
    },
    {
        "id": "3d8accd0febacb93",
        "type": "inject",
        "z": "47bb55aaa94ef2da",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 180,
        "y": 600,
        "wires": [
            [
                "7b1105576a36fc36"
            ]
        ]
    },
    {
        "id": "7b1105576a36fc36",
        "type": "change",
        "z": "47bb55aaa94ef2da",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "fileURL",
                "pt": "msg",
                "to": "/template_eval.xlsx",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "filename",
                "pt": "msg",
                "to": "template_eval.xlsx",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 360,
        "y": 600,
        "wires": [
            [
                "628636866bf8780c"
            ]
        ]
    },
    {
        "id": "46b7a9fdbd833857",
        "type": "ui-group",
        "name": "Workshop",
        "page": "b6fd77b16bf63f30",
        "width": "12",
        "height": "1",
        "order": 1,
        "showTitle": false,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "b6fd77b16bf63f30",
        "type": "ui-page",
        "name": "Automation Workshop Panel",
        "ui": "1dd9528287925708",
        "path": "/workshop",
        "icon": "cog",
        "layout": "grid",
        "theme": "09eceb51c02d0030",
        "order": -1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "1dd9528287925708",
        "type": "ui-base",
        "name": "Workshop Dashboard",
        "path": "/dashboard",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "navigationStyle": "none"
    },
    {
        "id": "09eceb51c02d0030",
        "type": "ui-theme",
        "name": "Default Theme",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094ce",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "15px",
            "widgetGap": "12px"
        }
    }
]

You need to either address msg.xxx or use computed properties

direct

    <div>
        <a :href="msg.fileURL" :download="msg.filename">
            <v-btn :disabled="!msg.filename" type="button"> Download {{ msg.filename || '' }}</v-btn>
        </a>
    </div>

via computed

</template>
    <div>
        <a :href="fileURL" :download="filename">
            <v-btn :disabled="!filename" type="button"> Download {{ filename || '' }}</v-btn>
        </a>
    </div>
</template>

<script>
    export default {
        computed: {
            fileURL: function () {
                return this.msg?.fileURL
            },
            filename: function () {
                return this.msg?.filename
            }
        }
    }
</script>
1 Like

Thank you Steve, this solved the issue.

I know it is not in the scope of this topic but how would I trigger the download without clicking on the button, when a msg with fileURL and filename properties is received by ui-template?

You can't. That would be a major security risk. Imagine vising www.i-am-super-save.com and they just download stuff to your computer!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.