skip to Main Content

I am new in working with Angular. I made a “repeating-cascaded-table”:
Each rows is connected. If in row 1, column 1 (R1C1) something changes, the other 2 columns in that row are cascaded.

I created two “plunkers” to demonstrate my issue.

  1. With a custom directive (for ng-repeat)
    (http://plnkr.co/edit/J8k74C3AUIAxv6rEKcWL?p=preview)
  2. The same but without directive (http://plnkr.co/edit/neOgWfuyuFnkawRGJB0c?p=preview)

In example 2, when in R1C1 “Function” is chosen, and in R1C2 a random option is selected then R1C3 is populated with cascaded options.

When, in the same example, a row is added (R2) and in R2C1 “department” is chosen and in R2C2 a other random option, R2C3 gets populated with cascaded options BUT… R1C3 changes too…

When i work WITH a custom directive then this problem is solved. However the delete button for each row is not working (and so is the $index..), and also the json formatted form data isn’t populated. How can i make a perfect match between these two plunkers? I am breaking my head over this 🙁

PLUNKER CODE (example 2):

JS:

'use strict';
/* global $ */

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $timeout, filterService) {
  $scope.filters = filterService.getFilters();

      $scope.addNewForm = function (){
        $scope.filters.push({});
      };

      $scope.attribute = filterService.getAttribute();
      $scope.comparison = function(index, field) {
        $scope.comparisonType = filterService.getComparison($scope.attribute[field]);
      };
      $scope.value = function(index, parent, field) {
        $scope.vType = filterService.getValueType($scope.attribute[parent], $scope.comparisonType[field]);
      }
})

app.service('filterService', function() {

  var attribute = [
    {name: 'Building year', type: 'integer', id: 1},
    {name: 'Square meters', type: 'integer', id: 2},
    {name: 'Function', type: 'select', id: 3},
    {name: 'Department', type: 'multiselect', id: 4},
    {name: 'Date of possesion', type: 'date', id: 5},
  ];

  this.getFilters = function() {
    return [];
  }

  this.getAttribute = function() {
    return attribute;
  }

  this.getComparison = function(attribute) {
    console.log(attribute.name)
     var comparisonType = [];
    if (attribute.type == 'select' || attribute.type == 'multiselect') {
      comparisonType = [
        {name: 'is', id: 1},
        {name: 'is not', id: 2},
        {name: 'one of', id: 3},
        {name: 'not one of', id: 4},
      ]
    } else if (attribute.type == 'integer' || attribute.type == 'date') {
      comparisonType = [
        {name: 'is', id: 1},
        {name: 'is not', id: 2},
        {name: 'greater then', id: 3},
        {name: 'smaller then', id: 4},
      ]

    }
    return comparisonType;
  }

  this.getValueType = function(attribute, comparison) {
    console.log(attribute.name)
    console.log(comparison.name)
    var vType = [];
    var vTypes = [
        {name: 'Department 1', id: 1, fk: 4},
        {name: 'Department 2', id: 2, fk: 4},
        {name: 'Department 3', id: 3, fk: 4},
        {name: 'Department 4', id: 4, fk: 4},
        {name: 'Department 5', id: 5, fk: 4},
        {name: 'Function 1', id: 6, fk: 3},
        {name: 'Function 2', id: 7, fk: 3},
        {name: 'Function 3', id: 8, fk: 3},
        {name: 'Function 4', id: 9, fk: 3},
        {name: 'Function 5', id: 10, fk: 3},
      ]
    var tmp = [];

    angular.forEach(vTypes, function(value, key) {
      if (value.fk === attribute.id)
        tmp.push(value);
    });


    if (attribute.type == 'select') {
      vType = [
        {name: 'Department 1', id: 1, fk: 4},
        {name: 'Department 2', id: 2, fk: 4},
        {name: 'Department 3', id: 3, fk: 4},
        {name: 'Department 4', id: 4, fk: 4},
        {name: 'Department 5', id: 5, fk: 4},
        {name: 'Function 1', id: 6, fk: 3},
        {name: 'Function 2', id: 7, fk: 3},
        {name: 'Function 3', id: 8, fk: 3},
        {name: 'Function 4', id: 9, fk: 3},
        {name: 'Function 5', id: 10, fk: 3},
      ];
    }
    //return vType;
    return tmp;
  }
});

HTML:

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <!-- Twitter bootstrap -->
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">

    <!-- apiCheck is used by formly to validate its api -->
    <script src="//npmcdn.com/api-check@latest/dist/api-check.js"></script>
    <!-- This is the latest version of angular (at the time this template was created) -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>

    <!-- This is the latest version of formly core. -->
    <script src="//npmcdn.com/angular-formly@latest/dist/formly.js"></script>
    <!-- This is the latest version of formly bootstrap templates -->
    <script src="//npmcdn.com/angular-formly-templates-bootstrap@latest/dist/angular-formly-templates-bootstrap.js"></script>

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
    <script src="https://rawgithub.com/eligrey/FileSaver.js/master/FileSaver.js" type="text/javascript"></script>
    <script src="script.js"></script>
  </head>

  <body ng-controller="myCtrl">

      <table class="table table-bordered" cellspacing="0" width="100%">

        <th>Attribute</th>
        <th>Compare</th>
        <th>Value</th>
        <th></th>

        <tbody>
          <tr data-ng-repeat="item in filters" class="text-center">
            <td>
              <select class="form-control" 
              ng-options='k as v.name for (k,v) in attribute' 
              ng-change='comparison($index, item.check1)' 
              ng-model='item.check1'>
                <option value='' disabled>Select...</option>
              </select>
            </td>
            <td>
              <select ng-if="item.check1" class="form-control" 
              ng-model='item.check2' 
              ng-options='a as b.name for (a,b) in comparisonType' 
              ng-change='value($index, item.check1, item.check2)'>
                <option value='' disabled>Select...</option>
              </select>
            </td>
            <td>

              <!--DATE-->
              <input class="form-control" ng-if="item.check1==='4' && item.check2"
              type="date" ng-model='item.check3'>
              </input>

              <!--INTEGER-->
              <input ng-if="item.check1==='0' && item.check2" 
              class="form-control" ng-model='item.check3' type="number" 
              </input>

              <!--SELECT-->
              <select ng-if="item.check1==='2' && item.check2" 
              class="form-control" ng-model='item.check3' 
              ng-options='c as d.name for (c,d) in vType'>
                <option value='' disabled>Select...</option>
              </select>

              <!--MULTIPLE-->
              <select multiple ng-if="item.check1==='3'" 
              class="form-control" ng-model='item.check3' 
              ng-options='c as d.name for (c,d) in vType'>
              </select>

            </td>
            <td>
              <button data-index="$index" type="button" 
              class="btn btn-sm btn-danger" 
                  ng-click="filters.splice($index, 1)" >
                Remove
              </button>
            </td>
          </tr>
        </tbody>
      </table>

      <button class="btn btn-default" id="addMore" aria-hidden="true" 
      ng-click="addNewForm()">Add</button>
      <pre>{{filters | json}}</pre>


  </body>

</html>

2

Answers


  1. Ok, i think you are calling splice inside the directive, but the filters are not defined in directive. What you should do is pass the function also to directive. like you have passed the ‘filter’. this ‘remove’ function can be on the outer controller and passed as

    scope: {
        filter: "=",
        removeFn: "&"
    }
    

    this way you can trigger this function within directive and pass the filter to be removed.

    You could also pass all filters to directive and then have them in your scope, and then remove them from directive with the function you alreday have

    EDIT: 2nd solution, can be achieved with just 2 lines of code, but I do not like it too much is this:

    scope: {
        filter: "=",
        filters: "="
    }
    

    and in directive you add

    <tr data-ng-repeat="item in filters" class="text-center" filter="item" filters="filters">
    
    Login or Signup to reply.
  2. In your second plunker, you were assigning the second select/multiselect values from $scope.vTypes. This is a global variable in your controller, so when you select the department in gets modified and the function part also receives the new value.

    What you need is a vTypes for each filter.

    So, in your js you can do:

    $scope.value = function(index, parent, field) {
        $scope.filters[index].vType = filterService.getValueType($scope.attribute[parent], $scope.comparisonType[field]);
    }
    

    And in your HTML:

    <td>
        <!--SELECT-->
        <select ng-if="item.check1==='2' && item.check2" 
               class="form-control" ng-model='item.check3' 
               ng-options='c as d.name for (c,d) in item.vType'>
    
            <option value='' disabled>Select...</option>
        </select>
    
        <!--MULTIPLE-->
        <select multiple ng-if="item.check1==='3'" 
                class="form-control" ng-model='item.check3' 
                ng-options='c as d.name for (c,d) in item.vType'>
        </select>
    </td>
    

    Here’s a plunkr with the example:
    http://plnkr.co/edit/nL23ScGLBrqFuHp48ZsL?p=preview

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search