AngularJS ngChange directive firing twice

1k Views Asked by At

https://plnkr.co/edit/zxTLC2VTfMkUu5zPs1Ss?p=preview

In the linked plunker I have 3 products listed out, each with a select box.

Each product has an array of records. If I was to select a record from the "Product One" row, I would expect "record one" to be disabled in the other two product row select boxes, but that is not happening.

It seems that the ngChange directive is being fired twice, though I'm not sure why. Maybe because I am changing the disabled state?

If it is correct that it should fire twice, then I am not sure why it is unselecting the value and setting the field to be blank again?

HTML:

  <head>
    <script data-require="[email protected]" data-semver="1.6.5" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <my-component></my-component>
    <script src="script.js"></script>
  </body>

</html>

JS:

angular
  .module('app', [])
  .component('myComponent', {
    template: `
      <div style="display: flex; margin-bottom: 10px;" ng-repeat="productField in $ctrl.selectedProduct.productFields track by $index">
        <div style="padding-right: 5px">
         {{ productField.label }} &rarr;
        </div>
        <div>
          <select
            ng-model="productField.recordKey"
            ng-options="recordField.key as recordField.label disable when recordField.disabled for recordField in productField.recordFields"
            ng-change="$ctrl.mappingFieldChange()">
          </select>
        </div>
      </div>
    `,
    controller: function() {
      $ctrl = this

      this.selectedProduct = {
        productFields: [
          {
            key: 'one',
            label: 'Product One',
            recordFields: [
              {
                key: 'one',
                label: 'Record One',
                disabled: false,
              },


    {
            key: 'two',
            label: 'Record Two',
            disabled: false,
          },
          {
            key: 'three',
            label: 'Record Three',
            disabled: false,
          },
        ],
      },
      {
        key: 'two',
        label: 'Product Two',
        recordFields: [
          {
            key: 'one',
            label: 'Record One',
            disabled: false,
          },
          {
            key: 'two',
            label: 'Record Two',
            disabled: false,
          },
          {
            key: 'three',
            label: 'Record Three',
            disabled: false,
          },
        ],
      },
      {
        key: 'three',
        label: 'Product Three',
        recordFields: [
          {
            key: 'one',
            label: 'Record One',
            disabled: false,
          },
          {
            key: 'two',
            label: 'Record Two',
            disabled: false,
          },
          {
            key: 'three',
            label: 'Record Three',
            disabled: false,
          },
        ],
      },
    ]
  }

  $ctrl.mappingFieldChange = () => {
    console.log('mappingFieldChange')
    const fieldsMapped = getSelectedFields()

    $ctrl.selectedProduct.productFields.forEach((productField) => {
      // Disable the record keys that are selected and enable any others
      productField.recordFields.forEach(recordField => {
        if (fieldsMapped.indexOf(recordField.key) >= 0) {
          recordField.disabled = true
        }
        else {
          recordField.disabled = false
        }
      })
    })
  }

  function getSelectedFields() {
    let fieldsMapped = []

    // Create an array of record field keys that are already selected
    $ctrl.selectedProduct.productFields.forEach((productField) => {
      if (productField.recordKey) {
        fieldsMapped.push(productField.recordKey)
      }
    })

    return fieldsMapped
  }
},

})

1

There are 1 best solutions below

0
On

Because I was disabling the value that was being selected, AngularJS was then nulling the value (nulling the disabled value was introduced in 1.6), hence why the select box would remain blank.

To fix this, I left the value enabled for the product row it was selected in, but disabled it in the others.

The updated mappingChange function now looks like this:

$ctrl.mappingFieldChange = () => {
  console.log('mappingFieldChange')
  const fieldsMapped = getSelectedFields()

  $ctrl.selectedProduct.productFields.forEach((productField) => {
    // Disable the record keys that are selected and enable any others
    productField.recordFields.forEach(recordField => {
      if (productField.recordKey === recordField.key) {
        recordField.disabled = false
      }
      else if (fieldsMapped.indexOf(recordField.key) >= 0) 
        recordField.disabled = true
      }
      else {
        recordField.disabled = false
      }
    })
  })
}