mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
305 lines
9.8 KiB
JavaScript
305 lines
9.8 KiB
JavaScript
'use strict';
|
|
|
|
function classDirective(name, selector) {
|
|
name = 'ngClass' + name;
|
|
return function() {
|
|
return {
|
|
restrict: 'AC',
|
|
link: function(scope, element, attr) {
|
|
var oldVal = undefined;
|
|
|
|
scope.$watch(attr[name], ngClassWatchAction, true);
|
|
|
|
attr.$observe('class', function(value) {
|
|
ngClassWatchAction(scope.$eval(attr[name]));
|
|
});
|
|
|
|
|
|
if (name !== 'ngClass') {
|
|
scope.$watch('$index', function($index, old$index) {
|
|
var mod = $index & 1;
|
|
if (mod !== old$index & 1) {
|
|
if (mod === selector) {
|
|
addClass(scope.$eval(attr[name]));
|
|
} else {
|
|
removeClass(scope.$eval(attr[name]));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function ngClassWatchAction(newVal) {
|
|
if (selector === true || scope.$index % 2 === selector) {
|
|
if (oldVal && !equals(newVal,oldVal)) {
|
|
removeClass(oldVal);
|
|
}
|
|
addClass(newVal);
|
|
}
|
|
oldVal = copy(newVal);
|
|
}
|
|
|
|
|
|
function removeClass(classVal) {
|
|
attr.$removeClass(flattenClasses(classVal));
|
|
}
|
|
|
|
|
|
function addClass(classVal) {
|
|
attr.$addClass(flattenClasses(classVal));
|
|
}
|
|
|
|
function flattenClasses(classVal) {
|
|
if(isArray(classVal)) {
|
|
return classVal.join(' ');
|
|
} else if (isObject(classVal)) {
|
|
var classes = [], i = 0;
|
|
forEach(classVal, function(v, k) {
|
|
if (v) {
|
|
classes.push(k);
|
|
}
|
|
});
|
|
return classes.join(' ');
|
|
}
|
|
|
|
return classVal;
|
|
};
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngClass
|
|
* @restrict AC
|
|
*
|
|
* @description
|
|
* The `ngClass` allows you to set CSS classes on HTML an 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
|
|
* new classes are added.
|
|
*
|
|
* @animations
|
|
* add - happens just before the class is applied to the element
|
|
* remove - happens just before the class is removed from the element
|
|
*
|
|
* @element ANY
|
|
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
|
|
* of the evaluation can be a string representing space delimited class
|
|
* names, an array, or a map of class names to boolean values. In the case of a map, the
|
|
* names of the properties whose values are truthy will be added as css classes to the
|
|
* element.
|
|
*
|
|
* @example Example that demostrates basic bindings via ngClass directive.
|
|
<example>
|
|
<file name="index.html">
|
|
<p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</p>
|
|
<input type="checkbox" ng-model="bold"> bold
|
|
<input type="checkbox" ng-model="strike"> strike
|
|
<input type="checkbox" ng-model="red"> red
|
|
<hr>
|
|
<p ng-class="style">Using String Syntax</p>
|
|
<input type="text" ng-model="style" placeholder="Type: bold strike red">
|
|
<hr>
|
|
<p ng-class="[style1, style2, style3]">Using Array Syntax</p>
|
|
<input ng-model="style1" placeholder="Type: bold"><br>
|
|
<input ng-model="style2" placeholder="Type: strike"><br>
|
|
<input ng-model="style3" placeholder="Type: red"><br>
|
|
</file>
|
|
<file name="style.css">
|
|
.strike {
|
|
text-decoration: line-through;
|
|
}
|
|
.bold {
|
|
font-weight: bold;
|
|
}
|
|
.red {
|
|
color: red;
|
|
}
|
|
</file>
|
|
<file name="scenario.js">
|
|
it('should let you toggle the class', function() {
|
|
|
|
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
|
|
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);
|
|
|
|
input('bold').check();
|
|
expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);
|
|
|
|
input('red').check();
|
|
expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
|
|
});
|
|
|
|
it('should let you toggle string example', function() {
|
|
expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('');
|
|
input('style').enter('red');
|
|
expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('red');
|
|
});
|
|
|
|
it('array example should have 3 classes', function() {
|
|
expect(element('.doc-example-live p:last').prop('className')).toBe('');
|
|
input('style1').enter('bold');
|
|
input('style2').enter('strike');
|
|
input('style3').enter('red');
|
|
expect(element('.doc-example-live p:last').prop('className')).toBe('bold strike red');
|
|
});
|
|
</file>
|
|
</example>
|
|
|
|
## Animations
|
|
|
|
The example below demonstrates how to perform animations using ngClass.
|
|
|
|
<example animations="true">
|
|
<file name="index.html">
|
|
<input type="button" value="set" ng-click="myVar='my-class'">
|
|
<input type="button" value="clear" ng-click="myVar=''">
|
|
<br>
|
|
<span ng-class="myVar">Sample Text</span>
|
|
</file>
|
|
<file name="style.css">
|
|
.my-class-add, .my-class-remove {
|
|
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
|
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
|
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
|
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
|
}
|
|
|
|
.my-class,
|
|
.my-class-add.my-class-add-active {
|
|
color: red;
|
|
font-size:3em;
|
|
}
|
|
|
|
.my-class-remove.my-class-remove-active {
|
|
font-size:1.0em;
|
|
color:black;
|
|
}
|
|
</file>
|
|
<file name="scenario.js">
|
|
it('should check ng-class', function() {
|
|
expect(element('.doc-example-live span').prop('className')).not().
|
|
toMatch(/my-class/);
|
|
|
|
using('.doc-example-live').element(':button:first').click();
|
|
|
|
expect(element('.doc-example-live span').prop('className')).
|
|
toMatch(/my-class/);
|
|
|
|
using('.doc-example-live').element(':button:last').click();
|
|
|
|
expect(element('.doc-example-live span').prop('className')).not().
|
|
toMatch(/my-class/);
|
|
});
|
|
</file>
|
|
</example>
|
|
|
|
|
|
## ngClass and pre-existing CSS3 Transitions/Animations
|
|
The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
|
|
Therefore, if any CSS3 Transition/Animation styles (outside of ngAnimate) are set on the element, then, if a ngClass animation
|
|
is triggered, the ngClass animation will be skipped so that ngAnimate can allow for the pre-existing transition or animation to
|
|
take over. This restriction allows for ngClass to still work with standard CSS3 Transitions/Animations that are defined
|
|
outside of ngAnimate.
|
|
*/
|
|
var ngClassDirective = classDirective('', true);
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngClassOdd
|
|
* @restrict AC
|
|
*
|
|
* @description
|
|
* The `ngClassOdd` and `ngClassEven` directives work exactly as
|
|
* {@link ng.directive:ngClass ngClass}, except it works in
|
|
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
|
|
*
|
|
* This directive can be applied only within a scope of an
|
|
* {@link ng.directive:ngRepeat ngRepeat}.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
|
|
* of the evaluation can be a string representing space delimited class names or an array.
|
|
*
|
|
* @example
|
|
<example>
|
|
<file name="index.html">
|
|
<ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
|
|
<li ng-repeat="name in names">
|
|
<span ng-class-odd="'odd'" ng-class-even="'even'">
|
|
{{name}}
|
|
</span>
|
|
</li>
|
|
</ol>
|
|
</file>
|
|
<file name="style.css">
|
|
.odd {
|
|
color: red;
|
|
}
|
|
.even {
|
|
color: blue;
|
|
}
|
|
</file>
|
|
<file name="scenario.js">
|
|
it('should check ng-class-odd and ng-class-even', function() {
|
|
expect(element('.doc-example-live li:first span').prop('className')).
|
|
toMatch(/odd/);
|
|
expect(element('.doc-example-live li:last span').prop('className')).
|
|
toMatch(/even/);
|
|
});
|
|
</file>
|
|
</example>
|
|
*/
|
|
var ngClassOddDirective = classDirective('Odd', 0);
|
|
|
|
/**
|
|
* @ngdoc directive
|
|
* @name ng.directive:ngClassEven
|
|
* @restrict AC
|
|
*
|
|
* @description
|
|
* The `ngClassOdd` and `ngClassEven` directives work exactly as
|
|
* {@link ng.directive:ngClass ngClass}, except it works in
|
|
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
|
|
*
|
|
* This directive can be applied only within a scope of an
|
|
* {@link ng.directive:ngRepeat ngRepeat}.
|
|
*
|
|
* @element ANY
|
|
* @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
|
|
* result of the evaluation can be a string representing space delimited class names or an array.
|
|
*
|
|
* @example
|
|
<example>
|
|
<file name="index.html">
|
|
<ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
|
|
<li ng-repeat="name in names">
|
|
<span ng-class-odd="'odd'" ng-class-even="'even'">
|
|
{{name}}
|
|
</span>
|
|
</li>
|
|
</ol>
|
|
</file>
|
|
<file name="style.css">
|
|
.odd {
|
|
color: red;
|
|
}
|
|
.even {
|
|
color: blue;
|
|
}
|
|
</file>
|
|
<file name="scenario.js">
|
|
it('should check ng-class-odd and ng-class-even', function() {
|
|
expect(element('.doc-example-live li:first span').prop('className')).
|
|
toMatch(/odd/);
|
|
expect(element('.doc-example-live li:last span').prop('className')).
|
|
toMatch(/even/);
|
|
});
|
|
</file>
|
|
</example>
|
|
*/
|
|
var ngClassEvenDirective = classDirective('Even', 1);
|