{"id":27513,"date":"2020-08-09T13:20:39","date_gmt":"2020-08-09T11:20:39","guid":{"rendered":"https:\/\/pi3g.com\/?p=27513"},"modified":"2020-08-09T13:20:39","modified_gmt":"2020-08-09T11:20:39","slug":"vuetify-open-detail-row-programmatically","status":"publish","type":"post","link":"https:\/\/pi3g.com\/de\/vuetify-open-detail-row-programmatically\/","title":{"rendered":"Vuetify :: Detailzeile programmatisch \u00f6ffnen"},"content":{"rendered":"<p>For the upcoming PiCockpit v2.0 release, I am preparing a GPIO application.<\/p>\n<p>The GPIO entries are table rows \u2013 and to configure them, we want to be able to access the detail row by clicking on the configure button:<\/p>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"484\" height=\"344\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image_thumb-5.png\" border=\"0\"><\/a><\/p>\n<p>clicking either the chevron or the configure button will lead to the detail row opening:<\/p>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image-6.png\"><img loading=\"lazy\" decoding=\"async\" width=\"485\" height=\"429\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image_thumb-6.png\" border=\"0\"><\/a><\/p>\n<p>Here is the corresponding Vue template:<\/p>\n<blockquote>\n<p>&lt;v-data-table<br \/>&nbsp;&nbsp; :headers=&#8221;gpioInputTableHeaders&#8221;<br \/>&nbsp;&nbsp; <strong>:items=&#8221;gpioInputTableData&#8221;<\/strong><br \/>&nbsp;&nbsp; :loading=&#8221;loading&#8221;<br \/>&nbsp;&nbsp; <strong>item-key=&#8221;bcm_id&#8221;<\/strong><br \/>&nbsp;&nbsp; loading-text=&#8221;waiting for data &#8230; &#8220;<br \/>&nbsp;&nbsp; fixed-header<br \/>&nbsp; <strong> show-expand<br \/>&nbsp;&nbsp; :expanded.sync=&#8221;gpioInputTableExpanded&#8221;<\/strong><br \/>&nbsp;&nbsp; class=&#8221;elevation-1&#8243;<br \/>\n&gt;<br \/>&nbsp;&nbsp; &lt;template v-slot:item.value=&#8221;{ item }&#8221;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;span v-if=&#8221;!onlineStateBinary&#8221;&gt;&lt;i class=&#8221;fa fa-warning&#8221;&gt;&lt;\/i&gt; &lt;\/span&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;span v-if=&#8221;item.state == 1&#8243;&gt;&lt;v-icon x-small&gt;mdi-arrow-up-bold&lt;\/v-icon&gt;{{item.high}}&lt;\/span&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;span v-else-if=&#8221;item.state == 0&#8243;&gt;&lt;v-icon x-small&gt;mdi-arrow-down-bold&lt;\/v-icon&gt;{{item.low}}&lt;\/span&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;span v-else-if=&#8221;item.state == 2&#8243;&gt;N\/A&lt;\/span&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;span v-else-if=&#8221;item.state == 4&#8243;&gt;N\/A&lt;\/span&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;span v-else&gt;ERROR&lt;\/span&gt;<br \/>&nbsp;&nbsp; &lt;\/template&gt;<br \/>&nbsp;&nbsp; &lt;template <strong>v-slot:item.action=&#8221;{ item }&#8221;<\/strong>&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;v-btn<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=&#8221;mr-2&#8243;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color=&#8221;primary&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; small<br \/><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @click.ctrl.exact=&#8221;configGpioAll(&#8216;input&#8217;, item)&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @click.exact=&#8221;configGpio(&#8216;input&#8217;, item)&#8221;<\/strong><br \/>&nbsp;&nbsp;&nbsp;&nbsp; &gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;v-icon left small class=&#8221;mr-2&#8243;&gt;mdi-hammer-wrench&lt;\/v-icon&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Configure<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/v-btn&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;v-btn<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class=&#8221;mr-2&#8243;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color=&#8221;error&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; small<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @click=&#8221;remove_gpio(&#8216;input&#8217;, item.bcm_id)&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;v-icon left small class=&#8221;mr-2&#8243;&gt;mdi-trash-can-outline&lt;\/v-icon&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Remove<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/v-btn&gt;<br \/>&nbsp;&nbsp; &lt;\/template&gt;<br \/><strong>&nbsp;&nbsp; &lt;template v-slot:expanded-item=&#8221;{ headers, item }&#8221;&gt;<\/strong><br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;!&#8211; v-on:pull-change=&#8221;pullChange&#8221; &#8211;&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; <strong>&lt;td :colspan=&#8221;headers.length&#8221;&gt;<\/strong><br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;gpioInputDetailRow <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :rowData=&#8221;item&#8221; <br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v-on:pull-change=&#8221;pullChange&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v-on:debounce-change=&#8221;debounceChange&#8221;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;&lt;\/gpioInputDetailRow&gt;<br \/>&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/td&gt;<br \/>&nbsp;&nbsp; &lt;\/template&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br \/>\n&lt;\/v-data-table&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/p>\n<p><\/p>\n<\/blockquote>\n<p>As you can see I wrap another component for the expanded-item slot. This component is irrelevant for our current discussion, therefore I do not provide code for it.<\/p>\n<p>I have highlighted the important bits above.<\/p>\n<p>These are:<\/p>\n<ul>\n<li><strong>:items=&#8221;gpioInputTableData&#8221; \u2013 <\/strong>this links the data to the items key of the Vuetify data table<\/li>\n<li><strong>item-key=&#8221;bcm_id&#8221; \u2013 <\/strong>this is important to give a unique key for every entry of your data (which is given as an array to items).<\/li>\n<ul>\n<li>if you omit setting this up, all your detail rows will open at once and close at once.<\/li>\n<li>the value should be unique as discussed; in case your data potentially has duplicate values across all keys you need to creat an additional unique key in it<\/li>\n<\/ul>\n<li><strong>show-expand \u2013 <\/strong>this will show the Chevrons <a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image-7.png\"><img loading=\"lazy\" decoding=\"async\" width=\"15\" height=\"15\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image_thumb-7.png\" border=\"0\"><\/a><\/li>\n<li><strong>:expanded.sync=&#8221;gpioInputTableExpanded&#8221;<\/strong> \u2013 note the use of the sync modifier, to synchronize changes back! This is a variable to store the currently expanded items in.<\/li>\n<\/ul>\n<p>then:<\/p>\n<ul>\n<li>&nbsp;<strong>@click.ctrl.exact=&#8221;configGpioAll(&#8216;input&#8217;, item)&#8221;<\/strong> \u2013 as you can see, on Ctrl + Click, I am calling a utility function to toggle all expansion slots, depending on the state of the current item. I pass the item to it, which is to be expanded<\/li>\n<li><strong>@click.exact=&#8221;configGpio(&#8216;input&#8217;, item)&#8221;<\/strong> \u2013 as you see, on an exact click, I call another utility function (we will get to these shortly), ALSO passing in the current item<\/li>\n<\/ul>\n<p>finally:<\/p>\n<ul>\n<li><strong>&lt;template v-slot:expanded-item=&#8221;{ headers, item }&#8221;&gt;<\/strong> \u2013 this Slot holds the expanded item. Passing in the headers is important:<\/li>\n<li><strong>&lt;td :colspan=&#8221;headers.length&#8221;&gt; \u2013 <\/strong>\u2026 because it allows you to create a cell which spans all columns<\/li>\n<\/ul>\n<p><\/p>\n<p>Here is how my data() looks like (excerpt):<\/p>\n<blockquote>\n<p>data () {<br \/>&nbsp;&nbsp; return {<br \/>&nbsp;&nbsp;&nbsp;&nbsp; loading: true,<br \/>&nbsp;&nbsp;&nbsp;&nbsp; gpioInputTableData: [],<br \/>&nbsp;&nbsp;&nbsp;&nbsp; gpioInputTableHeaders: [<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { text: &#8216;Name&#8217;, value: &#8216;name&#8217;},<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { text: &#8216;Value&#8217;, value: &#8216;value&#8217;},<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { text: &#8216;Action&#8217;, value: &#8216;action&#8217;}<br \/>&nbsp;&nbsp;&nbsp;&nbsp; ],<br \/>&nbsp;&nbsp;&nbsp;&nbsp; gpioInputTableExpanded: []\n<\/p>\n<p>&nbsp; }<br \/>\n},\n<\/p>\n<\/blockquote>\n<p>And here are the two utility functions:<\/p>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image-8.png\"><img loading=\"lazy\" decoding=\"async\" width=\"638\" height=\"514\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image_thumb-8.png\" border=\"0\"><\/a><\/p>\n<blockquote>\n<p>configGpioAll: function (source, item){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var ref = null;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var refTable = null;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (source === &#8216;input&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ref = &#8216;gpioInputTableExpanded&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; refTable = &#8216;gpioInputTableData&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (source === &#8216;output&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ref = &#8216;gpioOutputTableExpanded&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; refTable = &#8216;gpioOutputTableData&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else if (source === &#8216;softpwm&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ref = &#8216;gpioSoftPWMTableExpanded&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; refTable = &#8216;gpioSoftPWMTableData&#8217;;<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ do we match for this item (is it opened?)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong> if (this[ref].filter(e =&gt; e.bcm_id === item.bcm_id).length &gt; 0){<\/strong><br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ remove all items<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&#8220;configGpioAll: removing&#8221;);<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this[ref] = [];<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(&#8220;configGpioAll: adding&#8221;);<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ add all items<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this[ref] = this[refTable];<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<\/p>\n<\/blockquote>\n<p>in configGpioAll, I check whether I have the input, output or softpwm table (to avoid duplicating code), and set up references to the appropriate expanded variable (see data above), and the table data itself.<\/p>\n<p>If the item is contained within the expanded data (I match by my unique key, bcm_id), I close all expanded items \u2013 <strong>for this I simply set the expanded variable to an empty array<\/strong>.<\/p>\n<p>If the item is NOT contained within the expanded data, I open all items, by putting all the table data into it. <\/p>\n<p><strong><font style=\"background-color: rgb(255, 255, 0);\">Note: it might be worthwhile to experiment whether an array of objects containing just the unique keys might be enough for this to work as well.<\/font> <\/strong><\/p>\n<p><a href=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image-9.png\"><img loading=\"lazy\" decoding=\"async\" width=\"615\" height=\"368\" title=\"image\" style=\"display: inline; background-image: none;\" alt=\"image\" src=\"https:\/\/pi3g.com\/wp-content\/uploads\/2020\/08\/image_thumb-9.png\" border=\"0\"><\/a><\/p>\n<blockquote>\n<p>configGpio: function (source, item){<br \/>&nbsp;&nbsp; var ref = null;<br \/>&nbsp;&nbsp; if (source === &#8216;input&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ref = &#8216;gpioInputTableExpanded&#8217;;<br \/>&nbsp;&nbsp; } else if (source === &#8216;output&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ref = &#8216;gpioOutputTableExpanded&#8217;;<br \/>&nbsp;&nbsp; } else if (source === &#8216;softpwm&#8217;){<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ref = &#8216;gpioSoftPWMTableExpanded&#8217;;<br \/>&nbsp;&nbsp; }<br \/>&nbsp;&nbsp; if (this[ref].filter(e =&gt; e.bcm_id === item.bcm_id).length &gt; 0){<br \/>&nbsp;&nbsp;&nbsp;&nbsp; \/\/ remove the item<br \/>&nbsp;&nbsp;&nbsp;&nbsp; this[ref] = this[ref].filter(e =&gt; e.bcm_id !== item.bcm_id);<br \/>&nbsp;&nbsp; }<br \/>&nbsp;&nbsp; else {<br \/>&nbsp;&nbsp;&nbsp;&nbsp; \/\/ add the item<br \/>&nbsp;&nbsp;&nbsp;&nbsp; this[ref].push(item);<br \/>&nbsp;&nbsp; }<br \/>\n},<\/p>\n<\/blockquote>\n<p>in configGpio, I do similar things \u2013 but instead of removing all items, I filter and remove the item which matches the bcm_id I got passed in.<\/p>\n<p>If the item is not present, I push it into the expanded variable. <\/p>\n<p>This is the reason that I need the entire item, not just the bcm_id. I chose to have symmetry in my code, so configGpioAll which technically only needs the bcm_id also gets the entire item. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>F\u00fcr das kommende PiCockpit v2.0 Release bereite ich eine GPIO Anwendung vor. Die GPIO-Eintr\u00e4ge sind Tabellenzeilen - und um sie zu konfigurieren, wollen wir in der Lage sein, auf die Detailzeile zuzugreifen, indem wir auf die Schaltfl\u00e4che \"Konfigurieren\" klicken: Ein Klick auf den Chevron oder auf die Schaltfl\u00e4che \"Konfigurieren\" f\u00fchrt zum \u00d6ffnen der Detailzeile: Hier...<\/p>","protected":false},"author":830,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[666,853],"tags":[857,859,854,855,856,858],"class_list":["post-27513","post","type-post","status-publish","format-standard","hentry","category-vue-js","category-vuetify","tag-close-detail-row","tag-data-tables","tag-detail-row","tag-drive-detail-row","tag-open-detail-row","tag-v-data-table"],"_links":{"self":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts\/27513","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/users\/830"}],"replies":[{"embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/comments?post=27513"}],"version-history":[{"count":1,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts\/27513\/revisions"}],"predecessor-version":[{"id":27514,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/posts\/27513\/revisions\/27514"}],"wp:attachment":[{"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/media?parent=27513"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/categories?post=27513"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pi3g.com\/de\/wp-json\/wp\/v2\/tags?post=27513"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}