Dynamic dashboard ui-template with automatic resizing

When a dashboard ui-template node has dynamic content, it will not automatically resize to fit the new content after a change. This example shows how to manage a dynamic table and trigger a resize of the node so the whole table is visible. The resizing is triggered by setting the height of the parent element of a first-level div in the template to an empty string:

$("#tblIntervals").parent().css("height","");

Additionally, a loop back delay node is used as a workaround to replicate any changes to other possibly connected clients.

[{"id":"d2f356e8.51d6e8","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"afb44ebd.9f5c88","type":"delay","z":"d2f356e8.51d6e8","name":"replicate on other displays","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":500,"y":240,"wires":[["8cfe7bc7.f574e8"]]},{"id":"8cfe7bc7.f574e8","type":"ui_template","z":"d2f356e8.51d6e8","group":"1ed11c7.232e364","name":"Interval list","order":5,"width":"0","height":"0","format":"<script>\n(function(scope) {\n    scope.startH = 0;\n    scope.startM = 0;\n    scope.duration = 45;\n    scope.valve1 = 1;\n    scope.valve2 = 0;\n    scope.intervals = [];\n\n    scope.$watch('msg', function(msg){\n        if (msg) {\n            if (msg.payload) {\n                scope.intervals = msg.payload;\n                // Force repaint to adjust to table height\n                $(\"#tblIntervals\").parent().css(\"height\",\"\");\n            }\n        }\n    } );\n    \n    scope.valveList = function(valves) {\n        for (var arValves=[], i=0; 1<<i <= valves; i++) { if (valves & 1<<i) arValves.push(i+1); }\n        return arValves.join(\"+\");\n    }\n\n    scope.add = function() {\n        scope.intervals.push({\n            startH : scope.startH,\n            startM : scope.startM,\n            duration : scope.duration,\n            valve : scope.valve1 + scope.valve2\n        });\n        $(\"#tblIntervals\").parent().css(\"height\",\"\");\n        scope.intervals.sort(function(a,b){return a.startH*60+a.startM-b.startH*60-b.startM})\n        scope.send({payload:scope.intervals});\n    };\n\n    scope.remove = function(row) {\n        var removed = scope.intervals.splice(scope.intervals.indexOf(row),1);\n        $(\"#tblIntervals\").parent().css(\"height\",\"\");\n        scope.startH = removed[0].startH;\n        scope.startM = removed[0].startM;\n        scope.duration = removed[0].duration;\n        scope.valve1 = (removed[0].valve & 1);\n        scope.valve2 = (removed[0].valve & 2);\n        scope.send({payload:scope.intervals});\n    }\n})(scope)\n</script>\n\n<style>\ntable.intervals {\n    border-collapse: collapse;\n    width: 100%;\n}\ntable.intervals td, table.intervals th {\n    border: 1px solid #555;\n    padding: 1px;\n}\ntable.intervals tr:nth-child(odd) { background-color: #555; }\ntable.intervals tr:hover {background-color: #888;}\n</style>\n\n<div style=\"\">\n    <p class=\"nr-dashboard-cardtitle\">Add new interval</p>\n    <table>\n        <tr>\n            <td>Start:</td>\n            <td>\n                <input type=number min=0 max=23 style=\"width:35px\" ng-model=\"startH\">:<input type=number min=0 max=59 style=\"width:35px\" ng-model=\"startM\">\n            </td>\n        </tr>\n        <tr>\n            <td>Duration:</td>\n            <td><input type=number min=1 style=\"width:50px;\" ng-model=\"duration\"> s</td>\n        </tr>\n        <tr>\n            <td>Valve</td>\n            <td>\n                <input id=\"chkValve1\" type=\"checkbox\" ng-model=\"valve1\" ng-true-value=1 ng-false-value=0><label for=\"chkValve1\">1</label>\n                <input id=\"chkValve2\" type=\"checkbox\" ng-model=\"valve2\" ng-true-value=2 ng-false-value=0><label for=\"chkValve2\">2</label>\n            </td>\n        </tr>\n        <tr>\n            <td colspan=2><md-button ng-click=\"add()\">Add</md-button></td>\n        </tr>\n    </table>\n</div>\n\n<div id=\"tblIntervals\">\n    <p class=\"nr-dashboard-cardtitle\">Time Intervals</p>\n    <table class=\"intervals\">\n        <tr>\n            <td>Start</td>\n            <td>Durat.</td>\n            <td colspan=2>Valve</td>\n        </tr>\n        <tr ng-repeat=\"i in intervals\">\n            <td>{{i.startH}}:{{(\"0\"+i.startM).slice(-2)}}</td>\n            <td align=\"right\" style=\"padding-left:0px; padding-right:10px;\">{{i.duration}} s</td>\n            <td align=\"center\">{{valveList(i.valve)}}</td>\n            <td style=\"cursor:pointer;\" ng-click=\"remove(i)\" title=\"Remove\">&times;</td>\n        </tr>\n    </table>\n</div>","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":490,"y":180,"wires":[["afb44ebd.9f5c88"]]},{"id":"3a6318aa.8adf98","type":"inject","z":"d2f356e8.51d6e8","name":"init","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":110,"y":180,"wires":[["810e4ac0.2f53d"]]},{"id":"810e4ac0.2f53d","type":"change","z":"d2f356e8.51d6e8","name":"set sample data","rules":[{"t":"set","p":"payload","pt":"msg","to":"[{\"startH\":7,\"startM\":0,\"duration\":45,\"valve\":1}]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":180,"wires":[["8cfe7bc7.f574e8"]]},{"id":"1ed11c7.232e364","type":"ui_group","z":"","name":"Dynamic table example","tab":"12628f32.c04c99","disp":true,"width":"4","collapse":false},{"id":"12628f32.c04c99","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]

Flow Info

Created 6 years, 2 months ago
Rating: 5 2

Owner

Actions

Rate:

Node Types

Core
  • change (x1)
  • delay (x1)
  • inject (x1)
Other
  • tab (x1)
  • ui_group (x1)
  • ui_tab (x1)
  • ui_template (x1)

Tags

  • resize
  • dashboard
  • dynamic
  • table
  • template
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option