From 23b255a8b7481ff5c06004b3558c07f981c42276 Mon Sep 17 00:00:00 2001
From: Misko Hevery
Date: Mon, 7 Feb 2011 15:29:56 -0800
Subject: remove $init on scope from applying compilation template
Closes #40
---
CHANGELOG.md | 8 ++++
src/Angular.js | 6 +--
src/Compiler.js | 20 ++++------
src/widgets.js | 19 +++++-----
test/BinderSpec.js | 31 ++++++++--------
test/CompilerSpec.js | 5 +--
test/ScenarioSpec.js | 9 ++---
test/ValidatorsSpec.js | 5 +--
test/directivesSpec.js | 3 +-
test/markupSpec.js | 1 -
test/testabilityPatch.js | 1 +
test/widgetsSpec.js | 95 +++++++++++++++++++++++-------------------------
12 files changed, 95 insertions(+), 108 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de7b3f4f..979d2435 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,14 @@
# 0.9.12 thought-implanter (in-progress) #
+### Breaking changes
+- Removed the $init() method after the compilation. The old way of compiling the DOM element was
+ angular.compile(element).$init(); The $init was there to allow the users to do any work to the
+ scope before the view would be bound. This is a left over from not having proper MVC. The new
+ 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.
+
# 0.9.11 snow-maker (2011-02-08) #
diff --git a/src/Angular.js b/src/Angular.js
index f9047d32..5bd5e547 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -800,10 +800,8 @@ function merge(src, dst) {
* Compiles a piece of HTML or DOM into a {@link angular.scope scope} object.
var scope1 = angular.compile(window.document);
- scope1.$init();
var scope2 = angular.compile('click me
');
- scope2.$init();
*
* @param {string|DOMElement} element Element to compile.
@@ -948,7 +946,7 @@ function toKeyValue(obj) {
(function(window, previousOnLoad){
window.onload = function(){
try { (previousOnLoad||angular.noop)(); } catch(e) {}
- angular.compile(window.document).$init();
+ angular.compile(window.document);
};
})(window, window.onload);
</script>
@@ -1002,8 +1000,6 @@ function angularInit(config){
$browser.addCss(config.base_url + config.css);
else if(msie<8)
$browser.addJs(config.base_url + config.ie_compat, config.ie_compat_id);
-
- scope.$init();
}
}
diff --git a/src/Compiler.js b/src/Compiler.js
index 804a9622..6aee40b8 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -13,7 +13,7 @@ function Template(priority) {
}
Template.prototype = {
- init: function(element, scope) {
+ attach: function(element, scope) {
var inits = {};
this.collectInits(element, inits, scope);
forEachSorted(inits, function(queue){
@@ -96,18 +96,14 @@ Compiler.prototype = {
template = this.templatize(element, index, 0) || new Template();
return function(element, parentScope){
element = jqLite(element);
- var scope = parentScope && parentScope.$eval ?
- parentScope : createScope(parentScope);
+ var scope = parentScope && parentScope.$eval
+ ? parentScope
+ : createScope(parentScope);
element.data($$scope, scope);
- return extend(scope, {
- $element:element,
- $init: function() {
- template.init(element, scope);
- scope.$eval();
- delete scope.$init;
- return scope;
- }
- });
+ template.attach(element, scope);
+ scope.$element = element;
+ scope.$eval();
+ return scope;
};
},
diff --git a/src/widgets.js b/src/widgets.js
index 7438254d..66c9ecc5 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -677,7 +677,6 @@ angularWidget('ng:include', function(element){
element.html(response);
childScope = useScope || createScope(scope);
compiler.compile(element)(element, childScope);
- childScope.$init();
scope.$eval(onloadExp);
});
} else {
@@ -795,7 +794,6 @@ var ngSwitch = angularWidget('ng:switch', function (element){
element.append(caseElement);
childScope.$tryEval(switchCase.change, element);
switchCase.template(caseElement, childScope);
- childScope.$init();
}
});
});
@@ -891,7 +889,7 @@ angularWidget('a', function() {
angularWidget("@ng:repeat", function(expression, element){
element.removeAttr('ng:repeat');
element.replaceWith(jqLite(""));
- var template = this.compile(element);
+ var linker = this.compile(element);
return function(reference){
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
lhs, rhs, valueIdent, keyIdent;
@@ -912,6 +910,7 @@ 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),
@@ -937,16 +936,17 @@ angularWidget("@ng:repeat", function(expression, element){
if (keyIdent) childScope[keyIdent] = key;
} else {
// grow children
- childScope = template(quickClone(element), createScope(currentScope));
+ childScope = createScope(currentScope);
childScope[valueIdent] = collection[key];
if (keyIdent) childScope[keyIdent] = key;
- lastElement.after(childScope.$element);
childScope.$index = index;
childScope.$position = index == 0 ?
- 'first' :
- (index == collectionLength - 1 ? 'last' : 'middle');
- childScope.$element.attr('ng:repeat-index', index);
- childScope.$init();
+ 'first' :
+ (index == collectionLength - 1 ? 'last' : 'middle');
+ cloneElement = quickClone(element);
+ lastElement.after(cloneElement);
+ cloneElement.attr('ng:repeat-index', index);
+ linker(cloneElement, childScope);
children.push(childScope);
}
childScope.$eval();
@@ -1069,7 +1069,6 @@ angularWidget('ng:view', function(element) {
$xhr('GET', src, function(code, response){
element.html(response);
compiler.compile(element)(element, childScope);
- childScope.$init();
});
} else {
element.html('');
diff --git a/test/BinderSpec.js b/test/BinderSpec.js
index 73650bd6..c3f90ad7 100644
--- a/test/BinderSpec.js
+++ b/test/BinderSpec.js
@@ -3,16 +3,18 @@ describe('Binder', function(){
beforeEach(function(){
var self = this;
- this.compile = function(html, initialScope, parent) {
+ this.compile = function(html, parent) {
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
if (self.element) dealoc(self.element);
- var element = self.element = jqLite(html);
+ var element;
+ if (parent) {
+ parent.html(html);
+ element = parent.children();
+ } else {
+ element = jqLite(html);
+ }
+ self.element = element;
var scope = compiler.compile(element)(element);
-
- if (parent) parent.append(element);
-
- extend(scope, initialScope);
- scope.$init();
return {node:element, scope:scope};
};
this.compileToHtml = function (content) {
@@ -27,8 +29,8 @@ describe('Binder', function(){
});
- it('ChangingTextfieldUpdatesModel', function(){
- var state = this.compile(' ', {model:{}});
+ it('text-field should default to value attribute', function(){
+ var state = this.compile(' ');
state.scope.$eval();
assertEquals('abc', state.scope.model.price);
});
@@ -443,8 +445,7 @@ describe('Binder', function(){
});
it('ActionOnAHrefThrowsError', function(){
- var model = {books:[]};
- var c = this.compile('Add Phone ', model);
+ var c = this.compile('Add Phone ');
c.scope.action = function(){
throw new Error('MyError');
};
@@ -517,9 +518,9 @@ describe('Binder', function(){
});
it('ValidateForm', function(){
- var c = this.compile('',
- undefined, jqLite(document.body));
+ var c = this.compile(' ' +
+ '
',
+ jqLite(document.body));
var items = [{}, {}];
c.scope.$set("items", items);
c.scope.$eval();
@@ -547,7 +548,7 @@ describe('Binder', function(){
});
it('ValidateOnlyVisibleItems', function(){
- var c = this.compile('
', undefined, jqLite(document.body));
+ var c = this.compile('
', jqLite(document.body));
c.scope.$set("show", true);
c.scope.$eval();
assertEquals(2, c.scope.$service('$invalidWidgets').length);
diff --git a/test/CompilerSpec.js b/test/CompilerSpec.js
index 291a5011..647cc366 100644
--- a/test/CompilerSpec.js
+++ b/test/CompilerSpec.js
@@ -28,7 +28,6 @@ describe('compiler', function(){
compile = function(html){
var e = jqLite("" + html + "
");
var scope = compiler.compile(e)(e);
- scope.$init();
return scope;
};
});
@@ -48,10 +47,8 @@ describe('compiler', function(){
};
};
var template = compiler.compile(e);
- scope = template(e);
- var init = scope.$init;
expect(log).toEqual("found");
- init();
+ scope = template(e);
expect(e.hasClass('ng-directive')).toEqual(true);
expect(log).toEqual("found:init");
});
diff --git a/test/ScenarioSpec.js b/test/ScenarioSpec.js
index 50b5e51c..cd1e3115 100644
--- a/test/ScenarioSpec.js
+++ b/test/ScenarioSpec.js
@@ -13,25 +13,24 @@ describe("ScenarioSpec: Compilation", function(){
it("should compile dom node and return scope", function(){
var node = jqLite('{{b=a+1}}
')[0];
scope = compile(node);
- scope.$init();
expect(scope.a).toEqual(1);
expect(scope.b).toEqual(2);
});
it("should compile jQuery node and return scope", function(){
- scope = compile(jqLite('{{a=123}}
')).$init();
+ scope = compile(jqLite('{{a=123}}
'));
expect(jqLite(scope.$element).text()).toEqual('123');
});
it("should compile text node and return scope", function(){
- scope = compile('{{a=123}}
').$init();
+ scope = compile('{{a=123}}
');
expect(jqLite(scope.$element).text()).toEqual('123');
});
});
describe('scope', function(){
- it("should have set, get, eval, $init, updateView methods", function(){
- scope = compile('{{a}}
').$init();
+ it("should have $set, $get, $eval, $updateView methods", function(){
+ scope = compile('{{a}}
');
scope.$eval("$invalidWidgets.push({})");
expect(scope.$set("a", 2)).toEqual(2);
expect(scope.$get("a")).toEqual(2);
diff --git a/test/ValidatorsSpec.js b/test/ValidatorsSpec.js
index 60d20418..65b93133 100644
--- a/test/ValidatorsSpec.js
+++ b/test/ValidatorsSpec.js
@@ -9,7 +9,7 @@ describe('ValidatorTest', function(){
};
var scope = compile(' ');
scope.name = 'misko';
- scope.$init();
+ scope.$eval();
assertEquals('misko', validator.first);
assertEquals('hevery', validator.last);
expect(validator._this.$id).toEqual(scope.$id);
@@ -99,7 +99,6 @@ describe('ValidatorTest', function(){
jqLite(document.body).append(self.$element);
self.$element.data('$validate', noop);
self.$root = self;
- self.$init();
});
afterEach(function(){
@@ -110,7 +109,6 @@ describe('ValidatorTest', function(){
var value, fn;
var scope = compile(' ');
jqLite(document.body).append(scope.$element);
- scope.$init();
var input = scope.$element;
scope.asyncFn = function(v,f){
value=v; fn=f;
@@ -155,7 +153,6 @@ describe('ValidatorTest', function(){
scope.asyncFn = jasmine.createSpy();
scope.updateFn = jasmine.createSpy();
scope.name = 'misko';
- scope.$init();
scope.$eval();
expect(scope.asyncFn).wasCalledWith('misko', scope.asyncFn.mostRecentCall.args[1]);
assertTrue(scope.$element.hasClass('ng-input-indicator-wait'));
diff --git a/test/directivesSpec.js b/test/directivesSpec.js
index caf0bc15..8e5a10ee 100644
--- a/test/directivesSpec.js
+++ b/test/directivesSpec.js
@@ -7,7 +7,6 @@ describe("directive", function(){
compile = function(html) {
element = jqLite(html);
model = compiler.compile(element)(element);
- model.$init();
return model;
};
});
@@ -116,7 +115,7 @@ describe("directive", function(){
expect(element.attr('src')).toEqual('http://localhost/mysrc');
expect(element.attr('alt')).toEqual('myalt');
});
-
+
it('should not pretty print JSON in attributes', function(){
var scope = compile(' ');
expect(element.attr('alt')).toEqual('{"a":1}');
diff --git a/test/markupSpec.js b/test/markupSpec.js
index 16efba55..2462f19e 100644
--- a/test/markupSpec.js
+++ b/test/markupSpec.js
@@ -9,7 +9,6 @@ describe("markups", function(){
compile = function(html) {
element = jqLite(html);
scope = compiler.compile(element)(element);
- scope.$init();
};
});
diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js
index 32272a4d..dc6acf8b 100644
--- a/test/testabilityPatch.js
+++ b/test/testabilityPatch.js
@@ -28,6 +28,7 @@ beforeEach(function(){
compileCache = {};
// reset to jQuery or default to us.
bindJQuery();
+ jqLite(document.body).html('');
this.addMatchers({
toBeInvalid: function(){
var element = jqLite(this.actual);
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index 9aa3b95d..606491a9 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -5,12 +5,14 @@ describe("widget", function(){
scope = null;
element = null;
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
- compile = function(html, before, parent) {
- element = jqLite(html);
+ compile = function(html, parent) {
+ if (parent) {
+ parent.html(html);
+ element = parent.children();
+ } else {
+ element = jqLite(html);
+ }
scope = compiler.compile(element)(element);
- (before||noop).apply(scope);
- if (parent) parent.append(element);
- scope.$init();
return scope;
};
});
@@ -77,9 +79,7 @@ describe("widget", function(){
});
it("should come up blank if null", function(){
- compile(' ', function(){
- scope.age = null;
- });
+ compile(' ');
expect(scope.age).toBeNull();
expect(scope.$element[0].value).toEqual('');
});
@@ -137,9 +137,7 @@ describe("widget", function(){
describe("checkbox", function(){
it("should format booleans", function(){
- compile(' ', function(){
- scope.name = false;
- });
+ compile(' ');
expect(scope.name).toEqual(false);
expect(scope.$element[0].checked).toEqual(false);
});
@@ -184,7 +182,7 @@ describe("widget", function(){
describe("ng:validate", function(){
it("should process ng:validate", function(){
compile(' ',
- undefined, jqLite(document.body));
+ jqLite(document.body));
expect(element.hasClass('ng-validation-error')).toBeTruthy();
expect(element.attr('ng-validation-error')).toEqual('Not a number');
@@ -238,7 +236,7 @@ describe("widget", function(){
});
it("should process ng:required", function(){
- compile(' ', undefined, jqLite(document.body));
+ compile(' ', jqLite(document.body));
expect(element.hasClass('ng-validation-error')).toBeTruthy();
expect(element.attr('ng-validation-error')).toEqual('Required');
@@ -255,7 +253,7 @@ describe("widget", function(){
it('should allow conditions on ng:required', function() {
compile(' ',
- undefined, jqLite(document.body));
+ jqLite(document.body));
scope.$set('ineedz', false);
scope.$eval();
expect(element.hasClass('ng-validation-error')).toBeFalsy();
@@ -336,25 +334,21 @@ describe("widget", function(){
});
it('should honor model over html checked keyword after', function(){
- compile('');
expect(scope.choose).toEqual('C');
});
it('should honor model over html checked keyword before', function(){
- compile('');
expect(scope.choose).toEqual('A');
});
@@ -396,7 +390,7 @@ describe("widget", function(){
'' +
'{{opt}} ' +
' ',
- undefined, jqLite(document.body));
+ jqLite(document.body));
scope.selection = 1;
scope.options = ['one', 'two'];
scope.$eval();
@@ -492,14 +486,13 @@ describe("widget", function(){
});
it('should allow binding to objects through index', function(){
- compile('' +
- 'A ' +
- 'B ' +
- 'C ' +
- ' ',
- function(){
- scope.list = [{name:'A'}, {name:'B'}, {name:'C'}];
- });
+ compile('' +
+ '' +
+ 'A ' +
+ 'B ' +
+ 'C ' +
+ ' ' +
+ '
');
scope.$eval();
expect(scope.selection).toEqual([{name:'A'}, {name:'B'}]);
});
@@ -517,14 +510,13 @@ describe("widget", function(){
});
it('should be contain the selected object', function(){
- compile('' +
- 'A ' +
- 'B ' +
- 'C ' +
- ' ',
- function(){
- scope.list = [{name:'A'}, {name:'B'}, {name:'C'}];
- });
+ compile('' +
+ '' +
+ 'A ' +
+ 'B ' +
+ 'C ' +
+ ' ' +
+ '
');
scope.$eval();
expect(scope.selection).toEqual([{name:'B'}]);
});
@@ -604,7 +596,7 @@ describe("widget", function(){
it('should call change on switch', function(){
var scope = angular.compile('{{name}}
');
scope.url = 'a';
- scope.$init();
+ scope.$eval();
expect(scope.name).toEqual(undefined);
expect(scope.$element.text()).toEqual('works');
dealoc(scope);
@@ -619,7 +611,7 @@ describe("widget", function(){
scope.childScope.name = 'misko';
scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{name}}'};
- scope.$init();
+ scope.$eval();
scope.$service('$browser').defer.flush();
expect(element.text()).toEqual('misko');
dealoc(scope);
@@ -632,7 +624,7 @@ describe("widget", function(){
scope.childScope.name = 'igor';
scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{name}}'};
- scope.$init();
+ scope.$eval();
scope.$service('$browser').defer.flush();
expect(element.text()).toEqual('igor');
@@ -649,7 +641,7 @@ describe("widget", function(){
var scope = angular.compile(element);
scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'{{c=c+1}}'};
- scope.$init();
+ scope.$eval();
scope.$service('$browser').defer.flush();
// this one should really be just '1', but due to lack of real events things are not working
@@ -666,7 +658,7 @@ describe("widget", function(){
scope.url = 'myUrl';
scope.$service('$xhr.cache').data.myUrl = {value:'my partial'};
- scope.$init();
+ scope.$eval();
scope.$service('$browser').defer.flush();
expect(element.text()).toEqual('my partial');
expect(scope.loaded).toBe(true);
@@ -800,7 +792,6 @@ describe("widget", function(){
beforeEach(function() {
rootScope = angular.compile(' ');
- rootScope.$init();
$route = rootScope.$service('$route');
$location = rootScope.$service('$location');
$browser = rootScope.$service('$browser');
@@ -867,15 +858,19 @@ describe("widget", function(){
});
it('should be possible to nest ng:view in ng:include', function() {
- dealoc(rootScope);
- rootScope = angular.compile('include:
');
- $browser = rootScope.$service('$browser');
+ var myApp = angular.scope();
+ var $browser = myApp.$service('$browser');
$browser.xhr.expectGET('includePartial.html').respond('view: ');
$browser.setUrl('http://server/#/foo');
- $route = rootScope.$service('$route');
+ var $route = myApp.$service('$route');
$route.when('/foo', {controller: angular.noop, template: 'viewPartial.html'});
- rootScope.$init();
+
+ dealoc(rootScope); // we are about to override it.
+ rootScope = angular.compile(
+ '' +
+ 'include: ' +
+ '
', myApp);
$browser.xhr.expectGET('viewPartial.html').respond('content');
$browser.xhr.flush();
--
cgit v1.2.3