remove $init on scope from applying compilation template

Closes #40
This commit is contained in:
Misko Hevery 2011-02-07 15:29:56 -08:00
parent e2154cbc0b
commit 23b255a8b7
12 changed files with 95 additions and 108 deletions

View file

@ -1,6 +1,14 @@
<a name="0.9.12"><a/>
# <angular/> 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.
<a name="0.9.11"><a/>
# <angular/> 0.9.11 snow-maker (2011-02-08) #

View file

@ -800,10 +800,8 @@ function merge(src, dst) {
* Compiles a piece of HTML or DOM into a {@link angular.scope scope} object.
<pre>
var scope1 = angular.compile(window.document);
scope1.$init();
var scope2 = angular.compile('<div ng:click="clicked = true">click me</div>');
scope2.$init();
</pre>
*
* @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);
&lt;/script&gt;
@ -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();
}
}

View file

@ -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;
};
},

View file

@ -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("<!-- ng:repeat: " + expression + " --!>"));
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('');

View file

@ -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('<input type="text" name="model.price" value="abc">', {model:{}});
it('text-field should default to value attribute', function(){
var state = this.compile('<input type="text" name="model.price" value="abc">');
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('<a ng:click="action()">Add Phone</a>', model);
var c = this.compile('<a ng:click="action()">Add Phone</a>');
c.scope.action = function(){
throw new Error('MyError');
};
@ -517,9 +518,9 @@ describe('Binder', function(){
});
it('ValidateForm', function(){
var c = this.compile('<div><input name="name" ng:required>' +
'<div ng:repeat="item in items"><input name="item.name" ng:required/></div></div>',
undefined, jqLite(document.body));
var c = this.compile('<div id="test"><input name="name" ng:required>' +
'<input ng:repeat="item in items" name="item.name" ng:required/></div>',
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('<div><input name="name" ng:required><input ng:show="show" name="name" ng:required></div>', undefined, jqLite(document.body));
var c = this.compile('<div><input name="name" ng:required><input ng:show="show" name="name" ng:required></div>', jqLite(document.body));
c.scope.$set("show", true);
c.scope.$eval();
assertEquals(2, c.scope.$service('$invalidWidgets').length);

View file

@ -28,7 +28,6 @@ describe('compiler', function(){
compile = function(html){
var e = jqLite("<div>" + html + "</div>");
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");
});

View file

@ -13,25 +13,24 @@ describe("ScenarioSpec: Compilation", function(){
it("should compile dom node and return scope", function(){
var node = jqLite('<div ng:init="a=1">{{b=a+1}}</div>')[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('<div>{{a=123}}</div>')).$init();
scope = compile(jqLite('<div>{{a=123}}</div>'));
expect(jqLite(scope.$element).text()).toEqual('123');
});
it("should compile text node and return scope", function(){
scope = compile('<div>{{a=123}}</div>').$init();
scope = compile('<div>{{a=123}}</div>');
expect(jqLite(scope.$element).text()).toEqual('123');
});
});
describe('scope', function(){
it("should have set, get, eval, $init, updateView methods", function(){
scope = compile('<div>{{a}}</div>').$init();
it("should have $set, $get, $eval, $updateView methods", function(){
scope = compile('<div>{{a}}</div>');
scope.$eval("$invalidWidgets.push({})");
expect(scope.$set("a", 2)).toEqual(2);
expect(scope.$get("a")).toEqual(2);

View file

@ -9,7 +9,7 @@ describe('ValidatorTest', function(){
};
var scope = compile('<input name="name" ng:validate="myValidator:\'hevery\'"/>');
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('<input type="text" name="name" ng:validate="asynchronous:asyncFn"/>');
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'));

View file

@ -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('<img alt="{{ {a:1} }}"/>');
expect(element.attr('alt')).toEqual('{"a":1}');

View file

@ -9,7 +9,6 @@ describe("markups", function(){
compile = function(html) {
element = jqLite(html);
scope = compiler.compile(element)(element);
scope.$init();
};
});

View file

@ -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);

View file

@ -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('<input type="text" name="age" ng:format="number"/>', function(){
scope.age = null;
});
compile('<input type="text" name="age" ng:format="number" ng:init="age=null"/>');
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('<input type="checkbox" name="name"/>', function(){
scope.name = false;
});
compile('<input type="checkbox" name="name" ng:init="name=false"/>');
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('<input type="text" name="price" value="abc" ng:validate="number"/>',
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('<input type="text" name="price" ng:required/>', undefined, jqLite(document.body));
compile('<input type="text" name="price" ng:required/>', 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('<input type="text" name="price" ng:required="ineedz"/>',
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('<div>' +
compile('<div ng:init="choose=\'C\'">' +
'<input type="radio" name="choose" value="A""/>' +
'<input type="radio" name="choose" value="B" checked/>' +
'<input type="radio" name="choose" value="C"/>' +
'</div>', function(){
this.choose = 'C';
});
'</div>');
expect(scope.choose).toEqual('C');
});
it('should honor model over html checked keyword before', function(){
compile('<div>' +
compile('<div ng:init="choose=\'A\'">' +
'<input type="radio" name="choose" value="A""/>' +
'<input type="radio" name="choose" value="B" checked/>' +
'<input type="radio" name="choose" value="C"/>' +
'</div>', function(){
this.choose = 'A';
});
'</div>');
expect(scope.choose).toEqual('A');
});
@ -396,7 +390,7 @@ describe("widget", function(){
'<select name="selection" ng:required>' +
'<option value="{{$index}}" ng:repeat="opt in options">{{opt}}</option>' +
'</select>',
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('<select name="selection" multiple ng:format="index:list">' +
'<option selected value="0">A</option>' +
'<option selected value="1">B</option>' +
'<option value="2">C</option>' +
'</select>',
function(){
scope.list = [{name:'A'}, {name:'B'}, {name:'C'}];
});
compile('<div ng:init="list = [{name:\'A\'}, {name:\'B\'}, {name:\'C\'}]">' +
'<select name="selection" multiple ng:format="index:list">' +
'<option selected value="0">A</option>' +
'<option selected value="1">B</option>' +
'<option value="2">C</option>' +
'</select>' +
'</div>');
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('<select name="selection" multiple ng:format="index:list">' +
'<option value="0">A</option>' +
'<option value="1" selected>B</option>' +
'<option value="2">C</option>' +
'</select>',
function(){
scope.list = [{name:'A'}, {name:'B'}, {name:'C'}];
});
compile('<div ng:init="list = [{name:\'A\'}, {name:\'B\'}, {name:\'C\'}]">' +
'<select name="selection" multiple ng:format="index:list">' +
'<option value="0">A</option>' +
'<option value="1" selected>B</option>' +
'<option value="2">C</option>' +
'</select>' +
'</div>');
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('<ng:switch on="url" change="name=\'works\'"><div ng:switch-when="a">{{name}}</div></ng:switch>');
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('<ng:view></ng:view>');
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('<div>include: <ng:include src="\'includePartial.html\'"></ng:include></div>');
$browser = rootScope.$service('$browser');
var myApp = angular.scope();
var $browser = myApp.$service('$browser');
$browser.xhr.expectGET('includePartial.html').respond('view: <ng:view></ng: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(
'<div>' +
'include: <ng:include src="\'includePartial.html\'">' +
'</ng:include></div>', myApp);
$browser.xhr.expectGET('viewPartial.html').respond('content');
$browser.xhr.flush();