2 Replies Latest reply on Feb 14, 2020 8:45 AM by DaveArch

    Select One with For-Each Not Reflecting Observable Array Values

    DaveArch

      Hi Community

       

      Jet Version: 8.0

       

      We are using Select One with For-Each bound to an observable array to populate the list.

       

      When an action is performed in the page, we need to replace all values in the list.  The problem we are finding is that when the values in the underlying observable array are replaced, the new values are appended to the top of the list rather than being replaced.  The for-each is stamping the DOM with new elements as they are added to the array. 

       

      The code is below but here is a JS Fiddle showing the issue:

       

      https://jsfiddle.net/david_archbold/rg301qp7

       

      Observe how the initial list is populated with 3 values. If you click the 'Change List Values', the 'Values' region shows that the array is replaced with a different data set however the Select One component contains the original values and the new values.  If you keep clicking 'Change List Values' you will see the list grow.

       

      list_issue_1.PNG

      list_issue_2.PNG

       

      The question is how do we replace the entire set of values in this scenario and avoid the for-each adding values to the list?

       

      The effect is not seen if we use the 'options' attribute on the Select One component however we need to use for-each for a another reason that I won't go into here.

       

      View Model:

       

       

          let ViewModel = function () {

       

              const self = this;

              self.listSet = 0;


              self.listValue = ko.observable();

              self.listValueArray = ko.observableArray([

                  {value: "HOMER", label: "Homer Simpson"},

                  {value: "MARGE",label: "Marge Simpson"},

                  {value: "BART", label: "Bart Simpson"}

              ]);


              self.changeValues = function () {


                  self.listValueArray.removeAll();


                  if (self.listSet == 0) {


                      self.listValueArray([

                          {value: "LIST", label: "Lisa Simpson"},

                          {value: "MAGGIE",label: "Maggie Simpson"}

                      ]);


                      self.listSet = 1;


                  } else {

                      self.listValueArray([

                          {value: "HOMER", label: "Homer Simpson"},

                          {value: "MARGE",label: "Marge Simpson"},

                          {value: "BART", label: "Bart Simpson"}

                      ]);

                  }

                  document.getElementById("listvalues").refresh();

              }


          };

       

      HTML:

       

       

      <div id="container">

       


          <oj-form-layout id="listform" label-edge="start" direction="column">


              <oj-label for="listvalues" show-required>My List</oj-label>

              <oj-select-one id="listvalues" value="{{listValue}}" required placeholder="" style="max-width:22em">

                  <oj-bind-for-each data="[[listValueArray]]">

                      <template>

                          <oj-option value="[[$current.data.value]]">

                              <span>

                                  <oj-bind-text value="[[$current.data.label]]"></oj-bind-text>

                              </span>

                          </oj-option>

                      </template>

                  </oj-bind-for-each>

              </oj-select-one>


          </oj-form-layout>


          <oj-button on-oj-action='[[changeValues]]'>

              Change List Values

          </oj-button>


          <div class="oj-flex oj-sm-flex-direction-column">


              <h>Values</h>


              <oj-bind-for-each data="[[listValueArray]]">

                  <template>

                      <li>

                          <oj-bind-text value="[[$current.data.value]]"></oj-bind-text> : <oj-bind-text

                              value="[[$current.data.label]]"></oj-bind-text>

                      </li>

                  </template>

              </oj-bind-for-each>


          </div>


      </div>

       

      I see that the API Reference states the following for oj-bind-for-each which might explain what we are seeing.

       

      http://https://www.oracle.com/webfolder/technetwork/jet/jsdocs/oj.ojBindForEach.html

       

      "The markup section is duplicated for each array item when element is rendered"

       

      Any help appreciated

        • 1. Re: Select One with For-Each Not Reflecting Observable Array Values
          Duncan Mills-Oracle

          Unfortunately in this case it's a restriction of that particular component that it cannot handle a mutating collection driven through a nested forEach.

          The approach to take will  be to use options passing a data provider to provide the dynamic list rather than using forEach. For more flexibility you can look at oj-select-single if you want more flexible template support for options

          • 2. Re: Select One with For-Each Not Reflecting Observable Array Values
            DaveArch

            Thanks Duncan.

             

            We have worked around the scenario where we felt we needed to use a for-each instead of the 'options' binding on the Select One component.

             

            Binding the array to the 'options'  attribute works as expected.  When the array values are changed, the list automatically reflects the new values.

             

                <oj-form-layout id="listform" label-edge="start" direction="column">

             

                    <oj-label for="listvalues" show-required>My List</oj-label>

                    <oj-select-one id="listvalues" value="{{listValue}}" required placeholder="" style="max-width:22em" options="[[listValueArray]]">

                    </oj-select-one>

             

                </oj-form-layout>