
{"id":213,"date":"2017-05-02T18:30:16","date_gmt":"2017-05-02T08:30:16","guid":{"rendered":"http:\/\/bakke.online\/?p=213"},"modified":"2017-05-02T18:30:16","modified_gmt":"2017-05-02T08:30:16","slug":"how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-3","status":"publish","type":"post","link":"https:\/\/www.bakke.online\/index.php\/2017\/05\/02\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-3\/","title":{"rendered":"How-to: Tree lookups in Dynamics AX reference group controls, part 3"},"content":{"rendered":"<p>This is something that keeps coming up on different projects, so I&#8217;d like to share how I usually do this. \u00a0There are some examples out there which try to describe a very generic solution which can be used in almost any case. \u00a0However, such solutions very quickly become <strong>very<\/strong> complex. \u00a0To keep things simple, I have tried to keep this example fairly specific but adaptable to a wide range of circumstances. \u00a0Specifically this example is using a fixed hierarchy structure with a fixed number of levels, each of which will be represented by a different table.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/index.php\/2017\/04\/11\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-1\/\">Earlier<\/a>, I have introduced the data model sitting behind the tree lookup, and <a href=\"https:\/\/www.bakke.online\/index.php\/2017\/04\/12\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-2\/\">last time<\/a> I showed how to build the actual tree structure.<\/p>\n<p>If you have not read these posts yet, I recommend you do that first, to help with the understanding of this post, which explains how to provide a more useful tree reference in the calling form.<\/p>\n<p><!--more--><\/p>\n<h3>Introducing a better reference<\/h3>\n<p>Remember from last time, how the books form has a multi-segmented control for the shelf reference:<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/books_populated.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-203\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/04\/books_populated.png\" alt=\"\" width=\"255\" height=\"137\" \/><\/a><\/p>\n<p>This can be replaced by a single field providing a unique reference. \u00a0In a tree structure, a path is a useful and unique concept, so we&#8217;ll use that. \u00a0Note that this step is not required in any way, so if you are happy to use the segmented reference group control, go right ahead. \u00a0Have a read through the following, though, to compare the results and see what works best for you.<\/p>\n<p>First we create a new Extended Data Type called EB_ShelfPath:<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_shelfpath.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-214\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_shelfpath.png\" alt=\"\" width=\"308\" height=\"68\" \/><\/a><\/p>\n<p>Drag this new data type to the EB_Shelves table to create a new field:<a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/pathidx.png\"><br \/>\n<\/a><\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/shelves_path.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-215\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/shelves_path.png\" alt=\"\" width=\"306\" height=\"35\" \/><\/a><\/p>\n<p>We&#8217;re going to put this into the reference group on the books form, so we&#8217;ll need a unique index in order for the reference group to do the right thing.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/pathidx.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-216\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/pathidx.png\" alt=\"\" width=\"454\" height=\"137\" \/><\/a><\/p>\n<p>The reference group will need a field group as well, so we add a new field group containing only the Path field:<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_shelves_path.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-217\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_shelves_path.png\" alt=\"\" width=\"374\" height=\"87\" \/><\/a><\/p>\n<p>Note that we set the label to &#8220;Shelf&#8221;. \u00a0This will appear in the books form as the reference group&#8217;s label.<\/p>\n<h3>Keeping the path up to date<\/h3>\n<p>We&#8217;ll need to make sure the path is kept up to date as it is possible to rename libraries, aisles, racks and shelves. \u00a0The path must also be set automatically when new shelves are added to the system.<\/p>\n<p>To support all of this, we&#8217;ll add a new method to the EB_Shelves table, called getPath:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public display<\/strong><\/span> EB_ShelfPath getPath()\n{\n  EB_Racks     racks;\n  EB_Aisles    aisles;\n  EB_Libraries libraries;\n\n  <span style=\"color: #0000ff;\"><strong>select firstonly<\/strong><\/span> RackId\n    <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> racks\n   <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> racks.RecId     == this.RackRecId\n    <span style=\"color: #0000ff;\"><strong>join<\/strong><\/span> AisleId\n    <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> aisles\n   <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> aisles.RecId    == racks.AisleRecId\n    <span style=\"color: #0000ff;\"><strong>join<\/strong><\/span> LibraryId\n    <span style=\"color: #0000ff;\"><strong>from<\/strong><\/span> libraries\n   <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> libraries.RecId == aisles.LibraryRecId;\n\n  <span style=\"color: #0000ff;\"><strong>return strFmt<\/strong><\/span>( <span style=\"color: #993300;\">\"%1=&gt;%2=&gt;%3=&gt;%4\"<\/span>, libraries.LibraryId, aisles.AisleId, racks.RackId, this.ShelfId );\n}<\/pre>\n<p>Quite simple, really. \u00a0We do a select of the ancestors of the current shelf, i.e. the rack, aisle and library. \u00a0For performance reasons we also use field lists to limit the amount of information retrieved. \u00a0(I&#8217;ll write a series of performance related posts in a few weeks, to explain how and why this improves performance) \u00a0Once we have the fields, we return a formatted path string.<\/p>\n<p>Also override the insert() and update() methods to keep the path up to date:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public void<\/strong> <\/span>insert()\n{\n  this.Path = this.getPath();\n\n<span style=\"color: #0000ff;\"><strong>  super<\/strong><\/span>();\n}\n\n<span style=\"color: #0000ff;\"><strong>public void<\/strong><\/span> update()\n{\n  <span style=\"color: #008000;\"><em>\/\/ Only update the path if the shelf ID has changed<\/em><\/span>\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( this.ShelfId != this.orig().ShelfId )\n  {\n    this.Path = this.getPath();\n  }\n\n<span style=\"color: #0000ff;\"><strong>  super<\/strong><\/span>();\n}<\/pre>\n<p>We&#8217;ll also need to override the update() methods on the ancestor tables, racks, aisles and libraries, to update the path on the shelves whenever a rack, aisle or library ID changes. \u00a0We don&#8217;t need to worry about the insert() method for these tables, as there will be no shelves at the time of insert.<\/p>\n<p>First, override the update() method on the EB_Racks table:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public void<\/strong><\/span> update()\n{\n  EB_shelves shelves;\n\n  <span style=\"color: #008000;\"><em>\/\/ Only need to update the shelves if the rack ID has changed<\/em><\/span>\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( this.RackId != this.orig().RackId )\n  {\n    <span style=\"color: #008000;\"><em>\/\/ Call super() here so getPath() can see the updated value<\/em><\/span>\n    super();\n\n    <span style=\"color: #0000ff;\"><strong>while select forUpdate<\/strong><\/span> shelves\n           <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> shelves.RackRecId == this.RecId\n    {\n      shelves.Path = shelves.getPath();\n      shelves.update();\n    }\n  }\n  <span style=\"color: #0000ff;\"><strong>else<\/strong><\/span>\n  {\n    <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>();\n  }\n}<\/pre>\n<p>And also on the EB_Aisles table, where we do the same but with a join to the racks:<\/p>\n<pre><strong>public void<\/strong> update()\n{\n  EB_Shelves shelves;\n  EB_Racks   racks;\n\n  <span style=\"color: #008000;\"><em>\/\/ Only need to update the shelves if the aisle ID has changed<\/em><\/span>\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( this.AisleId != this.orig().AisleId )\n  {\n    <span style=\"color: #008000;\"><em>\/\/ Call super() here so getPath() can see the updated value<\/em><\/span>\n    <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>();\n\n    <span style=\"color: #0000ff;\"><strong>while select forUpdate<\/strong><\/span> shelves\n        \u00a0 <span style=\"color: #0000ff;\"><strong>exists join<\/strong><\/span> racks\n           <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> racks.RecId      == shelves.RackRecId\n              &amp;&amp; racks.AisleRecId == this.RecId\n    {\n      shelves.Path = shelves.getPath();\n      shelves.update();\n    }\n  }\n  <span style=\"color: #0000ff;\"><strong>else<\/strong><\/span>\n  {\n    <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>();\n  }\n}<\/pre>\n<p>And the last one for now, in the EB_Libraries table, where we have 3 tables in the while select loop:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>public void<\/strong><\/span> update()\n{\n  EB_Shelves shelves;\n  EB_Racks racks;\n  EB_Aisles aisles;\n\n  <span style=\"color: #008000;\"><em>\/\/ Only need to update the shelves if the library ID has changed<\/em><\/span>\n  <span style=\"color: #0000ff;\"><strong>if<\/strong><\/span>( this.LibraryId != this.orig().LibraryId )\n  {\n    <span style=\"color: #008000;\"><em>\/\/ Call super() here so getPath() can see the updated value\n<\/em><\/span>    <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>();\n\n    <span style=\"color: #0000ff;\"><strong>while select forUpdate<\/strong><\/span> shelves\n        \u00a0 <span style=\"color: #0000ff;\"><strong>exists<\/strong> <strong>join<\/strong> <\/span>racks\n           <span style=\"color: #0000ff;\"><strong>where<\/strong> <\/span>racks.RecId         == shelves.RackRecId\n          <span style=\"color: #0000ff;\"><strong>exists<\/strong> <strong>join<\/strong> <\/span>aisles\n           <span style=\"color: #0000ff;\"><strong>where<\/strong><\/span> aisles.RecId        == racks.AisleRecId\n              &amp;&amp; aisles.LibraryRecId == this.RecId\n    {\n      shelves.Path = shelves.getPath();\n      shelves.update();\n    }\n  }\n  <span style=\"color: #0000ff;\"><strong>else<\/strong><\/span>\n  {\n    <span style=\"color: #0000ff;\"><strong>super<\/strong><\/span>();\n  }\n}<\/pre>\n<p>To mass update all the existing shelves, copy\/paste and run the following job:<\/p>\n<pre><span style=\"color: #0000ff;\"><strong>static void<\/strong><\/span> EB_SetShelfPath( Args _args )\n{\n  EB_Shelves shelves;\n\n  <span style=\"color: #0000ff;\"><strong>ttsBegin<\/strong><\/span>;\n  <span style=\"color: #0000ff;\"><strong>while select forUpdate<\/strong><\/span> shelves\n  {\n    shelves.Path = shelves.getPath();\n    shelves.update();\n  }\n  <span style=\"color: #0000ff;\"><strong>ttsCommit<\/strong><\/span>;\n}<\/pre>\n<h3>Preparing the reference group<\/h3>\n<p>Find the reference group in the EB_Books form and change the ReplacementFieldGroup property to the Path group. \u00a0The list of field groups are selected from the EB_Shelves table, because that&#8217;s what the ShelfRecId field refers to. \u00a0Expand the reference group and see how AX has replaced the 4 individual fields with just the new Path field.<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_books_new_refgroup.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-218\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_books_new_refgroup.png\" alt=\"\" width=\"689\" height=\"222\" \/><\/a><\/p>\n<p>Open the form and see how the shelf is represented now:<\/p>\n<p><a href=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_books_form_new.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-219\" src=\"https:\/\/www.bakke.online\/wp-content\/uploads\/2017\/05\/eb_books_form_new.png\" alt=\"\" width=\"347\" height=\"131\" \/><\/a><\/p>\n<p>Nice and tidy&#8230;<\/p>\n<h3>In closing<\/h3>\n<p>I had originally planned to also show how to automatically navigate to the currently selected item when the lookup is opened, but due to the length of this post, I&#8217;m splitting that into a <a href=\"https:\/\/www.bakke.online\/index.php\/2017\/05\/02\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-4\/\">separate post<\/a> rather than introducing a new concept and\u00a0another big piece of code here.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is something that keeps coming up on different projects, so I&#8217;d like to share how I usually do this. \u00a0There are some examples out there which try to describe a very generic solution which can be used in almost any case. \u00a0However, such solutions very quickly become very complex. \u00a0To keep things simple, I &hellip; <a href=\"https:\/\/www.bakke.online\/index.php\/2017\/05\/02\/how-to-tree-lookups-in-dynamics-ax-reference-group-controls-part-3\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;How-to: Tree lookups in Dynamics AX reference group controls, part 3&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,1],"tags":[2,3,5],"class_list":["post-213","post","type-post","status-publish","format-standard","hentry","category-dynamics-ax","category-uncategorized","tag-ax2012r3","tag-dynamics-ax","tag-user-interface"],"_links":{"self":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/213","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/comments?post=213"}],"version-history":[{"count":0,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/posts\/213\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/media?parent=213"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/categories?post=213"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bakke.online\/index.php\/wp-json\/wp\/v2\/tags?post=213"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}