Stfvns
Stfvns

Reputation: 1041

How to group items inside foreach based on multiple if conditions

I want looping my form-group inside form-horizontal using foreach from knockout. I have condition when Id is 1 and 2, this should be grouped inside <fieldset>. I already tried when just 1 condition is working normally, but when multiple 1 and 2 it's not working.

Code Sample:

<div class="form-horizontal" data-bind="foreach: forArray()">
<!-- ko if: Id() === 1 -->
<fieldset>
    <legend>
        Foo
    </legend>
    <!-- /ko -->
    <!-- ko if: Id() === 1 || Id() === 2 -->
    <div class="form-group">
        <label class="control-label col-sm-5"><span data-bind="html: NameColumn"></span></label>
        <div class="col-sm-7">
            <input type="text" data-bind="value: Id" />
        </div>
    </div>
    <!-- /ko -->
    <!-- ko if: Id() === 2 -->
</fieldset>
<br />
<!-- /ko -->

<!-- ko if: Id() !== 1 && Id() !== 2 -->
<div class="form-group">
    <label class="control-label col-sm-5"><span data-bind="html: NameColumn"></span></label>
    <div class="col-sm-7">
        <input type="text" data-bind="value: Id" />
    </div>
</div>
<!-- /ko -->

Upvotes: 1

Views: 233

Answers (2)

adiga
adiga

Reputation: 35202

First of all, your containerless control flow syntax won't work. You should treat them as if they are real html container element like you would with a <div> tag.

From the documentation:

The <!-- ko --> and <!-- /ko --> comments act as start/end markers, defining a “virtual element” that contains the markup inside. Knockout understands this virtual element syntax and binds as if you had a real container element.

Even if you fix your syntax, it won't display your desired output. A single looping through forArray can't create this functionality, because it will create more than one fieldset element for both Id 1 and 2. So, you need to create a computed property. This computed property will have 2 array properties:

  • lessThanTwo: push the items with Ids 1 and 2
  • moreThanTwo: push the items with Id greater than 2

Every time the original forArray changes, groupedArray gets computed again. You can test this by changing the Id input of Ronaldo to 5. That item will get moved outside of the fieldset.

Use the with binding to create a new binding context in form-horizontal div

const viewModel = function() {
  const self = this;

  self.forArray = ko.observableArray([
  { NameColumn: '<u>Messi</u>', Id: ko.observable(1) },
  { NameColumn: '<b>Ronaldo</b>', Id: ko.observable(2) },
  { NameColumn: '<i>Griezmann</i>', Id: ko.observable(3) },
  { NameColumn: '<u>Mbappé</u>', Id: ko.observable(4) }
  ]);

  self.groupedArray = ko.computed(() => {
    const groupedObject = {
      lessThanTwo: [],
      moreThanTwo: []
    };

    self.forArray().forEach(item => {
      // if Id() > 2 push it a moreThanTwo property
      // otherwise push it to lessThanTwo property
      if (item.Id() == 1 || item.Id() == 2)
        groupedObject.lessThanTwo.push(item);
      else
        groupedObject.moreThanTwo.push(item);
    })

    return groupedObject;
  })
}

ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="form-horizontal" data-bind="with: groupedArray">
  <fieldset>
    <legend>
      Foo
    </legend>
    <!-- ko foreach: lessThanTwo -->
    <div class="form-group">
      <label class="control-label col-sm-5"><span data-bind="html: NameColumn"></span></label>
      <div class="col-sm-7">
        <input type="text" data-bind="value: Id" />
      </div>
    </div>
    <!-- /ko -->

  </fieldset>
  <br />
 <!-- ko foreach: moreThanTwo -->
  <div class="form-group">
    <label class="control-label col-sm-5"><span data-bind="html: NameColumn"></span></label>
    <div class="col-sm-7">
      <input type="text" data-bind="value: Id" />
    </div>
  </div>
 <!-- /ko -->
</div>

Upvotes: 1

Loid Gaba
Loid Gaba

Reputation: 19

Looks like you have a little mess with ko-/ko tag pairs

Form what you rote <fieldset> will be opened if condition Id() === 1 is valid but will be closed if Id() === 2

Upvotes: 1

Related Questions