Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

watch ng-model inside directive

I have the following directive:

directive('myInput', function() {
    return {
        restrict: 'AE',
        scope: {
            id: '@',
            label: '@',
            type: '@',
            value: '='
        },
        templateUrl: 'directives/dc-input.html',
        link: function(scope, element, attrs) {
            scope.disabled = attrs.hasOwnProperty('disabled');
            scope.required = attrs.hasOwnProperty('required');
            scope.pattern = attrs.pattern || '.*';
        }
    };
});

with the following template:

<div class="form-group">
    <label for="input-{{id}}" class="col-sm-2 control-label">{{label}}</label>
    <div class="col-sm-10" ng-switch on="type">
        <textarea ng-switch-when="textarea" ng-model="value" class="form-control" id="input-{{id}}" ng-disabled="disabled" ng-required="required"></textarea>
        <input ng-switch-default type="{{type}}" ng-model="value" class="form-control" id="input-{{id}}" ng-disabled="disabled" ng-required="required" pattern="{{pattern}}"/>
    </div>
</div>

It is used by this form:

<form ng-controller="UserDetailsCtrl" role="form" class="form-horizontal">
    <div ng-show="saved" class="alert alert-success">
        The user has been updated.
    </div>
    <my-input label="First name" value="user.firstName" id="firstName"></my-input>
    <my-input label="Last name" value="user.lastName" id="lastName"></my-input>
    <my-input label="Email" value="user.email" id="email" type="email" disabled></my-input>
    <my-input label="Password" value="user.password" id="password" type="password"></my-input>
    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
            <button ng-click="update()" class="btn btn-default">Save</button>
        </div>
    </div>
</form>

Which has this controller:

controller('UserDetailsCtrl', function($scope, $stateParams, User) {
    $scope.user = User.get({userId: $stateParams.id});
    /**
     * Update the current user in this scope.
     */
    $scope.update = function() {
        console.log($scope.user);
        $scope.user.$update({userId: $scope.user.id}).then(function(results) {
            $scope.saved = true;
        });
    };
}).

The form is rendered fine, but when I click the Save button, the user values are never updated.

How can I use the updated values from within the myInput directive in the controller scope?

like image 253
Remco Haszing Avatar asked Dec 29 '13 17:12

Remco Haszing


2 Answers

Here's the basic problem. Your ng-model is a primitive and is only being bound in one direction...it will update if parent object is changed, but since it is primitive it does not carry reference to parent object...just value. Thus updating the primitive does not update parent object that it's original value came from

Cardinal rule in angular...always have a dot in ng-model

Here's a solution that will pass the main user object to directive scope, as well as the property of that object to use for each input

<my-input id="firstName" model="user" field="firstName" label="First name"></my-input>

Now need to pass the object from controller into the directive scope:

app.directive('myInput', function() {
    return {  
        scope: {
           /* other props*/
            field: '@',
            model:'='/* now have reference to parent object in scope*/
        },
       ......
    };
});

Then in markup for an input will use [] notation in order to get our dot in:

<input  ng-model="model[field]".../>

DEMO

In order to use angular validation you will likely have to require the ngModel controller in your directive or use nested form

like image 71
charlietfl Avatar answered Nov 16 '22 10:11

charlietfl


Your problem is the ng-switch.

ng-switch like ng-repeat creates a new scope that inherits from the parent.

That means that if you have let's say:

$scope.foo = "hello";

And then you have something like:

<input type="text" ng-model="foo">

Inside a ng-switch. When you update foo it is going to create its own foo that hides/shadows the parent foo.

In other words, the input will show hello but when you modify it, a new foo is created hiding the parent one. That means that your parent one won't get updated (your problem).

That is not Angular.js issue, that is how Javascript works.

Normally you want to do a:

<input type="text" ng-model="foo.bar">

That way, you can play with the inheritance and instead of creating a new foo it will just update the bar on the parent.

Since that is not something you can do every time and maybe in your concrete use case you can't, the easy way is just to use $parent:

<input type="text" ng-model="$parent.value">

That way inside your ng-switch you will use directly the parent value.

I highly recommend you to read this ASAP: https://github.com/angular/angular.js/wiki/Understanding-Scopes

Example: http://plnkr.co/edit/z4D6Gk5fK7qdoh1mndzo?p=preview

Cheers.

like image 2
Jesus Rodriguez Avatar answered Nov 16 '22 11:11

Jesus Rodriguez