Add a script dynamically?

So like many I have added some buttons etc to the header.

For these to work the required code needs to be added to the header, from a template in a group on every tab.

As the number of tabs grows, this makes maintaining the code difficult, as any changes need to be duplicated many times in each template.

So I thought it should be possible to save the code once and then just load it in each template.

I found some info about this on the web, and it does “seem” to load the script, but perhaps it is not running it, as none of the buttons etc are drawn on the header.

Has anyone got this working or have any pointers for me ?


<script>
  var headerscript = document.createElement('script');
  headerscript.setAttribute('type','text/javascript');
  headerscript.setAttribute('src','/home/pi/.node-red/public/myjs/header.js');
  headerscript.setAttribute('async', 'defer');
  document.body.appendChild(headerscript);
</script>

You could store in file or context and then use msg.template to load the template script from a normal template node or function or change.
here is a very simplified example.

[{"id":"1af346df.7103f9","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":290,"y":140,"wires":[["7e3b5bc7.cbc43c"]]},{"id":"7e3b5bc7.cbc43c","type":"template","z":"bf9e1e33.030598","name":"","field":"template","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<script>\n    {{{flow.template_header_script}}}\n</script>","output":"str","x":470,"y":140,"wires":[["a4c28729.b95928"]]},{"id":"a4c28729.b95928","type":"ui_template","z":"bf9e1e33.030598","group":"8b5cde76.edd58","name":"","order":8,"width":0,"height":0,"format":"","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":650,"y":140,"wires":[[]]},{"id":"2650e6f5.4de6ca","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"alert(\"Hello! I am an alert box!!\")","payloadType":"str","x":120,"y":60,"wires":[["4c02e77d.95df48"]]},{"id":"4c02e77d.95df48","type":"change","z":"bf9e1e33.030598","name":"","rules":[{"t":"set","p":"template_header_script","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":60,"wires":[[]]},{"id":"8b5cde76.edd58","type":"ui_group","name":"default","tab":"8f03e639.85956","order":1,"disp":false,"width":"12","collapse":false},{"id":"8f03e639.85956","type":"ui_tab","name":"Home","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

or simply load the script from an endpoint or static folder.

[{"id":"93d1e86d.a97468","type":"http in","z":"bf9e1e33.030598","name":"","url":"/js_script","method":"get","upload":false,"swaggerDoc":"","x":160,"y":80,"wires":[["d7fcbfff.286458"]]},{"id":"d7fcbfff.286458","type":"change","z":"bf9e1e33.030598","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"alert(\"an alert\")","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":40,"wires":[["5fb30c14.5253fc"]]},{"id":"5fb30c14.5253fc","type":"http response","z":"bf9e1e33.030598","name":"","statusCode":"","headers":{},"x":410,"y":80,"wires":[]},{"id":"528140c6.f07058","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":140,"wires":[["a4c28729.b95928"]]},{"id":"a4c28729.b95928","type":"ui_template","z":"bf9e1e33.030598","group":"8b5cde76.edd58","name":"","order":8,"width":0,"height":0,"format":"<script type=\"text/javascript\" src=\"/js_script\"></script>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":450,"y":140,"wires":[[]]},{"id":"8b5cde76.edd58","type":"ui_group","name":"default","tab":"8f03e639.85956","order":1,"disp":false,"width":"12","collapse":false},{"id":"8f03e639.85956","type":"ui_tab","name":"Home","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

Thanks -

This only seems to run the script once at the inject - it needs the script to be added to the page as it listens for msgs.

If I send the messages to the template node nothing happens (as it an empty template, after the injected
script)

Hope that makes sense ?

That is not a URL, it is a filing system path. The Dashboard runs in the context of the browser, any nodes run in the context of the Node-RED server. These are completely different environments, often on completely different devices.

Also, depending on where you added that code, it will change when the script is executed. And also, I don't believe that setAttribute('async', 'defer') is valid, there need to be two entries setAttribute('async', 'true') and setAttribute('defer', 'true') (I think, not tested).

Not sure what you mean by that but I think what you meant was that you want the script to run when a message is received? If so, you need to use an Angular v1 watch function.

1 Like

I thought you wanted to maintain the script in one place?
The script is loaded and will then run on. The alert() in the simple example only runs once unless you refresh, that may of confused the issue.
try this

[{"id":"93d1e86d.a97468","type":"http in","z":"bf9e1e33.030598","name":"","url":"/js_script","method":"get","upload":false,"swaggerDoc":"","x":150,"y":60,"wires":[["710d926e.2f1d6c"]]},{"id":"710d926e.2f1d6c","type":"template","z":"bf9e1e33.030598","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"var value = \"hello world\"; \nthis.scope.action = function() { return value; }","output":"str","x":300,"y":20,"wires":[["5fb30c14.5253fc"]]},{"id":"5fb30c14.5253fc","type":"http response","z":"bf9e1e33.030598","name":"","statusCode":"","headers":{},"x":400,"y":60,"wires":[]},{"id":"c860115e.5cea28","type":"inject","z":"bf9e1e33.030598","name":"load script","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"tab\":\"\"}","payloadType":"json","x":510,"y":20,"wires":[["a43dc4b9.dd5e08"]]},{"id":"a43dc4b9.dd5e08","type":"ui_ui_control","z":"bf9e1e33.030598","name":"","events":"all","x":680,"y":40,"wires":[[]]},{"id":"528140c6.f07058","type":"inject","z":"bf9e1e33.030598","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":250,"y":140,"wires":[["a4c28729.b95928"]]},{"id":"a4c28729.b95928","type":"ui_template","z":"bf9e1e33.030598","group":"8b5cde76.edd58","name":"","order":8,"width":0,"height":0,"format":"<script type=\"text/javascript\" src=\"/js_script\"></script>\n<md-button ng-click=\"send({payload:action()})\">\n    Click me to send a hello world\n</md-button>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":450,"y":140,"wires":[["c114099e.6af7f"]]},{"id":"c114099e.6af7f","type":"debug","z":"bf9e1e33.030598","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":640,"y":180,"wires":[]},{"id":"8b5cde76.edd58","type":"ui_group","name":"default","tab":"8f03e639.85956","order":1,"disp":false,"width":"12","collapse":false},{"id":"8f03e639.85956","type":"ui_tab","name":"Home","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

Edit the script in the template, refresh the page and the new script is loaded.
[edit] Or use ui-control to refresh dashboard tabs when you have edited the script. I added it to above example.

1 Like

"sound of cogs turning"

I tried this to see if I could load MY script -

[{"id":"8cf6d0f2f015e72c","type":"ui_template","z":"ccaa93c6.20c32","group":"09bc015144beae74","name":"Weather","order":8,"width":0,"height":0,"format":"<script type=\"text/javascript\" src=\"/js_script\"></script>","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":700,"y":1480,"wires":[["32342ba7.177b04"]]},{"id":"7ca9c3d6e3b9dc26","type":"file in","z":"ccaa93c6.20c32","name":"","filename":"/home/pi/.node-red/public/myjs/header.js","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":760,"y":1540,"wires":[["5fb30c14.5253fc"]]},{"id":"93d1e86d.a97468","type":"http in","z":"ccaa93c6.20c32","name":"","url":"/js_script","method":"get","upload":false,"swaggerDoc":"","x":490,"y":1540,"wires":[["7ca9c3d6e3b9dc26"]]},{"id":"5fb30c14.5253fc","type":"http response","z":"ccaa93c6.20c32","name":"","statusCode":"","headers":{},"x":1010,"y":1540,"wires":[]},{"id":"528140c6.f07058","type":"inject","z":"ccaa93c6.20c32","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":500,"y":1480,"wires":[["8cf6d0f2f015e72c"]]},{"id":"09bc015144beae74","type":"ui_group","name":"data","tab":"575545d5645126b6","order":1,"disp":true,"width":"24","collapse":false,"className":""},{"id":"575545d5645126b6","type":"ui_tab","name":"Weather","icon":"dashboard","disabled":false,"hidden":false}]

extract from MY script -

var theScope = scope;

        var myButton = document.getElementById("my-fault-button");
        if(!myButton) {
        var button = new Image();
        button.src = ''
        button.height = '50' ;
        button.addEventListener('click', fault);
        button.id = 'my-fault-button'
        button.style='margin: 20px auto'
        var titleBar = document.getElementById("nr-dashboard-toolbar");
        titleBar.append(button);
        }
        function fault(){
        msg={payload:"",topic:"fault"}
        theScope.send(msg);
        }

I "think" its working on one tab, need to do more tests. odd thing is I see lots of errors in firefox console, but not in chrome ?


Error: [$rootScope:inprog] http://errors.angularjs.org/1.8.2/$rootScope/inprog?p0=%24digest

@E1cid @TotallyInformation Thanks for the help, so it turns out to be much easier than I thought :wink:

I'm loading the script from disk and "serving" it to each template node. This means I can just edit the script once without having to update 30 odd templates :smiley: :beers:

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