0

I'm trying to figure out where and how to use directives in angularjs. I have several pages with lots of input fields and each time I want to add some conditional ng-class and a button that only shows in certain conditions. A rough example can be seen at:

JSFiddle without directives

Now I'd like to type less and hope that directives can help me. The following code does not work, but maybe it shows where I want to go:

this is how I'd like to type my html:

  <tr>
    <td>First Name:</td>
    <td>
        <dirty-input attr="firstName"/>
    </td>
  </tr>
  <tr>
    <td>Last Name:</td>
    <td>
        <dirty-input attr="lastName"/>
    </td>
  </tr>

so I tried to accomplish this with following controller:

app.controller('PersonController', function($scope) {
    $scope.person = {firstName: 'John', lastName: 'Doe'};
    $scope.personEdited = {firstName: $scope.person.firstName, lastName: $scope.person.lastName};
    $scope.firstName = {objName: 'person', editedObject: 'personEdited', attrName: 'firstName'};
    $scope.lastName = {objName: 'person', editedObject: 'personEdited', attrName: 'lastName'};
});

and this directive:

app.directive('dirtyInput', function() {
    return {
        restrict: 'E',
        scope: {
            attr: '=attr',
        },
        template: '<input type="text" ng-model="{{attr.editedObject}}.{{attr.attrName}}"/>'
    };
});

This can be seen at: JSFiddle with directives (not working)

Obviously, this doesn't work. Am I trying to do something that isn't possible or am I just doing it wrong?

2 Answers 2

1

You were pretty close !

A couple of things that need to change:

  1. On ngModel you have to refer to a property in the scope.
    Instead of: ng-model="{{attr.editedObject}}.{{attr.attrName}}
    You should have something like: ng-model="attr.editedObject[attr.attrName]
    (which means: "Bind to the property named x of the object referenced by the 'editedObject' property of the object referenced by scope's attr property. Where x is the property whose name equals the value of attr's 'attrName' property.")
    Yeah, whatever !

  2. In order for the isolate scope to get access to the actually edited object (i.e. personEdited), it needs a reference to it. There are a couple of ways to achieve this, but I think the easiest would be to put a reference to it inside the firstname, lastname objects. E.g.:
    Instead of: $scope.firstName = {...editedObject: 'personEdited',...}
    Use: $scope.firstName = {...editedObject: $scope.personEdited,...}

See, also, this short demo.

Sign up to request clarification or add additional context in comments.

Comments

0

You are misunderstanding how the isolate scope works. Your directive's template is trying to access {{attr.editedObject}}.{{attr.attrName}} on its isolate scope. Let's suppose angular resolves {{attr.editedObject}}.{{attr.attrName}} to personEdited.firstName. Now consider the isolate scope, which currently consists of a single property, attr. When your directive resolves personEdited.firstName, it looks in its isolate scope, and only sees attr. Because personEdited is undefined, nothing happens. In order for this to work, you would have to include another property in your isolate scope that was assigned the value of personEdited on your controllers scope. That property will need to be named personEdited:

scope: {
  attr: '=attr',
  personEdited: '=personEdited'
}

And then you need to assign it in your directive usage:

<dirty-input attr="firstName" personEdited="personEdited" />

Now your isolate scope will have access to your personEdited object defined in your PersonController. Hope this helps.

edit

After thinking about this for a bit, you might want to try a controller within a directive. From what I am guessing based on your question, what you really want is access to the scope of PersonController. You can accomplish this using the following syntax in your directive:

myApp.directive('myDirtyInput', function () {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'DirtyInput.html',
    controller: '@',
    name: 'ctrl'
  };
});

The two pieces to note here are the controller property and the name property. What these are telling angular js is that we are going to allow the user of our directive to bind a controller for us to use, and it will be assigned by defining the ctrl attribute on the directive declaration. So in our case this will look like:

<my-dirty-input ctrl="PersonController"></my-dirty-input>

Now, with our controller bound to our directive, we have full access to our controllers scope from within the directive. We don't have to create an isolate scope, we can simply start accessing our controller's properties.

Here is a plunker with an example of what I guessed you were aiming to do (based on the fiddle you posted): http://plnkr.co/edit/4AQkCSIJvqsrUOBOqway?p=preview

2 Comments

I like that approach, but I can't get it to work. Could you look at this jsfiddle for an error: jsfiddle.net/ZyCdm/3
sorry, that's not really what I wanted. As shown in my jsfiddle examples, I wanted to have serveral input fields in the scope of one Controller. My plan was to specify a field name like this: <dirty-input attr="firstName"/> and not make a Controller for each and every field name.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.