2

I have a set of clients, displayed on a form, which must have individual scopes (per client):

View plunker here.

Naturally, I would expect that creating a new directive, with isolate scope, would not allow elements to be bound to oustide $scope, using a custom directive like this:

<fieldset client="156510">
      <legend>Client 156510</legend>
      <!-- Form elements -->
      </section>
</fieldset>

And likewise:

angular.module("plunker", [])

  .controller("ClientCtrl", function($scope) {})

  .directive("client", function() {
    return {
      restrict: "A",
      scope: {
        name: "@name",
        client: "=client"
      }
    };
  });

Given that ng-repeat is not an option, how can I isolate scope of any contained HTML using a directive? The angular docs seem to suggest this is possible, but my implementation does not seem to work as intended.

Thanks in advance.

1
  • I'm a little confused are you trying to isolate the scope from the elements within the fieldset, e.g. the legend? Or from sibling and parent elements to fieldset? Commented Aug 6, 2014 at 19:12

4 Answers 4

2

Any directive on an element with isolated scope WITHOUT template/templateUrl actually does NOT get a new scope.

Here is the proof http://plnkr.co/edit/jXwrtG?p=preview

.directive("client", function() {
    return {
      restrict: "A",
      template: '  ',//notice extra spaces
      replace: true,//notice this
      scope: {
        name: "@name",
        client: "=client"
      }
    };
  });

Also scope=true will solve your problem.

http://plnkr.co/edit/JdiCVV?p=preview

  .directive("client", function() {
    return {
      restrict: "A",

      scope: true
    };
  });

Also as pointed by @imscrb transclude = true also works but you must add ng-transclude to the element

<fieldset client="156510" ng-transclude>

http://plnkr.co/edit/b0hX5h?p=preview

.directive("client", function() {
    return {
      restrict: "A",
      transclude: true,

      scope: {
        name: "@name",
        client: "=client"
      }
    };
  });
Sign up to request clarification or add additional context in comments.

1 Comment

Very concise, and useful examples. Two thumbs up.
1

If you want each client to be totally isolate then you need to put all of the HTML being used within the template of the directive. this is because the current html you have, and the model's your binding are controlled by the controller and not the directive

I.E... (haven't tested with your code, but its what you need to do) - it's likely you need to change the ng-model in your template to the model you will push into the directive though - this is just as an example

return{
    ....
    template: '<legend>Client 156510</legend>'
            + '<section>'
            + '<div class="horizontal-field">'
            ...........
            + '</section>'
}

then your html would simply be something like (again, psuedo-code):

<fieldset client="156510" ng-model="yourModel"></fieldset>

Comments

0

If i understand your question correctly then enabling transclusion should solve your problem:

angular.module("plunker", [])
  .controller("ClientCtrl", function($scope) {})
  .directive("client", function() {
    return {
      restrict: "A",
      scope: {
        name: "@name",
        client: "=client"
      },
      transclude: true,
      template: '<div ng-transclude></div>'
    };
  });

2 Comments

I believe you need to have a template/templateurl in place for transclution. doing that as-is will show nothing.
Yes, you are right Darren, i have amended my answer. Thanks
-1

I believe that you do things in wrong way since uou broke the DRY principle. Instead of repeating HTML for each client in view, move it into directive's template and pass into directive isolated scope client themselve. Of course, you can pass into directive only client's id and get client from some service just in directive if you want. And don't specify all Angular service classes manually like ng-pristine, ng-dirty etc.

Controller and directive

app = angular.module("plunker", []).controller("ClientCtrl", function($scope) {
  $scope.clients = [{
    id: 12345,
    firstName: 'First',
    lastName: 'First',
    middleInitial: 'A'
  }, {
    id: 123456,
    firstName: 'Second',
    lastName: 'Second',
    middleInitial: 'B'
  }];

})
.directive("client", function() {
  return {
    restrict: "A",
    templateUrl: 'client-form.html',
    scope: {
      client: "=client"
    }
  };
});

Client form template

<fieldset ng-form='clientForm'>
  <legend>Client {{client.id}}</legend>
  <section>
    <div class="horizontal-field">
      <label for="title">Title</label>
      <select ng-model="client.title" name="title">
        <option value=""></option>
        <option value="mr">Mr.</option>
        <option value="mrs">Mrs.</option>
        <option value="ms">Ms.</option>
        <option value="miss">Miss</option>
        <option value="dr">Dr.</option>
      </select>
    </div>
    <div class="horizontal-field">
      <label for="first-name">First Name</label>
      <input placeholder="First Name" required="" autofocus="" ng-model="client.firstName" name='first_name' type="text" >
    </div>
    <div class="horizontal-field">
      <label for="middle-initial">Middle Initial</label>
      <input maxlength="1" ng-model="client.middleInitial" type="text" name="middle_initial" value="" id="middle-initial">
    </div>
    <div class="horizontal-field">
      <label for="last-name">Last Name</label>
      <input placeholder="First Name" ng-model="client.lastName" type="text" name="last_name">
    </div>
  </section>
</fieldset>

View

<div ng-repeat='client in clients' client='client' ></div>

Plunker

3 Comments

Ya- what he said - I couldn't be bothered to refactor the OP plunker :)
Read the op: ng-repeat is not an option.
@user1429980 in situation when I have a set of clients the ng-repeat is not just an option, it required.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.