mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 23:40:23 +00:00
Because only controllers don't have currying, we can infer its arguments, all other APIs needing currying, automatic inference complicates the matters unecessary.
883 lines
31 KiB
JavaScript
883 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);
|
|
inferInjectionArgs(Controller);
|
|
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');
|
|
});
|