Jordan Rieger
Jordan Rieger

Reputation: 2683

How do I reference the scope/model property of an element from within an AngularJS directive?

I am using a custom directive to attach the jQuery Chosen plugin to a multi-select element. (I realize there are similar native AngularJS plugins out there, but I want to learn how to integrate a jQuery plugin the right way because I'm sure I will eventually come across a requirement for which there exists only a jQuery plugin.)

The element is already bound to a scope model property, and in the directive, I attach a watch handler on this model property to ensure that the plugin's update/refresh function is called whenever it changes. However, I'm currently doing this by hard-coding the name of the model property into the directive, which is obviously not ideal. I want the directive to be able to figure that out on its own, so that it can be used in a variety of situations. I suppose I could pass it into the directive via an attribute value, but I would prefer if the directive was smart enough to figure it out on its own.

Is there a reference path available from the element object to its bound scope model property?

HTML:

<div ng-app="testApp" ng-controller="testController">
    <select multiple ng-model="selection" jquery-ng-chosen>
        <option value="1">First option</option>
        <option value="2">Second option</option>
    </select><br/>
    <button type="button" ng-click="selection = []">Clear</button>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chosen/1.1.0/chosen.jquery.min.js"></script>

JS:

var testApp = angular.module('testApp', []);
testApp.controller('testController', function($scope) {

});
testApp.directive('jqueryNgChosen', function () {
    return {
        link: function (scope, element, attrs) {
            element.chosen();
            scope.$watch('selection', function () {
                element.trigger("chosen:updated");
            });
        }
    };
});

Fiddle. Notice that the clear button works properly because the directive has used a watch to update the jQuery chosen plugin. (Note: the jQuery JS and Chosen CSS links are omitted because they are defined from within the jsFiddle.)

Upvotes: 0

Views: 1159

Answers (2)

runTarm
runTarm

Reputation: 11547

To get the value in the ng-model attributes, you could use attrs.ngModel like this:

link: function (scope, element, attrs) {
  element.chosen();
  scope.$watch(attrs.ngModel, function () {
    element.trigger("chosen:updated");
  });
}

This way, you don't have to be worried if the attribute is actually be ng-model, data-ng-model, ng:model or other variant. It will be normalized by angular.

Hope this helps.

Upvotes: 1

Ben Fischer
Ben Fischer

Reputation: 6402

Whenever you create a reusable directive like this you want to be sure to give it isolate scope (https://egghead.io/lessons/angularjs-understanding-isolate-scope)

In your isolate scope object you can bind the value of the ng-model attribute to your directive's scope with something like this: model: "=ngModel" Then you just have to watch the model property you put on your directives scope. You can choose to deep watch that property by passing true as the 3rd parameter to watch, although in this example you wouldn't need to

testApp.directive('jqueryNgChosen', function () {
    return {
        scope: {
            'model': '=ngModel',
        },
        link: function (scope, element, attrs) {
            element.chosen();
            scope.$watch('model', function (newValue, old) {
                element.trigger("chosen:updated");
            }, true);
        }
    };
});

http://jsfiddle.net/z4vqxdtv/

Upvotes: 2

Related Questions