aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--docs/angular.element.ngdoc4
-rw-r--r--src/Angular.js20
-rw-r--r--src/Compiler.js15
-rw-r--r--src/jqLite.js9
-rw-r--r--src/widgets.js29
-rw-r--r--test/AngularSpec.js8
-rw-r--r--test/BinderSpec.js2
-rw-r--r--test/CompilerSpec.js4
-rw-r--r--test/jqLiteSpec.js8
10 files changed, 57 insertions, 44 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 579dd61b..57665b9a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,7 @@
recommended way to deal with initializing scope is to put it in the root constructor controller.
To migrate simply remove the call to $init() and move any code you had before $init() to the
root controller.
-- Change API angular.compile(..) to angular.compile(element)([scope], [element/true])
+- Change API angular.compile(..) to angular.compile(element)([scope], [cloneAttachFn])
<a name="0.9.11"><a/>
diff --git a/docs/angular.element.ngdoc b/docs/angular.element.ngdoc
index 83680d1f..2ce007fd 100644
--- a/docs/angular.element.ngdoc
+++ b/docs/angular.element.ngdoc
@@ -44,10 +44,6 @@ raw DOM references.
version:
- `scope()` - retrieves the current angular scope of the element.
-- `cloneNode()` - Clones the current node, ensuring identical structure. This is important since
- the `clone()` method implemented by jQuery under some circumstances changes the DOM
- structure, which then prevents proper application of compiled template to the cloned node.
- __Always use `cloneNode()` when cloning previously compiled templates.__
@param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
@returns {Object} jQuery object.
diff --git a/src/Angular.js b/src/Angular.js
index 87be29a7..6e5786ec 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -803,17 +803,18 @@ function merge(src, dst) {
</pre>
*
* @param {string|DOMElement} element Element or HTML to compile into a template function.
- * @returns {function([scope][, element])} a template function which is used to bind element
+ * @returns {function([scope][, cloneAttachFn])} a template function which is used to bind element
* and scope. Where:
*
* * `scope` - {@link angular.scope scope} A scope to bind to. If none specified, then a new
* root scope is created.
- * * `element` - {@link angular.element element} Element to use as the template. If none
- * specified then reuse the element from `angular.compile(element)`. If `true`
- * then clone the `angular.compile(element)`. The element must be either the same
- * element as `angular.compile(element)` or an identical clone to
- * `angular.compile(element)`. Using an element with differnt structure will cause
- * unpredictable behavior.
+ * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
+ * `template` and call the `cloneAttachFn` allowing the caller to attach the
+ * clonned elements to the DOM at the approriate place. The `cloneAttachFn` is
+ * called as: <br/> `cloneAttachFn(clonedElement, scope)`:
+ *
+ * * `clonedElement` - is a clone of the originale `element` passed into the compiler.
+ * * `scope` - is the current scope with which the linking function is working with.
*
* Calling the template function returns object: `{scope:?, view:?}`, where:
*
@@ -1006,7 +1007,7 @@ function toKeyValue(obj) {
function angularInit(config){
if (config.autobind) {
// TODO default to the source of angular.js
- var scope = compile(window.document)(null, createScope({'$config':config})),
+ var scope = compile(window.document)(createScope({'$config':config})).scope,
$browser = scope.$service('$browser');
if (config.css)
@@ -1048,8 +1049,7 @@ function bindJQuery(){
if (jQuery) {
jqLite = jQuery;
extend(jQuery.fn, {
- scope: JQLitePrototype.scope,
- cloneNode: JQLitePrototype.cloneNode
+ scope: JQLitePrototype.scope
});
} else {
jqLite = jqLiteWrap;
diff --git a/src/Compiler.js b/src/Compiler.js
index 77c83846..78d7a2b0 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -93,14 +93,17 @@ Compiler.prototype = {
}
}
template = this.templatize(templateElement, index, 0) || new Template();
- return function(scope, element){
- scope = scope || createScope();
- element = element === true
- ? templateElement.cloneNode()
- : (element ? jqLite(element) : templateElement);
+ return function(scope, cloneConnectFn){
+ // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
+ // and sometimes changes the structure of the DOM.
+ var element = cloneConnectFn
+ ? JQLitePrototype.clone.call(templateElement) // IMPORTAN!!!
+ : templateElement;
+ scope = scope || createScope();
element.data($$scope, scope);
- template.attach(element, scope);
scope.$element = element;
+ (cloneConnectFn||noop)(element, scope);
+ template.attach(element, scope);
scope.$eval();
return {scope:scope, view:element};
};
diff --git a/src/jqLite.js b/src/jqLite.js
index 8a507212..c4a10a0b 100644
--- a/src/jqLite.js
+++ b/src/jqLite.js
@@ -72,7 +72,7 @@ function JQLite(element) {
}
}
-function JQLiteCloneNode(element) {
+function JQLiteClone(element) {
return element.cloneNode(true);
}
@@ -370,12 +370,15 @@ forEach({
return element.parentNode || null;
},
+ next: function(element) {
+ return element.nextSibling;
+ },
+
find: function(element, selector) {
return element.getElementsByTagName(selector);
},
- clone: JQLiteCloneNode,
- cloneNode: JQLiteCloneNode
+ clone: JQLiteClone
}, function(fn, name){
/**
* chaining functions
diff --git a/src/widgets.js b/src/widgets.js
index ce877c71..d58e7789 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -790,10 +790,10 @@ var ngSwitch = angularWidget('ng:switch', function (element){
forEach(cases, function(switchCase){
if (!found && switchCase.when(childScope, value)) {
found = true;
- var caseElement = switchCase.element.cloneNode();
- element.append(caseElement);
childScope.$tryEval(switchCase.change, element);
- switchCase.template(childScope, caseElement);
+ switchCase.template(childScope, function(caseElement){
+ element.append(caseElement);
+ });
}
});
});
@@ -886,11 +886,11 @@ angularWidget('a', function() {
</doc:scenario>
</doc:example>
*/
-angularWidget("@ng:repeat", function(expression, element){
+angularWidget('@ng:repeat', function(expression, element){
element.removeAttr('ng:repeat');
- element.replaceWith(jqLite("<!-- ng:repeat: " + expression + " --!>"));
+ element.replaceWith(jqLite('<!-- ng:repeat: ' + expression + ' --!>'));
var linker = this.compile(element);
- return function(reference){
+ return function(iterStartElement){
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
lhs, rhs, valueIdent, keyIdent;
if (! match) {
@@ -910,10 +910,9 @@ angularWidget("@ng:repeat", function(expression, element){
var children = [], currentScope = this;
this.$onEval(function(){
var index = 0,
- cloneElement,
childCount = children.length,
- lastElement = reference,
- collection = this.$tryEval(rhs, reference),
+ lastIterElement = iterStartElement,
+ collection = this.$tryEval(rhs, iterStartElement),
is_array = isArray(collection),
collectionLength = 0,
childScope,
@@ -934,6 +933,7 @@ angularWidget("@ng:repeat", function(expression, element){
childScope = children[index];
childScope[valueIdent] = collection[key];
if (keyIdent) childScope[keyIdent] = key;
+ lastIterElement = childScope.$element;
} else {
// grow children
childScope = createScope(currentScope);
@@ -943,13 +943,14 @@ angularWidget("@ng:repeat", function(expression, element){
childScope.$position = index == 0
? 'first'
: (index == collectionLength - 1 ? 'last' : 'middle');
- lastElement.after(cloneElement = element.cloneNode());
- cloneElement.attr('ng:repeat-index', index);
- linker(childScope, cloneElement);
children.push(childScope);
+ linker(childScope, function(clone){
+ clone.attr('ng:repeat-index', index);
+ lastIterElement.after(clone);
+ lastIterElement = clone;
+ });
}
childScope.$eval();
- lastElement = childScope.$element;
index ++;
}
}
@@ -957,7 +958,7 @@ angularWidget("@ng:repeat", function(expression, element){
while(children.length > index) {
children.pop().$element.remove();
}
- }, reference);
+ }, iterStartElement);
};
});
diff --git a/test/AngularSpec.js b/test/AngularSpec.js
index 8ff0631d..8753d887 100644
--- a/test/AngularSpec.js
+++ b/test/AngularSpec.js
@@ -369,8 +369,10 @@ describe('angular', function(){
var scope = angular.scope();
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
var templateFn = angular.compile(template);
- var templateClone = template.cloneNode();
- mvc = templateFn(scope, templateClone);
+ var templateClone = template.clone();
+ mvc = templateFn(scope, function(clone){
+ templateClone = clone;
+ });
expect(template.text()).toEqual('');
expect(mvc.view.text()).toEqual('hello world');
expect(mvc.view).toEqual(templateClone);
@@ -380,7 +382,7 @@ describe('angular', function(){
it('should link to cloned node and create scope', function(){
var scope = angular.scope();
var template = jqLite('<div>{{greeting = "hello world"}}</div>');
- mvc = angular.compile(template)(scope, true);
+ mvc = angular.compile(template)(scope, noop);
expect(template.text()).toEqual('');
expect(mvc.view.text()).toEqual('hello world');
expect(mvc.scope.greeting).toEqual('hello world');
diff --git a/test/BinderSpec.js b/test/BinderSpec.js
index a1b9be14..ce2a9d3a 100644
--- a/test/BinderSpec.js
+++ b/test/BinderSpec.js
@@ -417,7 +417,7 @@ describe('Binder', function(){
});
it('BindClassEvenOdd', function(){
- var x = this.compile('<div><div ng:repeat="i in [0,1]" ng:class-even="\'e\'" ng:class-odd="\'o\'"/></div>');
+ var x = this.compile('<div><div ng:repeat="i in [0,1]" ng:class-even="\'e\'" ng:class-odd="\'o\'"></div></div>');
x.scope.$eval();
var d1 = jqLite(x.view[0].childNodes[1]);
var d2 = jqLite(x.view[0].childNodes[2]);
diff --git a/test/CompilerSpec.js b/test/CompilerSpec.js
index 1a93ac78..f1e1c607 100644
--- a/test/CompilerSpec.js
+++ b/test/CompilerSpec.js
@@ -47,7 +47,7 @@ describe('compiler', function(){
};
var template = compiler.compile(e);
expect(log).toEqual("found");
- scope = template(angular.scope(), e).scope;
+ scope = template(angular.scope()).scope;
expect(e.hasClass('ng-directive')).toEqual(true);
expect(log).toEqual("found:init");
});
@@ -84,7 +84,7 @@ describe('compiler', function(){
var template = this.compile(element);
return function(marker) {
this.$onEval(function() {
- marker.after(template(angular.scope(), true).view);
+ marker.after(template(angular.scope(), noop).view);
});
};
};
diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js
index c5e92c0f..e42e9f14 100644
--- a/test/jqLiteSpec.js
+++ b/test/jqLiteSpec.js
@@ -300,6 +300,14 @@ describe('jqLite', function(){
expect(element.parent().length).toEqual(0);
});
});
+ describe('next', function(){
+ it('should return next sibling', function(){
+ var element = jqLite('<div><b>b</b><i>i</i></div>');
+ var b = element.find('b');
+ var i = element.find('i');
+ expect(b.next()).toJqEqual([i]);
+ });
+ });
describe('find', function(){
it('should find child by name', function(){
var root = jqLite('<div><div>text</div></div>');