mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
882 lines
31 KiB
JavaScript
882 lines
31 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @ngdoc overview
|
|
* @name angular.directive
|
|
* @description
|
|
*
|
|
* Angular directives create custom attributes for DOM elements. A directive can modify the
|
|
* behavior of the element in which it is specified. Do not use directives to add elements to the
|
|
* DOM; instead, use {@link angular.widget widgets} to add DOM elements.
|
|
*
|
|
* Following is the list of built-in Angular directives:
|
|
*
|
|
* * {@link angular.directive.ng:autobind ng:autobind} - An Angular bootstrap parameter that can
|
|
* act as a directive.
|
|
* * {@link angular.directive.ng:bind ng:bind} - Creates a data-binding between an HTML text value
|
|
* and a data model.
|
|
* * {@link angular.directive.ng:bind-attr ng:bind-attr} - Creates a data-binding in a way similar
|
|
* to `ng:bind`, but uses JSON key / value pairs to do so.
|
|
* * {@link angular.directive.ng:bind-template ng:bind-template} - Replaces the text value of an
|
|
* element with a specified template.
|
|
* * {@link angular.directive.ng:change ng:change} - Executes an expression when the value of an
|
|
* input widget changes.
|
|
* * {@link angular.directive.ng:class ng:class} - Conditionally set a CSS class on an element.
|
|
* * {@link angular.directive.ng:class-even ng:class-even} - Like `ng:class`, but works in
|
|
* conjunction with {@link angular.widget.@ng:repeat} to affect even rows in a collection.
|
|
* * {@link angular.directive.ng:class-odd ng:class-odd} - Like `ng:class`, but works with {@link
|
|
* angular.widget.@ng:repeat} to affect odd rows.
|
|
* * {@link angular.directive.ng:click ng:click} - Executes custom behavior when an element is
|
|
* clicked.
|
|
* * {@link angular.directive.ng:controller ng:controller} - Creates a scope object linked to the
|
|
* DOM element and assigns behavior to the scope.
|
|
* * {@link angular.directive.ng:hide ng:hide} - Conditionally hides a portion of HTML.
|
|
* * {@link angular.directive.ng:href ng:href} - Places an href in the Angular namespace.
|
|
* * {@link angular.directive.ng:init} - Initialization tasks run before a template is executed.
|
|
* * {@link angular.directive.ng:show ng:show} - Conditionally displays a portion of HTML.
|
|
* * {@link angular.directive.ng:src ng:src} - Places a `src` attribute into the Angular namespace.
|
|
* * {@link angular.directive.ng:style ng:style} - Conditionally set CSS styles on an element.
|
|
* * {@link angular.directive.ng:submit} - Binds Angular expressions to `onSubmit` events.
|
|
*
|
|
* For more information about how Angular directives work, and to learn how to create your own
|
|
* directives, see {@link guide/dev_guide.compiler.directives Understanding Angular Directives} in
|
|
* the Angular Developer Guide.
|
|
*/
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:init
|
|
*
|
|
* @description
|
|
* The `ng:init` attribute specifies initialization tasks to be executed
|
|
* before the template enters execution mode during bootstrap.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<div ng:init="greeting='Hello'; person='World'">
|
|
{{greeting}} {{person}}!
|
|
</div>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check greeting', function(){
|
|
expect(binding('greeting')).toBe('Hello');
|
|
expect(binding('person')).toBe('World');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:init", function(expression){
|
|
return function(element){
|
|
this.$eval(expression);
|
|
};
|
|
});
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:controller
|
|
*
|
|
* @description
|
|
* The `ng:controller` directive assigns behavior to a scope. This is a key aspect of how angular
|
|
* supports the principles behind the Model-View-Controller design pattern.
|
|
*
|
|
* MVC components in angular:
|
|
*
|
|
* * Model — The Model is data in scope properties; scopes are attached to the DOM.
|
|
* * View — The template (HTML with data bindings) is rendered into the View.
|
|
* * Controller — The `ng:controller` directive specifies a Controller class; the class has
|
|
* methods that typically express the business logic behind the application.
|
|
*
|
|
* Note that an alternative way to define controllers is via the `{@link angular.service.$route}`
|
|
* service.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression Name of a globally accessible constructor function or an
|
|
* {@link guide/dev_guide.expressions expression} that on the current scope evaluates to a
|
|
* constructor function.
|
|
*
|
|
* @example
|
|
* Here is a simple form for editing user contact information. Adding, removing, clearing, and
|
|
* greeting are methods declared on the controller (see source tab). These methods can
|
|
* easily be called from the angular markup. Notice that the scope becomes the `this` for the
|
|
* controller's instance. This allows for easy access to the view data from the controller. Also
|
|
* notice that any changes to the data are automatically reflected in the View without the need
|
|
* for a manual update.
|
|
<doc:example>
|
|
<doc:source>
|
|
<script type="text/javascript">
|
|
function SettingsController() {
|
|
this.name = "John Smith";
|
|
this.contacts = [
|
|
{type:'phone', value:'408 555 1212'},
|
|
{type:'email', value:'john.smith@example.org'} ];
|
|
}
|
|
SettingsController.prototype = {
|
|
greet: function(){
|
|
alert(this.name);
|
|
},
|
|
addContact: function(){
|
|
this.contacts.push({type:'email', value:'yourname@example.org'});
|
|
},
|
|
removeContact: function(contactToRemove) {
|
|
angular.Array.remove(this.contacts, contactToRemove);
|
|
},
|
|
clearContact: function(contact) {
|
|
contact.type = 'phone';
|
|
contact.value = '';
|
|
}
|
|
};
|
|
</script>
|
|
<div ng:controller="SettingsController">
|
|
Name: <input type="text" name="name"/>
|
|
[ <a href="" ng:click="greet()">greet</a> ]<br/>
|
|
Contact:
|
|
<ul>
|
|
<li ng:repeat="contact in contacts">
|
|
<select name="contact.type">
|
|
<option>phone</option>
|
|
<option>email</option>
|
|
</select>
|
|
<input type="text" name="contact.value"/>
|
|
[ <a href="" ng:click="clearContact(contact)">clear</a>
|
|
| <a href="" ng:click="removeContact(contact)">X</a> ]
|
|
</li>
|
|
<li>[ <a href="" ng:click="addContact()">add</a> ]</li>
|
|
</ul>
|
|
</div>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check controller', function(){
|
|
expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
|
|
expect(element('.doc-example-live li[ng\\:repeat-index="0"] input').val())
|
|
.toBe('408 555 1212');
|
|
expect(element('.doc-example-live li[ng\\:repeat-index="1"] input').val())
|
|
.toBe('john.smith@example.org');
|
|
|
|
element('.doc-example-live li:first a:contains("clear")').click();
|
|
expect(element('.doc-example-live li:first input').val()).toBe('');
|
|
|
|
element('.doc-example-live li:last a:contains("add")').click();
|
|
expect(element('.doc-example-live li[ng\\:repeat-index="2"] input').val())
|
|
.toBe('yourname@example.org');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:controller", function(expression){
|
|
this.scope(function(scope){
|
|
var Controller =
|
|
getter(scope, expression, true) ||
|
|
getter(window, expression, true);
|
|
assertArgFn(Controller, expression);
|
|
return Controller;
|
|
});
|
|
return noop;
|
|
});
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name angular.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>
|
|
Enter name: <input type="text" name="name" value="Whirled"> <br>
|
|
Hello <span ng:bind="name"></span>!
|
|
</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>
|
|
*/
|
|
angularDirective("ng:bind", function(expression, element){
|
|
element.addClass('ng-binding');
|
|
var exprFn = parser(expression).statements();
|
|
return function(element) {
|
|
var lastValue = noop, lastError = noop;
|
|
this.$watch(function(scope) {
|
|
// TODO(misko): remove error handling https://github.com/angular/angular.js/issues/347
|
|
var error, value, html, isHtml, isDomElement,
|
|
hadOwnElement = scope.hasOwnProperty('$element'),
|
|
oldElement = scope.$element;
|
|
// TODO(misko): get rid of $element https://github.com/angular/angular.js/issues/348
|
|
scope.$element = element;
|
|
try {
|
|
value = exprFn(scope);
|
|
} catch (e) {
|
|
scope.$service('$exceptionHandler')(e);
|
|
error = formatError(e);
|
|
} finally {
|
|
if (hadOwnElement) {
|
|
scope.$element = oldElement;
|
|
} else {
|
|
delete scope.$element;
|
|
}
|
|
}
|
|
// If we are HTML, then save the raw HTML data so that we don't
|
|
// recompute sanitization since that is expensive.
|
|
// TODO: turn this into a more generic way to compute this
|
|
if ((isHtml = (value instanceof HTML)))
|
|
value = (html = value).html;
|
|
if (lastValue === value && lastError == error) return;
|
|
isDomElement = isElement(value);
|
|
if (!isHtml && !isDomElement && isObject(value)) {
|
|
value = toJson(value, true);
|
|
}
|
|
if (value != lastValue || error != lastError) {
|
|
lastValue = value;
|
|
lastError = error;
|
|
elementError(element, NG_EXCEPTION, error);
|
|
if (error) value = error;
|
|
if (isHtml) {
|
|
element.html(html.get());
|
|
} else if (isDomElement) {
|
|
element.html('');
|
|
element.append(value);
|
|
} else {
|
|
element.text(value == undefined ? '' : value);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
});
|
|
|
|
var bindTemplateCache = {};
|
|
function compileBindTemplate(template){
|
|
var fn = bindTemplateCache[template];
|
|
if (!fn) {
|
|
var bindings = [];
|
|
forEach(parseBindings(template), function(text){
|
|
var exp = binding(text);
|
|
bindings.push(exp
|
|
? function(scope, element) {
|
|
var error, value;
|
|
try {
|
|
value = scope.$eval(exp);
|
|
} catch(e) {
|
|
scope.$service('$exceptionHandler')(e);
|
|
error = toJson(e);
|
|
}
|
|
elementError(element, NG_EXCEPTION, error);
|
|
return error ? error : value;
|
|
}
|
|
: function() {
|
|
return text;
|
|
});
|
|
});
|
|
bindTemplateCache[template] = fn = function(scope, element, prettyPrintJson) {
|
|
var parts = [],
|
|
hadOwnElement = scope.hasOwnProperty('$element'),
|
|
oldElement = scope.$element;
|
|
|
|
// TODO(misko): get rid of $element
|
|
scope.$element = element;
|
|
try {
|
|
for (var i = 0; i < bindings.length; i++) {
|
|
var value = bindings[i](scope, element);
|
|
if (isElement(value))
|
|
value = '';
|
|
else if (isObject(value))
|
|
value = toJson(value, prettyPrintJson);
|
|
parts.push(value);
|
|
}
|
|
return parts.join('');
|
|
} finally {
|
|
if (hadOwnElement) {
|
|
scope.$element = oldElement;
|
|
} else {
|
|
delete scope.$element;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.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>
|
|
Salutation: <input type="text" name="salutation" value="Hello"><br/>
|
|
Name: <input type="text" name="name" value="World"><br/>
|
|
<pre ng:bind-template="{{salutation}} {{name}}!"></pre>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:bind', function(){
|
|
expect(using('.doc-example-live').binding('{{salutation}} {{name}}')).
|
|
toBe('Hello World!');
|
|
using('.doc-example-live').input('salutation').enter('Greetings');
|
|
using('.doc-example-live').input('name').enter('user');
|
|
expect(using('.doc-example-live').binding('{{salutation}} {{name}}')).
|
|
toBe('Greetings user!');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:bind-template", function(expression, element){
|
|
element.addClass('ng-binding');
|
|
var templateFn = compileBindTemplate(expression);
|
|
return function(element) {
|
|
var lastValue;
|
|
this.$watch(function(scope) {
|
|
var value = templateFn(scope, element, true);
|
|
if (value != lastValue) {
|
|
element.text(value);
|
|
lastValue = value;
|
|
}
|
|
});
|
|
};
|
|
});
|
|
|
|
var REMOVE_ATTRIBUTES = {
|
|
'disabled':'disabled',
|
|
'readonly':'readOnly',
|
|
'checked':'checked',
|
|
'selected':'selected',
|
|
'multiple':'multiple'
|
|
};
|
|
/**
|
|
* @ngdoc directive
|
|
* @name angular.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}"/>`
|
|
*
|
|
* The following HTML snippet shows how to specify `ng:bind-attr`:
|
|
* <pre>
|
|
* <a href="http://www.google.com/search?q={{query}}">Google</a>
|
|
* </pre>
|
|
*
|
|
* During compilation, the snippet gets translated to the following:
|
|
* <pre>
|
|
* <a ng:bind-attr='{"href":"http://www.google.com/search?q={{query}}"}'>Google</a>
|
|
* </pre>
|
|
*
|
|
* @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>
|
|
Google for:
|
|
<input type="text" name="query" value="AngularJS"/>
|
|
<a href="http://www.google.com/search?q={{query}}">Google</a>
|
|
</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>
|
|
*/
|
|
angularDirective("ng:bind-attr", function(expression){
|
|
return function(element){
|
|
var lastValue = {};
|
|
this.$watch(function(scope){
|
|
var values = scope.$eval(expression);
|
|
for(var key in values) {
|
|
var value = compileBindTemplate(values[key])(scope, element),
|
|
specialName = REMOVE_ATTRIBUTES[lowercase(key)];
|
|
if (lastValue[key] !== value) {
|
|
lastValue[key] = value;
|
|
if (specialName) {
|
|
if (toBoolean(value)) {
|
|
element.attr(specialName, specialName);
|
|
element.attr('ng-' + specialName, value);
|
|
} else {
|
|
element.removeAttr(specialName);
|
|
element.removeAttr('ng-' + specialName);
|
|
}
|
|
(element.data($$validate)||noop)();
|
|
} else {
|
|
element.attr(key, value);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
});
|
|
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:click
|
|
*
|
|
* @description
|
|
* The ng:click allows you to specify custom behavior when
|
|
* element is clicked.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to evaluate upon
|
|
* click.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<button ng:click="count = count + 1" ng:init="count=0">
|
|
Increment
|
|
</button>
|
|
count: {{count}}
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:click', function(){
|
|
expect(binding('count')).toBe('0');
|
|
element('.doc-example-live :button').click();
|
|
expect(binding('count')).toBe('1');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
/*
|
|
* A directive that allows creation of custom onclick handlers that are defined as angular
|
|
* expressions and are compiled and executed within the current scope.
|
|
*
|
|
* Events that are handled via these handler are always configured not to propagate further.
|
|
*
|
|
* TODO: maybe we should consider allowing users to control event propagation in the future.
|
|
*/
|
|
angularDirective("ng:click", function(expression, element){
|
|
return function(element){
|
|
var self = this;
|
|
element.bind('click', function(event){
|
|
self.$apply(expression);
|
|
event.stopPropagation();
|
|
});
|
|
};
|
|
});
|
|
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:submit
|
|
*
|
|
* @description
|
|
* Enables binding angular expressions to onsubmit events.
|
|
*
|
|
* Additionally it prevents the default action (which for form means sending the request to the
|
|
* server and reloading the current page).
|
|
*
|
|
* @element form
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<form ng:submit="list.push(text);text='';" ng:init="list=[]">
|
|
Enter text and hit enter:
|
|
<input type="text" name="text" value="hello"/>
|
|
<input type="submit" id="submit" value="Submit" />
|
|
</form>
|
|
<pre>list={{list}}</pre>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:submit', function(){
|
|
expect(binding('list')).toBe('list=[]');
|
|
element('.doc-example-live #submit').click();
|
|
expect(binding('list')).toBe('list=["hello"]');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:submit", function(expression, element) {
|
|
return function(element) {
|
|
var self = this;
|
|
element.bind('submit', function(event) {
|
|
self.$apply(expression);
|
|
event.preventDefault();
|
|
});
|
|
};
|
|
});
|
|
|
|
|
|
function ngClass(selector) {
|
|
return function(expression, element) {
|
|
return function(element) {
|
|
this.$watch(expression, function(scope, newVal, oldVal) {
|
|
if (selector(scope.$index)) {
|
|
element.removeClass(isArray(oldVal) ? oldVal.join(' ') : oldVal)
|
|
element.addClass(isArray(newVal) ? newVal.join(' ') : newVal);
|
|
}
|
|
});
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:class
|
|
*
|
|
* @description
|
|
* The `ng:class` allows you to set CSS class on HTML element dynamically by databinding an
|
|
* expression that represents all classes to be added.
|
|
*
|
|
* The directive won't add duplicate classes if a particular class was already set.
|
|
*
|
|
* When the expression changes, the previously added classes are removed and only then the classes
|
|
* new classes are added.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
|
|
* of the evaluation can be a string representing space delimited class names or an array.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<input type="button" value="set" ng:click="myVar='ng-input-indicator-wait'">
|
|
<input type="button" value="clear" ng:click="myVar=''">
|
|
<br>
|
|
<span ng:class="myVar">Sample Text </span>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:class', function(){
|
|
expect(element('.doc-example-live span').prop('className')).not().
|
|
toMatch(/ng-input-indicator-wait/);
|
|
|
|
using('.doc-example-live').element(':button:first').click();
|
|
|
|
expect(element('.doc-example-live span').prop('className')).
|
|
toMatch(/ng-input-indicator-wait/);
|
|
|
|
using('.doc-example-live').element(':button:last').click();
|
|
|
|
expect(element('.doc-example-live span').prop('className')).not().
|
|
toMatch(/ng-input-indicator-wait/);
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:class", ngClass(function(){return true;}));
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:class-odd
|
|
*
|
|
* @description
|
|
* The `ng:class-odd` and `ng:class-even` works exactly as
|
|
* {@link angular.directive.ng:class ng:class}, except it works in conjunction with `ng:repeat` and
|
|
* takes affect only on odd (even) rows.
|
|
*
|
|
* This directive can be applied only within a scope of an
|
|
* {@link angular.widget.@ng:repeat ng:repeat}.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
|
|
* of the evaluation can be a string representing space delimited class names or an array.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
|
|
<li ng:repeat="name in names">
|
|
<span ng:class-odd="'ng-format-negative'"
|
|
ng:class-even="'ng-input-indicator-wait'">
|
|
{{name}}
|
|
</span>
|
|
</li>
|
|
</ol>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:class-odd and ng:class-even', function(){
|
|
expect(element('.doc-example-live li:first span').prop('className')).
|
|
toMatch(/ng-format-negative/);
|
|
expect(element('.doc-example-live li:last span').prop('className')).
|
|
toMatch(/ng-input-indicator-wait/);
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:class-even
|
|
*
|
|
* @description
|
|
* The `ng:class-odd` and `ng:class-even` works exactly as
|
|
* {@link angular.directive.ng:class ng:class}, except it works in conjunction with `ng:repeat` and
|
|
* takes affect only on odd (even) rows.
|
|
*
|
|
* This directive can be applied only within a scope of an
|
|
* {@link angular.widget.@ng:repeat ng:repeat}.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} to eval. The result
|
|
* of the evaluation can be a string representing space delimited class names or an array.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
|
|
<li ng:repeat="name in names">
|
|
<span ng:class-odd="'ng-format-negative'"
|
|
ng:class-even="'ng-input-indicator-wait'">
|
|
{{name}}
|
|
</span>
|
|
</li>
|
|
</ol>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:class-odd and ng:class-even', function(){
|
|
expect(element('.doc-example-live li:first span').prop('className')).
|
|
toMatch(/ng-format-negative/);
|
|
expect(element('.doc-example-live li:last span').prop('className')).
|
|
toMatch(/ng-input-indicator-wait/);
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:show
|
|
*
|
|
* @description
|
|
* The `ng:show` and `ng:hide` directives show or hide a portion of the DOM tree (HTML)
|
|
* conditionally.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression If the {@link guide/dev_guide.expressions expression} is truthy
|
|
* then the element is shown or hidden respectively.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Click me: <input type="checkbox" name="checked"><br/>
|
|
Show: <span ng:show="checked">I show up when your checkbox is checked.</span> <br/>
|
|
Hide: <span ng:hide="checked">I hide when your checkbox is checked.</span>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:show / ng:hide', function(){
|
|
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
|
|
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
|
|
|
|
input('checked').check();
|
|
|
|
expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
|
|
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:show", function(expression, element){
|
|
return function(element){
|
|
this.$watch(expression, function(scope, value){
|
|
element.css('display', toBoolean(value) ? '' : 'none');
|
|
});
|
|
};
|
|
});
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:hide
|
|
*
|
|
* @description
|
|
* The `ng:hide` and `ng:show` directives hide or show a portion
|
|
* of the HTML conditionally.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression If the {@link guide/dev_guide.expressions expression} truthy then
|
|
* the element is shown or hidden respectively.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
Click me: <input type="checkbox" name="checked"><br/>
|
|
Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/>
|
|
Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:show / ng:hide', function(){
|
|
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
|
|
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
|
|
|
|
input('checked').check();
|
|
|
|
expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
|
|
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:hide", function(expression, element){
|
|
return function(element){
|
|
this.$watch(expression, function(scope, value){
|
|
element.css('display', toBoolean(value) ? 'none' : '');
|
|
});
|
|
};
|
|
});
|
|
|
|
/**
|
|
* @workInProgress
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:style
|
|
*
|
|
* @description
|
|
* The ng:style allows you to set CSS style on an HTML element conditionally.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} expression {@link guide/dev_guide.expressions Expression} which evals to an
|
|
* object whose keys are CSS style names and values are corresponding values for those CSS
|
|
* keys.
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<input type="button" value="set" ng:click="myStyle={color:'red'}">
|
|
<input type="button" value="clear" ng:click="myStyle={}">
|
|
<br/>
|
|
<span ng:style="myStyle">Sample Text</span>
|
|
<pre>myStyle={{myStyle}}</pre>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should check ng:style', function(){
|
|
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
|
|
element('.doc-example-live :button[value=set]').click();
|
|
expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
|
|
element('.doc-example-live :button[value=clear]').click();
|
|
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*/
|
|
angularDirective("ng:style", function(expression, element){
|
|
// TODO(i): this is inefficient (runs on every $digest) and obtrusive (overrides 3rd part css)
|
|
// we should change it in a similar way as I changed ng:class
|
|
return function(element){
|
|
var resetStyle = getStyle(element);
|
|
this.$watch(function(scope){
|
|
var style = scope.$eval(expression) || {}, key, mergedStyle = {};
|
|
for(key in style) {
|
|
if (resetStyle[key] === undefined) resetStyle[key] = '';
|
|
mergedStyle[key] = style[key];
|
|
}
|
|
for(key in resetStyle) {
|
|
mergedStyle[key] = mergedStyle[key] || resetStyle[key];
|
|
}
|
|
element.css(mergedStyle);
|
|
});
|
|
};
|
|
});
|
|
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name angular.directive.ng:cloak
|
|
*
|
|
* @description
|
|
* The `ng:cloak` directive is used to prevent the Angular html template from being briefly
|
|
* displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
|
|
* directive to avoid the undesirable flicker effect caused by the html template display.
|
|
*
|
|
* The directive can be applied to the `<body>` element, but typically a fine-grained application is
|
|
* prefered in order to benefit from progressive rendering of the browser view.
|
|
*
|
|
* `ng:cloak` works in cooperation with a css rule that is embedded within `angular.js` and
|
|
* `angular.min.js` files. Following is the css rule:
|
|
*
|
|
* <pre>
|
|
* [ng\:cloak], .ng-cloak {
|
|
* display: none;
|
|
* }
|
|
* </pre>
|
|
*
|
|
* When this css rule is loaded by the browser, all html elements (including their children) that
|
|
* are tagged with the `ng:cloak` directive are hidden. When Angular comes across this directive
|
|
* during the compilation of the template it deletes the `ng:cloak` element attribute, which
|
|
* makes the compiled element visible.
|
|
*
|
|
* For the best result, `angular.js` script must be loaded in the head section of the html file;
|
|
* alternatively, the css rule (above) must be included in the external stylesheet of the
|
|
* application.
|
|
*
|
|
* Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
|
|
* cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
|
|
* class `ng-cloak` in addition to `ng:cloak` directive as shown in the example below.
|
|
*
|
|
* @element ANY
|
|
*
|
|
* @example
|
|
<doc:example>
|
|
<doc:source>
|
|
<div id="template1" ng:cloak>{{ 'hello' }}</div>
|
|
<div id="template2" ng:cloak class="ng-cloak">{{ 'hello IE7' }}</div>
|
|
</doc:source>
|
|
<doc:scenario>
|
|
it('should remove the template directive and css class', function() {
|
|
expect(element('.doc-example-live #template1').attr('ng:cloak')).
|
|
not().toBeDefined();
|
|
expect(element('.doc-example-live #template2').attr('ng:cloak')).
|
|
not().toBeDefined();
|
|
});
|
|
</doc:scenario>
|
|
</doc:example>
|
|
*
|
|
*/
|
|
angularDirective("ng:cloak", function(expression, element) {
|
|
element.removeAttr('ng:cloak');
|
|
element.removeClass('ng-cloak');
|
|
});
|