Daimz
Daimz

Reputation: 3281

How to make a directive have a unique scope item for each use

I have created a directive and used it to make two time counters however I am a bit confused about how I can use different scope items for each time counter.

I have made a Plunkr to illustrate.

I am not sure whether I should be adding to scope items to the mainCtrl controller or the directives scope. And if I add it to the directives scope then how can I make that apart of mainCtrl so that it could later be saved? hopefully the plunker is a bit clearer.

Upvotes: 1

Views: 1054

Answers (3)

GregL
GregL

Reputation: 38113

You need to declare an isolate scope to define a property on the directive's scope that is two-way-bound with a property on the parent scope.

For example, your directive code should be:

app.directive('time', function() {
  return {
    templateUrl: 'time.html',
    restrict: 'E',
    scope: {
      Time: '=value'
    },
    link: function(scope, element, attrs) {
      element.addClass('time');
    }
  };
});

And the relevant markup becomes:

  <h1>Times</h1>
  <strong>Timer 1</strong> – I would like this to use the 'Time' scope item
  <br>
  <time value="Time"></time>
  <br>
  <br>
  <strong>Timer 2</strong> – I would like this to use the 'altTime' scope item
  <br>
  <time value="altTime"></time>

See my forked Plunkr.

EDIT: A few more comments:

  • The general JS convention for naming properties is to use camelCasing, with PascalCase reserved for functions serving as the JS equivalent of classes (i.e. functions you should invoke with the new operator).
  • You made a typo in your original plunkr where you declared the initial scope property as alTime but referred to altTime in the markup (my Plunkr fixes this).
  • The thing that does the two-way binding in the directive is the = operator. See the Angular docs for more info.
  • You don't need to use ng-model in this case to point to the value. You theoretically could, but it would be harder to do and I don't believe it is necessary in this case. Just use the isolate scope bindings as I described.
  • Keep in mind what I consider the AngularJS Golden Rule: "Always have a dot (.) in your ng-model expressions, unless you have a good reason for omitting it." This will mean that you are always setting the property of an object on the correct scope, even if that scope is several levels up in the hierarchy. In your code, for instance, the use of the ng-click="Time = Time + 1" directive will actually have you creating a new property Time on the directive's scope, even if the property initially exists on a parent scope.

    Note I did violate this rule in my Plunkr, but that is because I knew that the isolate scope would have a Time property that was different to the parent scope's Time property, and the two are kept in sync by AngularJS under the covers by the use of the = specifier on the isolate scope.

Upvotes: 1

Kousha
Kousha

Reputation: 36199

While @MarkM suggestion works, I suggest adding scope: {} as it is more inline with Angular's implementation:

app.directive('time', function () {
    return {
      templateUrl: 'time.html',
      scope: {},
      restrict: 'E',
      link: function (scope, element, attrs) {
        element.addClass('time');
        // $compile(element)(scope);
      }
    };
  });

Upvotes: 1

Mark
Mark

Reputation: 92440

You can just add scope:true to your directive. Here's a forked Plunkr. It will make a new scope that should inherit from the enclosing element's scope.

Upvotes: 0

Related Questions