[{"data":1,"prerenderedAt":384},["ShallowReactive",2],{"blog-\u002Fblog\u002F2023\u002F09\u002Fdashboard-notebook-layout":3,"blog-all-for-related":383},{"id":4,"title":5,"authors":6,"body":8,"cta":362,"date":363,"description":364,"extension":132,"image":365,"lastUpdated":362,"meta":366,"navigation":154,"path":371,"seo":372,"sitemap":373,"stem":374,"subtitle":375,"tags":376,"tldr":362,"video":362,"__hash__":382},"blog\u002Fblog\u002F2023\u002F09\u002Fdashboard-notebook-layout.md","Dynamic Markdown, Tables & Notebooks with Dashboard 2.0",[7],"joe-pavitt",{"type":9,"value":10,"toc":356},"minimark",[11,15,18,35,57,69,74,77,105,109,116,124,127,223,243,249,252,258,262,269,280,287,293,296,301,304,309,313,328,336,352],[12,13,14],"p",{},"Whilst we're still busy backporting through the existing Dashboard 1.0 features, we did want to highlight some new features we've built in Dashboard 2.0 released this week.",[12,16,17],{},"In our v0.4.0 release, we've introduced a new \"Notebook\" layout, alongside a new Table widget.",[12,19,20,21,28,29,34],{},"The Notebook layout is designed to allow users to create Dashboards structured like a Notebook (most often seen with the likes of ",[22,23,27],"a",{"href":24,"rel":25},"https:\u002F\u002Fjupyter.org\u002F",[26],"nofollow","Jupyter Notebooks"," or ",[22,30,33],{"href":31,"rel":32},"https:\u002F\u002Fobservablehq.com\u002F",[26],"ObservableHQ",").",[12,36,37,38,42,43,48,49,42,52,56],{},"Here we will deepdive into the Notebook layout, and show how, alongside our new ",[39,40,41],"strong",{},"Markdown Node"," (",[22,44,47],{"href":45,"rel":46},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-markdown.html",[26],"docs","), ",[39,50,51],{},"Table Node",[22,53,47],{"href":54,"rel":55},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-table.html",[26],") and others, it's becoming easier to create dynamic and interactive Dashboards.",[12,58,59],{},[60,61,62,63,68],"em",{},"Note: If you're not familiar with Markdown, it's a simple markup language that allows you to format text. You can learn more about it ",[22,64,67],{"href":65,"rel":66},"https:\u002F\u002Fwww.markdownguide.org\u002Fcheat-sheet\u002F",[26],"here",".",[70,71,73],"h2",{"id":72},"dashboard-hierarchy","Dashboard Hierarchy",[12,75,76],{},"As a quick introductory note ahead of our below guide, each Dashboard is structured accordingly:",[78,79,80,87,93,99],"ul",{},[81,82,83,86],"li",{},[39,84,85],{},"Widget",": An individual functional block, e.g. button, chart, slider",[81,88,89,92],{},[39,90,91],{},"Group",": A collection of widgets that render together",[81,94,95,98],{},[39,96,97],{},"Page",": A single page\u002Ftab in your Dashboard. Each page can have it's own Layout, in this case we'll use \"Notebook\"",[81,100,101,104],{},[39,102,103],{},"UI",": Contains a collection of pages, deployed from Node-RED, provides the basic side navigation to switch between Pages.",[70,106,108],{"id":107},"building-a-notebook","Building a Notebook",[12,110,111],{},[112,113],"img",{"alt":114,"src":115},"Example Notebook created in Dashboard","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fdb-notebook-example.png",[12,117,118,119,123],{},"To get started, drop your first widget (in this case, we'll add a ",[120,121,122],"code",{},"ui-markdown",") onto the Node-RED canvas. This in turn will prompt us to create our first Group\u002FPage\u002FDashboard which we can name and configure accordingly.",[12,125,126],{},"Let's add the following Markdown to our first widget:",[128,129,134],"pre",{"className":130,"code":131,"language":132,"meta":133,"style":133},"language-md shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Markdown Content\n\nHere we can render dynamic Markdown content that is\neasily _styled_.\n\n{% raw %}\nWe can inject `msg.payload`. For example, here is a\ntimestamp updating every second: {{ msg.payload }}\n{% endraw %}\n","md","",[120,135,136,149,156,163,182,187,193,211,217],{"__ignoreMap":133},[137,138,141,145],"span",{"class":139,"line":140},"line",1,[137,142,144],{"class":143},"sMK4o","# ",[137,146,148],{"class":147},"sBMFI","Markdown Content\n",[137,150,152],{"class":139,"line":151},2,[137,153,155],{"emptyLinePlaceholder":154},true,"\n",[137,157,159],{"class":139,"line":158},3,[137,160,162],{"class":161},"sTEyZ","Here we can render dynamic Markdown content that is\n",[137,164,166,169,173,177,179],{"class":139,"line":165},4,[137,167,168],{"class":161},"easily ",[137,170,172],{"class":171},"s7zQu","_",[137,174,176],{"class":175},"s5tWE","styled",[137,178,172],{"class":171},[137,180,181],{"class":161},".\n",[137,183,185],{"class":139,"line":184},5,[137,186,155],{"emptyLinePlaceholder":154},[137,188,190],{"class":139,"line":189},6,[137,191,192],{"class":161},"{% raw %}\n",[137,194,196,199,202,206,208],{"class":139,"line":195},7,[137,197,198],{"class":161},"We can inject ",[137,200,201],{"class":143},"`",[137,203,205],{"class":204},"sfazB","msg.payload",[137,207,201],{"class":143},[137,209,210],{"class":161},". For example, here is a\n",[137,212,214],{"class":139,"line":213},8,[137,215,216],{"class":161},"timestamp updating every second: {{ msg.payload }}\n",[137,218,220],{"class":139,"line":219},9,[137,221,222],{"class":161},"{% endraw %}\n",[12,224,225,226,228,229,232,233,235,236,239,240,242],{},"The joy of ",[120,227,122],{}," in Dashboard 2.0 is ",[60,230,231],{},"dynamic"," content, i.e. content that can be updated by passing messages to the ",[120,234,122],{}," node. We can wire an ",[120,237,238],{},"inject"," node, set it up to repeat every second, and connect it to ",[120,241,122],{},". Now, our Markdown content will automatically update show this value.",[12,244,245],{},[112,246],{"alt":247,"src":248},"Screenshot to show how an inject node can drive content of a ui-markdown node","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fdb-notebook-inject.png",[12,250,251],{},"Resulting in:",[12,253,254],{},[112,255],{"alt":256,"src":257},"Dynamic markdown with an updating timestamp every 1 second","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fmd-timestamp.gif",[70,259,261],{"id":260},"adding-more-widgets","Adding More Widgets",[12,263,264,265,268],{},"Because the Notebook is ",[60,266,267],{},"just"," a layout, we can still wire together any of the available widgets and existing nodes and display them accordingly.",[12,270,271,272,275,276,279],{},"Let's wire a ",[120,273,274],{},"ui-button",", HTTP Request, and ",[120,277,278],{},"ui-table"," node. When we click the button, it will perform the HTTP request, and then render the response in the table.",[12,281,282,283,286],{},"For this, we're going to use the Random Jokes API, and in particular, a call to ",[120,284,285],{},"https:\u002F\u002Fofficial-joke-api.appspot.com\u002Fjokes\u002Ften"," which will return 10 random jokes.",[12,288,289],{},[112,290],{"alt":291,"src":292},"Screenshot showing a simple Button > HTTP Request > Table flow","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fgenerate-jokes-flow.png",[12,294,295],{},"We can also re-order the widgets on the page using the Dashboard 2.0 sidebar (as you could in Dashboard 1.0).",[12,297,298],{},[112,299],{"alt":247,"src":300},"\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fdb-notebook-order.png",[12,302,303],{},"The above effort results in the following output in our Notebook:",[12,305,306],{},[112,307],{"alt":247,"src":308},"\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fdb-notebook-jokes-table.png",[70,310,312],{"id":311},"what-else-is-new-in-040","What else is new in 0.4.0?",[12,314,315,316,321,322,327],{},"The above demonstrates just a few of the new features in the 0.4.0 Release, but we've also added ",[22,317,320],{"href":318,"rel":319},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Freleases\u002Ftag\u002Fv0.4.0",[26],"other fixes and improvements",". In particular, I want to call out Steve's great work on implementing custom class injection, the first of our new ",[22,323,326],{"href":324,"rel":325},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fuser\u002Fdynamic-properties.html",[26],"\"Dynamic Properties\"",", of which there will be more (e.g. visibility, disabled, etc.) to come.",[12,329,330,331,68],{},"As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs\u002Fcomplaints or general feedback, please do reach out, and raise issues on our relevant ",[22,332,335],{"href":333,"rel":334},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard",[26],"GitHub repository",[78,337,338,345],{},[81,339,340],{},[22,341,344],{"href":342,"rel":343},"https:\u002F\u002Fgithub.com\u002Forgs\u002FFlowFuse\u002Fprojects\u002F15\u002Fviews\u002F1",[26],"Dashboard 2.0 Activity Tracker",[81,346,347],{},[22,348,351],{"href":349,"rel":350},"https:\u002F\u002Fgithub.com\u002Forgs\u002FFlowFuse\u002Fprojects\u002F15\u002Fviews\u002F4",[26],"Dashboard 2.0 Planning Board",[353,354,355],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s5tWE, html code.shiki .s5tWE{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#F07178;--shiki-default-font-style:italic;--shiki-dark:#F07178;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":133,"searchDepth":165,"depth":165,"links":357},[358,359,360,361],{"id":72,"depth":151,"text":73},{"id":107,"depth":151,"text":108},{"id":260,"depth":151,"text":261},{"id":311,"depth":151,"text":312},null,"2023-09-06","Explore the latest features in Dashboard 2.0's v0.4.0 release, including \"Notebook\" layout, dynamic Markdown & Table widgets, and additional enhancements.","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fdb-notebook-tile.png",{"excerpt":367},{"type":9,"value":368},[369],[12,370,14],{},"\u002Fblog\u002F2023\u002F09\u002Fdashboard-notebook-layout",{"title":5,"description":364},{"loc":371},"blog\u002F2023\u002F09\u002Fdashboard-notebook-layout","A dive into the new features available in 0.4.0 - The \"Notebook\" Layout and new dynamic Markdown & Table widgets.",[377,378,379,380,381],"posts","releases","community","changelog","dashboard","6fU7RFKqryqT85Q9gudjfXS-bzzPLWcHsHsCgXVvfyo",[],1785528687045]