mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-04-10 01:51:00 +00:00
It was not explicitly and consistently stated that the transient nature of boolean attributes precludes them from hosting binding expressions. This change make that more clear and reinforces the simplicity and elegance of the solution. Closes #5031
367 lines
13 KiB
JavaScript
367 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngHref
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* Using Angular markup like `{{hash}}` in an href attribute will
|
|
* make the link go to the wrong URL if the user clicks it before
|
|
* Angular has a chance to replace the `{{hash}}` markup with its
|
|
* value. Until Angular replaces the markup the link will be broken
|
|
* and will most likely return a 404 error.
|
|
*
|
|
* The `ngHref` directive solves this problem.
|
|
*
|
|
* The wrong way to write it:
|
|
* <pre>
|
|
* <a href="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* The correct way to write it:
|
|
* <pre>
|
|
* <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* @element A
|
|
* @param {template} ngHref any string which can contain `{{}}` markup.
|
|
*
|
|
* @example
|
|
* This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
|
|
* in links and their different behaviors:
|
|
<doc:example>
|
|
<doc:source>
|
|
<input ng-model="value" /><br />
|
|
<a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
|
|
<a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
|
|
<a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
|
|
<a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
|
|
<a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
|
|
<a id="link-6" ng-href="{{value}}">link</a> (link, change location)
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should execute ng-click but not reload when href without value', function() {
|
|
element('#link-1').click();
|
|
expect(input('value').val()).toEqual('1');
|
|
expect(element('#link-1').attr('href')).toBe("");
|
|
});
|
|
|
|
it('should execute ng-click but not reload when href empty string', function() {
|
|
element('#link-2').click();
|
|
expect(input('value').val()).toEqual('2');
|
|
expect(element('#link-2').attr('href')).toBe("");
|
|
});
|
|
|
|
it('should execute ng-click and change url when ng-href specified', function() {
|
|
expect(element('#link-3').attr('href')).toBe("/123");
|
|
|
|
element('#link-3').click();
|
|
expect(browser().window().path()).toEqual('/123');
|
|
});
|
|
|
|
it('should execute ng-click but not reload when href empty string and name specified', function() {
|
|
element('#link-4').click();
|
|
expect(input('value').val()).toEqual('4');
|
|
expect(element('#link-4').attr('href')).toBe('');
|
|
});
|
|
|
|
it('should execute ng-click but not reload when no href but name specified', function() {
|
|
element('#link-5').click();
|
|
expect(input('value').val()).toEqual('5');
|
|
expect(element('#link-5').attr('href')).toBe(undefined);
|
|
});
|
|
|
|
it('should only change url when only ng-href', function() {
|
|
input('value').enter('6');
|
|
expect(element('#link-6').attr('href')).toBe('6');
|
|
|
|
element('#link-6').click();
|
|
expect(browser().location().url()).toEqual('/6');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngSrc
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* Using Angular markup like `{{hash}}` in a `src` attribute doesn't
|
|
* work right: The browser will fetch from the URL with the literal
|
|
* text `{{hash}}` until Angular replaces the expression inside
|
|
* `{{hash}}`. The `ngSrc` directive solves this problem.
|
|
*
|
|
* The buggy way to write it:
|
|
* <pre>
|
|
* <img src="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* The correct way to write it:
|
|
* <pre>
|
|
* <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
|
|
* </pre>
|
|
*
|
|
* @element IMG
|
|
* @param {template} ngSrc any string which can contain `{{}}` markup.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngSrcset
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
|
|
* work right: The browser will fetch from the URL with the literal
|
|
* text `{{hash}}` until Angular replaces the expression inside
|
|
* `{{hash}}`. The `ngSrcset` directive solves this problem.
|
|
*
|
|
* The buggy way to write it:
|
|
* <pre>
|
|
* <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
|
|
* </pre>
|
|
*
|
|
* The correct way to write it:
|
|
* <pre>
|
|
* <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
|
|
* </pre>
|
|
*
|
|
* @element IMG
|
|
* @param {template} ngSrcset any string which can contain `{{}}` markup.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngDisabled
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
*
|
|
* The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
|
|
* <pre>
|
|
* <div ng-init="scope = { isDisabled: false }">
|
|
* <button disabled="{{scope.isDisabled}}">Disabled</button>
|
|
* </div>
|
|
* </pre>
|
|
*
|
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
|
* such as disabled. (Their presence means true and their absence means false.)
|
|
* If we put an Angular interpolation expression into such an attribute then the
|
|
* binding information would be lost when the browser removes the attribute.
|
|
* The `ngDisabled` directive solves this problem for the `disabled` attribute.
|
|
* This complementary directive is not removed by the browser and so provides
|
|
* a permanent reliable place to store the binding information.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
|
|
<button ng-model="button" ng-disabled="checked">Button</button>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should toggle button', function() {
|
|
expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
|
|
input('checked').check();
|
|
expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element INPUT
|
|
* @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
|
|
* then special attribute "disabled" will be set on the element
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngChecked
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
|
* such as checked. (Their presence means true and their absence means false.)
|
|
* If we put an Angular interpolation expression into such an attribute then the
|
|
* binding information would be lost when the browser removes the attribute.
|
|
* The `ngChecked` directive solves this problem for the `checked` attribute.
|
|
* This complementary directive is not removed by the browser and so provides
|
|
* a permanent reliable place to store the binding information.
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me to check both: <input type="checkbox" ng-model="master"><br/>
|
|
<input id="checkSlave" type="checkbox" ng-checked="master">
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check both checkBoxes', function() {
|
|
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
|
|
input('master').check();
|
|
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element INPUT
|
|
* @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
|
|
* then special attribute "checked" will be set on the element
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngReadonly
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
|
* such as readonly. (Their presence means true and their absence means false.)
|
|
* If we put an Angular interpolation expression into such an attribute then the
|
|
* binding information would be lost when the browser removes the attribute.
|
|
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
|
|
* This complementary directive is not removed by the browser and so provides
|
|
* a permanent reliable place to store the binding information.
|
|
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
|
|
<input type="text" ng-readonly="checked" value="I'm Angular"/>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should toggle readonly attr', function() {
|
|
expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
|
|
input('checked').check();
|
|
expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element INPUT
|
|
* @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
|
|
* then special attribute "readonly" will be set on the element
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngSelected
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
|
* such as selected. (Their presence means true and their absence means false.)
|
|
* If we put an Angular interpolation expression into such an attribute then the
|
|
* binding information would be lost when the browser removes the attribute.
|
|
* The `ngSelected` directive solves this problem for the `selected` atttribute.
|
|
* This complementary directive is not removed by the browser and so provides
|
|
* a permanent reliable place to store the binding information.
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me to select: <input type="checkbox" ng-model="selected"><br/>
|
|
<select>
|
|
<option>Hello!</option>
|
|
<option id="greet" ng-selected="selected">Greetings!</option>
|
|
</select>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should select Greetings!', function() {
|
|
expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
|
|
input('selected').check();
|
|
expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element OPTION
|
|
* @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
|
|
* then special attribute "selected" will be set on the element
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngOpen
|
|
* @restrict A
|
|
*
|
|
* @description
|
|
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
|
* such as open. (Their presence means true and their absence means false.)
|
|
* If we put an Angular interpolation expression into such an attribute then the
|
|
* binding information would be lost when the browser removes the attribute.
|
|
* The `ngOpen` directive solves this problem for the `open` attribute.
|
|
* This complementary directive is not removed by the browser and so provides
|
|
* a permanent reliable place to store the binding information.
|
|
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Check me check multiple: <input type="checkbox" ng-model="open"><br/>
|
|
<details id="details" ng-open="open">
|
|
<summary>Show/Hide me</summary>
|
|
</details>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should toggle open', function() {
|
|
expect(element('#details').prop('open')).toBeFalsy();
|
|
input('open').check();
|
|
expect(element('#details').prop('open')).toBeTruthy();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
* @element DETAILS
|
|
* @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
|
|
* then special attribute "open" will be set on the element
|
|
*/
|
|
|
|
var ngAttributeAliasDirectives = {};
|
|
|
|
|
|
// boolean attrs are evaluated
|
|
forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
|
// binding to multiple is not supported
|
|
if (propName == "multiple") return;
|
|
|
|
var normalized = directiveNormalize('ng-' + attrName);
|
|
ngAttributeAliasDirectives[normalized] = function() {
|
|
return {
|
|
priority: 100,
|
|
compile: function() {
|
|
return function(scope, element, attr) {
|
|
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
|
attr.$set(attrName, !!value);
|
|
});
|
|
};
|
|
}
|
|
};
|
|
};
|
|
});
|
|
|
|
|
|
// ng-src, ng-srcset, ng-href are interpolated
|
|
forEach(['src', 'srcset', 'href'], function(attrName) {
|
|
var normalized = directiveNormalize('ng-' + attrName);
|
|
ngAttributeAliasDirectives[normalized] = function() {
|
|
return {
|
|
priority: 99, // it needs to run after the attributes are interpolated
|
|
link: function(scope, element, attr) {
|
|
attr.$observe(normalized, function(value) {
|
|
if (!value)
|
|
return;
|
|
|
|
attr.$set(attrName, value);
|
|
|
|
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
|
|
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
|
|
// to set the property as well to achieve the desired effect.
|
|
// we use attr[attrName] value since $set can sanitize the url.
|
|
if (msie) element.prop(attrName, attr[attrName]);
|
|
});
|
|
}
|
|
};
|
|
};
|
|
});
|