Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - Controller of my partial

I've a template that is appendend many times in my DOM.

<div ng-controller="theController">
    content does not matter
</div>

So the controller is istantiated many times. This is a problem because if i put a watcher in the controller

theController = function($scope) {
    $scope.$on('myVar', function() {
        // run one time for each time the template is repeated
    })
}

Any ideas about how to avoid this? Thanks in advance.

UPDATE

OK, i will try to be clearer.

Perhaps i've a form, that is built dynamically on the basis of the response of an asynchronous request.

<form ng-controller="formController">
    <div ng-repeat="f in fields">
        <ng-inclide src="f.fields"></ng-include>
    </div>
</form>

The controller is something like:

function formController($scope) {
    $scope.fields = [{ template:'', ... }];
    // data come from an ajax request... 
    // here are static for the sake of simplicity.
}

So I don't know what fields will be appended in the form.

The form field structure is stored in html partials... something like:

<div ng-controller="inputController">
    <label> .... </label>
    <input type="text" .... />
</div>

or

<div ng-controller="selectController">
    <label> .... </label>
    <select>
        ....
    </select>
</div>

function selectController($scope){
    $scope.$on("myCustomEvent", function(event) {
        cionsole.info("Options were updated");
    });
}

When the form has more than an input type=text, or a select, the inputController, or the selectController are instantiated more than once.

Why do you not want the $watch to occur for every instance?

I would like to update the options of one of the selects in the page when a specific event occurs.

What i get is instead that i update all the select in the page.

From the comment, i understood that is wrong to have more element with the same controller in the same page. So currently the only available solution seem to me that is to avoid to define a controller for each element of the form, right?

UPDATE 2

$emit is used in the inputController:

function inputController() {
    $scope.fireclick = function(p) {
        if (p == 'specificInput') {
            /* this is a temporary work around 
            I used to bind the event only with a specific field */

            $scope.$emit("myCustomEvent");      
        }
    }
}

This is the complete code of the input field used in the html partial:

<input type="text" ng-click="fireclick(f.name);" name="{{f.name}}" />

@Anybody:

Could at least confirm, (and eventually say why), to have on the same page more elements with the same controller is wrong

like image 447
Bruno Avatar asked Apr 08 '13 19:04

Bruno


1 Answers

I think the Angular Way to do this is to use directives. I would do something like an ng-switch in your main view's ng-repeat, and have the ng-switch just include the appropriate directive... Assuming an "input-text" directive an an "input-dropdown" directive exist:

<div ng-swtich on="field.type" ng-repeat="field in fields">
    <div ng-switch-when="text"><input-text ...></div>
    <div ng-switch-when="dropdown"><input-dropdown ...></div>
</div>

I believe this way you will not have the same issue you're having now. I haven't actually setup what you're trying to do, but I'm 99% certain that you should be using directives! They are ideal for the type of thing you're doing, and will be much more re-usable.

I use a directive on http://angularlist.com to handle the ratings, and I can confidently say they don't cross wires when I have more than one on the page - That said, I'm not watching anything there, just responding to events... Actually, let me test something (testing.........) Yes! I added a watch to the model value the my ratings directive is editing and when clicking on a rating, only ONE watcher is fired - the one for the controller in question. This isn't on the live site, just my dev server here at home, but here's the directive if it helps you:

app.directive("angularStars", function() {
  return {
    restrict: 'E',
    scope: {
      model: '=ngModel',
      notifyId: '=notifyId'
    },
    replace: true,
    transclude: true,
    template: '<div><ol class="angular-stars">' + '<li ng-class="{active:model>0,over:over>0}">1</li>' + '<li ng-class="{active:model>1,over:over>1}">2</li>' + '<li ng-class="{active:model>2,over:over>2}">3</li>' + '<li ng-class="{active:model>3,over:over>3}">4</li>' + '<li ng-class="{active:model>4,over:over>4}">5</li>' + '</ol></div>',
    controller: function($scope, $attrs, $http) {
      $scope.over = 0;

      // TEST WATCH
      $scope.$watch('model', function() {
        console.log('modelChange', $scope.model);
      });

      $scope.setRating = function(rating) {
        $scope.model = rating;
        $scope.$apply();
        if ($attrs.notifyUrl !== void 0 && $scope.notifyId) {
          return $http.post($attrs.notifyUrl, {
            id: $scope.notifyId,
            rating: rating
          }).error(function(data) {
            if (typeof data === 'string') {
              alert(data);
            }
            return $scope.model = 0;
          });
        }
      };
      return $scope.setOver = function(n) {
        $scope.over = n;
        return $scope.$apply();
      };
    },
    link: function(scope, iElem, iAttrs) {
      if (iAttrs.notifyUrl !== void 0) {
        return angular.forEach(iElem.children(), function(ol) {
          return angular.forEach(ol.children, function(li) {
            li.addEventListener('mouseover', function() {
              return scope.setOver(parseInt(li.innerHTML));
            });
            li.addEventListener('mouseout', function() {
              return scope.setOver(0);
            });
            return li.addEventListener('click', function() {
              return scope.setRating(parseInt(li.innerHTML));
            });
          });
        });
      }
    }
  };
});

Directives are tough to get your head wrapped around - I am still mainly just shooting in the dark with them, trying things out to see how they work - but no doubt - the power you need is in the directives. I highly recommend reading the AngularJS docs on writting directives and then spending time looking at other people's directives - there are many available on GitHub to learn from!

like image 96
Thom Porter Avatar answered Oct 01 '22 13:10

Thom Porter