angular.js/src/directives.js

286 lines
8.6 KiB
JavaScript
Raw Normal View History

angularDirective("ng:init", function(expression){
2010-03-26 05:03:11 +00:00
return function(element){
this.$tryEval(expression, element);
2010-03-16 17:30:26 +00:00
};
});
angularDirective("ng:controller", function(expression){
this.scope(true);
2010-04-06 04:26:52 +00:00
return function(element){
var controller = getter(window, expression, true) || getter(this, expression, true);
if (!controller)
throw "Can not find '"+expression+"' controller.";
if (!isFunction(controller))
throw "Reference '"+expression+"' is not a class.";
this.$become(controller);
};
});
angularDirective("ng:eval", function(expression){
2010-03-26 05:03:11 +00:00
return function(element){
this.$onEval(expression, element);
};
2010-03-16 17:30:26 +00:00
});
angularDirective("ng:bind", function(expression){
2010-03-22 20:58:04 +00:00
return function(element) {
var lastValue = noop, lastError = noop;
this.$onEval(function() {
2010-07-15 20:13:21 +00:00
var error, value, isHtml, isDomElement,
oldElement = this.hasOwnProperty($$element) ? this.$element : _undefined;
2010-07-15 20:13:21 +00:00
this.$element = element;
value = this.$tryEval(expression, function(e){
error = toJson(e);
});
this.$element = oldElement;
if (lastValue === value && lastError == error) return;
2010-09-14 21:22:15 +00:00
isHtml = value instanceof HTML;
isDomElement = isElement(value);
2010-05-07 20:43:54 +00:00
if (!isHtml && !isDomElement && isObject(value)) {
2010-04-21 01:14:13 +00:00
value = toJson(value);
}
if (value != lastValue || error != lastError) {
lastValue = value;
lastError = error;
elementError(element, NG_EXCEPTION, error);
if (error) value = error;
2010-04-23 00:11:56 +00:00
if (isHtml) {
element.html(value.html);
2010-05-07 20:43:54 +00:00
} else if (isDomElement) {
element.html('');
element.append(value);
} else {
element.text(value);
}
}
2010-03-26 05:03:11 +00:00
}, element);
2010-03-16 17:30:26 +00:00
};
});
2010-03-23 21:57:11 +00:00
var bindTemplateCache = {};
function compileBindTemplate(template){
var fn = bindTemplateCache[template];
if (!fn) {
var bindings = [];
foreach(parseBindings(template), function(text){
var exp = binding(text);
2010-03-31 20:57:25 +00:00
bindings.push(exp ? function(element){
var error, value = this.$tryEval(exp, function(e){
error = toJson(e);
});
elementError(element, NG_EXCEPTION, error);
return error ? error : value;
} : function() {
2010-03-23 21:57:11 +00:00
return text;
});
});
2010-03-31 20:57:25 +00:00
bindTemplateCache[template] = fn = function(element){
2010-07-15 20:13:21 +00:00
var parts = [], self = this,
oldElement = this.hasOwnProperty($$element) ? self.$element : _undefined;
2010-07-15 20:35:00 +00:00
self.$element = element;
for ( var i = 0; i < bindings.length; i++) {
var value = bindings[i].call(self, element);
if (isElement(value))
value = '';
else if (isObject(value))
value = toJson(value, true);
parts.push(value);
2010-09-14 21:22:15 +00:00
}
2010-07-15 20:35:00 +00:00
self.$element = oldElement;
2010-03-23 21:57:11 +00:00
return parts.join('');
};
}
return fn;
2010-04-04 00:04:36 +00:00
}
angularDirective("ng:bind-template", function(expression){
2010-03-23 21:57:11 +00:00
var templateFn = compileBindTemplate(expression);
return function(element) {
var lastValue;
2010-03-26 05:03:11 +00:00
this.$onEval(function() {
2010-03-31 20:57:25 +00:00
var value = templateFn.call(this, element);
2010-03-23 21:57:11 +00:00
if (value != lastValue) {
element.text(value);
lastValue = value;
}
2010-03-26 05:03:11 +00:00
}, element);
2010-03-23 21:57:11 +00:00
};
});
var REMOVE_ATTRIBUTES = {
2010-04-23 05:09:17 +00:00
'disabled':'disabled',
'readonly':'readOnly',
'checked':'checked'
};
angularDirective("ng:bind-attr", function(expression){
2010-03-22 20:58:04 +00:00
return function(element){
var lastValue = {};
var updateFn = element.parent().data('$update');
2010-03-26 05:03:11 +00:00
this.$onEval(function(){
var values = this.$eval(expression);
for(var key in values) {
var value = compileBindTemplate(values[key]).call(this, element),
2010-04-23 05:09:17 +00:00
specialName = REMOVE_ATTRIBUTES[lowercase(key)];
if (lastValue[key] !== value) {
lastValue[key] = value;
if (specialName) {
if (element[specialName] = toBoolean(value)) {
element.attr(specialName, value);
} else {
element.removeAttr(key);
}
(element.data('$validate')||noop)();
2010-04-23 05:09:17 +00:00
} else {
element.attr(key, value);
2010-04-16 21:01:29 +00:00
}
this.$postEval(updateFn);
2010-03-30 21:55:04 +00:00
}
2010-09-14 21:22:15 +00:00
}
2010-03-26 05:03:11 +00:00
}, element);
2010-03-16 17:30:26 +00:00
};
});
angularWidget("@ng:non-bindable", noop);
2010-03-22 20:58:04 +00:00
angularWidget("@ng:repeat", function(expression, element){
element.removeAttr('ng:repeat');
element.replaceWith(this.comment("ng:repeat: " + expression));
2010-03-26 05:03:11 +00:00
var template = this.compile(element);
return function(reference){
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
lhs, rhs, valueIdent, keyIdent;
if (! match) {
throw "Expected ng:repeat in form of 'item in collection' but got '" +
2010-03-22 22:46:34 +00:00
expression + "'.";
2010-03-26 05:03:11 +00:00
}
lhs = match[1];
rhs = match[2];
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
if (!match) {
throw "'item' in 'item in collection' should be identifier or (key, value) but got '" +
2010-03-22 22:46:34 +00:00
keyValue + "'.";
2010-03-26 05:03:11 +00:00
}
valueIdent = match[3] || match[1];
keyIdent = match[2];
2010-03-22 22:46:34 +00:00
2010-03-26 05:03:11 +00:00
var children = [], currentScope = this;
this.$onEval(function(){
var index = 0, childCount = children.length, childScope, lastElement = reference,
2010-07-09 21:45:29 +00:00
collection = this.$tryEval(rhs, reference), is_array = isArray(collection);
for ( var key in collection) {
2010-07-23 20:36:08 +00:00
if (!is_array || collection.hasOwnProperty(key)) {
if (index < childCount) {
// reuse existing child
childScope = children[index];
childScope[valueIdent] = collection[key];
if (keyIdent) childScope[keyIdent] = key;
} else {
// grow children
childScope = template(quickClone(element), createScope(currentScope));
2010-07-23 20:36:08 +00:00
childScope[valueIdent] = collection[key];
if (keyIdent) childScope[keyIdent] = key;
lastElement.after(childScope.$element);
childScope.$index = index;
childScope.$element.attr('ng:repeat-index', index);
childScope.$init();
children.push(childScope);
}
childScope.$eval();
lastElement = childScope.$element;
index ++;
2010-03-22 20:58:04 +00:00
}
2010-09-14 21:22:15 +00:00
}
2010-03-22 20:58:04 +00:00
// shrink children
while(children.length > index) {
2010-03-26 05:03:11 +00:00
children.pop().$element.remove();
2010-03-22 20:58:04 +00:00
}
2010-03-26 05:03:11 +00:00
}, reference);
2010-03-16 17:30:26 +00:00
};
2010-03-30 21:55:04 +00:00
});
2010-03-22 20:58:04 +00:00
/*
* 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 even propagation in the future.
*/
angularDirective("ng:click", function(expression, element){
2010-03-26 05:03:11 +00:00
return function(element){
var self = this;
2010-07-27 22:54:50 +00:00
element.bind('click', function(event){
2010-03-26 05:03:11 +00:00
self.$tryEval(expression, element);
2010-04-04 00:04:36 +00:00
self.$root.$eval();
event.stopPropagation();
});
};
});
angularDirective("ng:watch", function(expression, element){
2010-03-26 05:03:11 +00:00
return function(element){
2010-03-30 21:55:04 +00:00
var self = this;
new Parser(expression).watch()({
addListener:function(watch, exp){
self.$watch(watch, function(){
return exp(self);
2010-03-30 21:55:04 +00:00
}, element);
}
});
2010-03-22 23:07:42 +00:00
};
});
2010-03-23 04:29:57 +00:00
function ngClass(selector) {
return function(expression, element){
var existing = element[0].className + ' ';
return function(element){
2010-03-26 05:03:11 +00:00
this.$onEval(function(){
2010-03-23 04:29:57 +00:00
if (selector(this.$index)) {
2010-07-23 19:01:59 +00:00
var value = this.$eval(expression);
2010-03-23 04:29:57 +00:00
if (isArray(value)) value = value.join(' ');
2010-03-31 20:57:25 +00:00
element[0].className = trim(existing + value);
2010-03-23 04:29:57 +00:00
}
2010-03-26 05:03:11 +00:00
}, element);
2010-03-23 04:29:57 +00:00
};
};
}
angularDirective("ng:class", ngClass(function(){return true;}));
angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
2010-03-22 20:58:04 +00:00
angularDirective("ng:show", function(expression, element){
2010-03-23 04:29:57 +00:00
return function(element){
2010-03-26 05:03:11 +00:00
this.$onEval(function(){
element.css($display, toBoolean(this.$eval(expression)) ? '' : $none);
2010-03-26 05:03:11 +00:00
}, element);
2010-03-23 04:29:57 +00:00
};
});
angularDirective("ng:hide", function(expression, element){
2010-03-23 04:29:57 +00:00
return function(element){
2010-03-26 05:03:11 +00:00
this.$onEval(function(){
element.css($display, toBoolean(this.$eval(expression)) ? $none : '');
2010-03-26 05:03:11 +00:00
}, element);
2010-03-23 04:29:57 +00:00
};
});
2010-03-16 17:30:26 +00:00
angularDirective("ng:style", function(expression, element){
2010-03-23 21:57:11 +00:00
return function(element){
2010-07-15 00:48:09 +00:00
var resetStyle = getStyle(element);
2010-03-26 05:03:11 +00:00
this.$onEval(function(){
var style = this.$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);
2010-03-26 05:03:11 +00:00
}, element);
2010-03-23 21:57:11 +00:00
};
});
2010-03-16 20:50:47 +00:00