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\">×</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"}]