aboutsummaryrefslogtreecommitdiffstats
path: root/src/directive/ngBind.js
blob: 4f4433281b282588927c0d80f1c96bf4b945a4a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
'use strict';

/**
 * @ngdoc directive
 * @name angular.module.ng.$compileProvider.directive.ng-bind
 *
 * @description
 * The `ng-bind` attribute tells Angular to replace the text content of the specified HTML element
 * with the value of a given expression, and to update the text content when the value of that
 * expression changes.
 *
 * Typically, you don't use `ng-bind` directly, but instead you use the double curly markup like
 * `{{ expression }}` and let the Angular compiler transform it to
 * `<span ng-bind="expression"></span>` when the template is compiled.
 *
 * @element ANY
 * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
 *
 * @example
 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
   <doc:example>
     <doc:source>
       <script>
         function Ctrl($scope) {
           $scope.name = 'Whirled';
         }
       </script>
       <div ng-controller="Ctrl">
         Enter name: <input type="text" ng-model="name"> <br/>
         Hello <span ng-bind="name"></span>!
       </div>
     </doc:source>
     <doc:scenario>
       it('should check ng-bind', function() {
         expect(using('.doc-example-live').binding('name')).toBe('Whirled');
         using('.doc-example-live').input('name').enter('world');
         expect(using('.doc-example-live').binding('name')).toBe('world');
       });
     </doc:scenario>
   </doc:example>
 */
var ngBindDirective = ngDirective(function(scope, element, attr) {
  element.addClass('ng-binding').data('$binding', attr.ngBind);
  scope.$watch(attr.ngBind, function(value) {
    element.text(value == undefined ? '' : value);
  });
});


/**
 * @ngdoc directive
 * @name angular.module.ng.$compileProvider.directive.ng-bind-html-unsafe
 *
 * @description
 * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
 * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if
 * {@link angular.module.ng.$compileProvider.directive.ng-bind-html ng-bind-html} directive is too
 * restrictive and when you absolutely trust the source of the content you are binding to.
 *
 * See {@link angular.module.ng.$sanitize $sanitize} docs for examples.
 *
 * @element ANY
 * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
 */
var ngBindHtmlUnsafeDirective = ngDirective(function(scope, element, attr) {
  element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
  scope.$watch(attr.ngBindHtmlUnsafe, function(value) {
    element.html(value == undefined ? '' : value);
  });
});


/**
 * @ngdoc directive
 * @name angular.module.ng.$compileProvider.directive.ng-bind-html
 *
 * @description
 * Creates a binding that will sanitize the result of evaluating the `expression` with the
 * {@link angular.module.ng.$sanitize $sanitize} service and innerHTML the result into the current
 * element.
 *
 * See {@link angular.module.ng.$sanitize $sanitize} docs for examples.
 *
 * @element ANY
 * @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate.
 */
var ngBindHtmlDirective = ['$sanitize', function($sanitize) {
  return function(scope, element, attr) {
    element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
    scope.$watch(attr.ngBindHtml, function(value) {
      if (value = $sanitize(value)) {
        element.html(value);
      }
    });
  }
}];


/**
 * @ngdoc directive
 * @name angular.module.ng.$compileProvider.directive.ng-bind-template
 *
 * @description
 * The `ng-bind-template` attribute specifies that the element
 * text should be replaced with the template in ng-bind-template.
 * Unlike ng-bind the ng-bind-template can contain multiple `{{` `}}`
 * expressions. (This is required since some HTML elements
 * can not have SPAN elements such as TITLE, or OPTION to name a few.)
 *
 * @element ANY
 * @param {string} template of form
 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
 *
 * @example
 * Try it here: enter text in text box and watch the greeting change.
   <doc:example>
     <doc:source>
       <script>
         function Ctrl($scope) {
           $scope.salutation = 'Hello';
           $scope.name = 'World';
         }
       </script>
       <div ng-controller="Ctrl">
        Salutation: <input type="text" ng-model="salutation"><br/>
        Name: <input type="text" ng-model="name"><br/>
        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
       </div>
     </doc:source>
     <doc:scenario>
       it('should check ng-bind', function() {
         expect(using('.doc-example-live').binding('salutation')).
           toBe('Hello');
         expect(using('.doc-example-live').binding('name')).
           toBe('World');
         using('.doc-example-live').input('salutation').enter('Greetings');
         using('.doc-example-live').input('name').enter('user');
         expect(using('.doc-example-live').binding('salutation')).
           toBe('Greetings');
         expect(using('.doc-example-live').binding('name')).
           toBe('user');
       });
     </doc:scenario>
   </doc:example>
 */
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
  return function(scope, element, attr) {
    // TODO: move this to scenario runner
    var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
    element.addClass('ng-binding').data('$binding', interpolateFn);
    attr.$observe('ngBindTemplate', function(value) {
      element.text(value);
    });
  }
}];


/**
 * @ngdoc directive
 * @name angular.module.ng.$compileProvider.directive.ng-bind-attr
 *
 * @description
 * The `ng-bind-attr` attribute specifies that a
 * {@link guide/dev_guide.templates.databinding databinding}  should be created between a particular
 * element attribute and a given expression. Unlike `ng-bind`, the `ng-bind-attr` contains one or
 * more JSON key value pairs; each pair specifies an attribute and the
 * {@link guide/dev_guide.expressions expression} to which it will be mapped.
 *
 * Instead of writing `ng-bind-attr` statements in your HTML, you can use double-curly markup to
 * specify an <tt ng-non-bindable>{{expression}}</tt> for the value of an attribute.
 * At compile time, the attribute is translated into an
 * `<span ng-bind-attr="{attr:expression}"></span>`.
 *
 * The following HTML snippet shows how to specify `ng-bind-attr`:
 * <pre>
 *   <a ng-bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>Google</a>
 * </pre>
 *
 * This is cumbersome, so as we mentioned using double-curly markup is a prefered way of creating
 * this binding:
 * <pre>
 *   <a href="http://www.google.com/search?q={{query}}">Google</a>
 * </pre>
 *
 * During compilation, the template with attribute markup gets translated to the ng-bind-attr form
 * mentioned above.
 *
 * _Note_: You might want to consider using {@link angular.module.ng.$compileProvider.directive.ng-href ng-href} instead of
 * `href` if the binding is present in the main application template (`index.html`) and you want to
 * make sure that a user is not capable of clicking on raw/uncompiled link.
 *
 *
 * @element ANY
 * @param {string} attribute_json one or more JSON key-value pairs representing
 *    the attributes to replace with expressions. Each key matches an attribute
 *    which needs to be replaced. Each value is a text template of
 *    the attribute with the embedded
 *    <tt ng-non-bindable>{{expression}}</tt>s. Any number of
 *    key-value pairs can be specified.
 *
 * @example
 * Enter a search string in the Live Preview text box and then click "Google". The search executes instantly.
   <doc:example>
     <doc:source>
       <script>
         function Ctrl($scope) {
           $scope.query = 'AngularJS';
         }
       </script>
       <div ng-controller="Ctrl">
        Google for:
        <input type="text" ng-model="query"/>
        <a ng-bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>
          Google
        </a> (ng-bind-attr) |
        <a href="http://www.google.com/search?q={{query}}">Google</a>
        (curly binding in attribute val)
       </div>
     </doc:source>
     <doc:scenario>
       it('should check ng-bind-attr', function() {
         expect(using('.doc-example-live').element('a').attr('href')).
           toBe('http://www.google.com/search?q=AngularJS');
         using('.doc-example-live').input('query').enter('google');
         expect(using('.doc-example-live').element('a').attr('href')).
           toBe('http://www.google.com/search?q=google');
       });
     </doc:scenario>
   </doc:example>
 */

var ngBindAttrDirective = ['$interpolate', function($interpolate) {
  return function(scope, element, attr) {
    var lastValue = {};
    var interpolateFns = {};
    scope.$watch(function() {
      var values = scope.$eval(attr.ngBindAttr);
      for(var key in values) {
        var exp = values[key],
            fn = (interpolateFns[exp] ||
              (interpolateFns[values[key]] = $interpolate(exp))),
            value = fn(scope);
        if (lastValue[key] !== value) {
          attr.$set(key, lastValue[key] = value);
        }
      }
    });
  }
}];