1

I have a problem using angularjs with angular material. In a form, I have twenty select fields. I have to watch all the fields because I have to permit the user to compile only three of the twenty fields. If the user try to compile the fourth field, that field have to return blank (without value select).

I give HTML with only four fields, but indeed are 20 fields(I take code from angular material site).

<div ng-controller="SelectAsyncController" layout="column" layout-align="center center" style="padding:40px" ng-cloak>
  <p>Select can call an arbitrary function on show. If this function returns a promise, it will display a loading indicator while it is being resolved:</p>
  <div layout="column" layout-align="center center" style="height: 100px;">
    <md-select placeholder="Assign to user" ng-model="field1" md-on-open="loadUsers()" style="min-width: 200px;">
      <md-option ng-value="user" ng-repeat="user in users">{{user.name}}</md-option>
    </md-select>
    <md-select placeholder="Assign to user" ng-model="field2" md-on-open="loadUsers()" style="min-width: 200px;">
      <md-option ng-value="user" ng-repeat="user in users">{{user.name}}</md-option>
    </md-select>
    <md-select placeholder="Assign to user" ng-model="field3" md-on-open="loadUsers()" style="min-width: 200px;">
      <md-option ng-value="user" ng-repeat="user in users">{{user.name}}</md-option>
    </md-select>
    <md-select placeholder="Assign to user" ng-model="field4" md-on-open="loadUsers()" style="min-width: 200px;">
      <md-option ng-value="user" ng-repeat="user in users">{{user.name}}</md-option>
    </md-select>
    <p class="md-caption">You have assigned the task to: {{ user ? user.name : 'No one yet' }}</p>
  </div>
</div>

js:

  $scope.user = null;
  $scope.users = null;
  $scope.loadUsers = function() {
    // Use timeout to simulate a 650ms request.
    return $timeout(function() {
      $scope.users =  $scope.users  || [
        { id: 1, name: 'Scooby Doo' },
        { id: 2, name: 'Shaggy Rodgers' },
        { id: 3, name: 'Fred Jones' },
        { id: 4, name: 'Daphne Blake' },
        { id: 5, name: 'Velma Dinkley' }
      ];
    }, 650);
  };
});

There is a way to do a similar thing? Thanks in advice.

beaver
  • 17,333
  • 2
  • 40
  • 66
Michael
  • 324
  • 2
  • 20
  • Why don't you use an array as a model for the fields and $watch('yourarray', ...) or use $watchCollection('yourarray', ...) to check for array change? – beaver Dec 31 '15 at 11:23

1 Answers1

1

Below you can see my proposed solution at work.

A brief explanation: I have used an array as a model for the fields, so <md-select> directives are generated by a ng-repeat loop. Specifically the model of each select element is the value property of the field item (each item of the array).

A $watch('fields', ...) checks for array changes and its listener counts the fields already filled (using the function filledFieldsCount()) and verifies if counting equals to the maximum allowed...

Note that $watch has the third parameter set to true in order to watch deeply changes in the array item properties: $scope.$watch('fields', listener, true);

angular
  .module('MyApp', ['ngMaterial', 'ngMessages'])
  .controller('AppCtrl', function($scope, $timeout) {

    $scope.fields = [
      { "name": "task1", "value": null, "enabled": true },
      { "name": "task2", "value": null, "enabled": true },
      { "name": "task3", "value": null, "enabled": true },
      { "name": "task4", "value": null, "enabled": true },
      { "name": "task5", "value": null, "enabled": true }
    ];
  
    $scope.maxNrFields = 3;
    $scope.users = null;
 
    $scope.loadUsers = function() {

    // Use timeout to simulate a 250ms request.
    return $timeout(function() {

      $scope.users =  $scope.users  || [
        { id: 1, name: 'Scooby Doo' },
        { id: 2, name: 'Shaggy Rodgers' },
        { id: 3, name: 'Fred Jones' },
        { id: 4, name: 'Daphne Blake' },
        { id: 5, name: 'Velma Dinkley' }
      ];

    }, 250);
  };
  
  $scope.$watch('fields', function(newValue, oldValue) {
    console.log("$watch");
    if ($scope.filledFieldsCount()==$scope.maxNrFields) {
      angular.forEach($scope.fields, function(field){
          if (!field.value) field.enabled = false;
      });
    }
  }, true);
  
  $scope.filledFieldsCount = function() {
      var count = 0;
      angular.forEach($scope.fields, function(field){
          count += field.value ? 1 : 0;
      });
      return count; 
  }
  
});
.alert {
  text-align: center;
  width: 100%;
  color: red;
}
<html lang="en" >
<head>
  <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<!-- Angular Material requires Angular.js Libraries -->
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>

  <!-- Angular Material Library -->
  <script src="http://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>

</head>
<body> 
<div ng-controller="AppCtrl as ctrl" class="md-padding" ng-cloak="" ng-app="MyApp">
  <div layout="column" layout-align="center center" style="padding:40px" ng-cloak>
    <div layout="column" layout-align="center center">
      <md-select placeholder="Assign {{field.name}} to user" ng-repeat="field in fields" ng-model="field.value" ng-disabled="!field.enabled" md-on-open="loadUsers()" style="min-width: 200px;">
        <md-option ng-value="user" ng-repeat="user in users">{{user.name}}</md-option>
      </md-select>
    </div>
  </div>
  <div class="alert" ng-show="filledFieldsCount()==3">3 task assigned!</div>
  <hr>
  <pre>filledFieldsCount = {{ filledFieldsCount() | json}}</pre>
  <pre>fields = {{ fields | json}}</pre>
</div>
    
</body>
</html>
beaver
  • 17,333
  • 2
  • 40
  • 66
  • This needs some kind of explanation. – serraosays Dec 31 '15 at 14:18
  • Yes, you're right. I'm going to edit my answer and explain some details – beaver Dec 31 '15 at 14:20
  • @beaver Yeah, good answer ! I have a question.. How can I do if an item can be select in only one field? For example: if I select "Fred Jones" in the second field, I cannot re-select "Fred Jones" in the fourth field. – Michael Jan 04 '16 at 08:20
  • Maybe you could use a [Filter](https://docs.angularjs.org/api/ng/filter/filter) with ng-repeat, in which an appropriate expression filters only users that are not already chosen. – beaver Jan 04 '16 at 08:29
  • For example see this answer and the fiddle referenced there: http://stackoverflow.com/questions/14840176/angular-js-filter-ng-repeat-by-absence-in-array – beaver Jan 04 '16 at 08:36
  • Here is a CodePen with a possible solution but I haven't used filters: http://codepen.io/beaver71/pen/xZRoQW?editors=101 – beaver Jan 04 '16 at 09:01