removing support for 'eager-published' services

This commit is contained in:
Igor Minar 2011-01-04 00:46:25 -08:00
parent d0270d9256
commit 3ea5941f0e
9 changed files with 83 additions and 61 deletions

View file

@ -1,12 +1,39 @@
# <angular/> 0.9.9 time-shift (in-progress) # # <angular/> 0.9.9 time-shift (in-progress) #
### Performance
- $location and $cookies services are now lazily initialized to avoid the polling overhead when
not needed.
### Breaking changes ### Breaking changes
- Many of the services are now lazy created instead of 'eager-publish'. You can get these - Support for 'eager-published' services was removed. This change was done to make explicit
services back into the root scope by adding ng:init="$location = $inject('$location')" dependency declaration always required in order to allow making relatively expensive services
in your view. The services effected are: lazily initialized (e.g. $cookie, $location), as well as remove 'magic' and reduce unnecessary
scope namespace pollution.
Complete list of affected services:
- $location - $location
- $route - $route
- $cookies - $cookies
- $window
- $document
- $exceptionHandler
- $invalidWidgets
To temporarily preserve the 'eager-published' status for these services, you may use `ng:init`
(e.g. `ng:init="$location = $inject('$location'), ...`) in the view or more correctly create
a service like this:
angular.service('published-svc-shim', function() {
this.$location = this.$inject('$location');
this.$route = this.$inject('$route');
this.$cookies = this.$inject('$cookies');
this.$window = this.$inject('$window');
this.$document = this.$inject('$document');
this.$exceptionHandler = this.$inject('$exceptionHandler');
this.$invalidWidgets = this.$inject('$invalidWidgets');
}, {$creation: 'eager'});
# <angular/> 0.9.8 astral-projection (2010-12-23) # # <angular/> 0.9.8 astral-projection (2010-12-23) #

View file

@ -58,9 +58,9 @@ function createInjector(providerScope, providers, cache) {
creation = provider.$creation; creation = provider.$creation;
if (creation == 'eager') { if (creation == 'eager') {
inject(name); inject(name);
} } else {
if (creation == 'eager-published') { if (isDefined(creation))
setter(value, name, inject(name)); throw "Unknown $creation value '" + creation + "' for service " + name;
} }
}); });
} else { } else {

View file

@ -1,8 +1,7 @@
var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/, var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/, HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/,
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21}, DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21},
EAGER = 'eager', EAGER = 'eager';
EAGER_PUBLISHED = EAGER + '-published';
function angularServiceInject(name, fn, inject, eager) { function angularServiceInject(name, fn, inject, eager) {
angularService(name, fn, {$inject:inject, $creation:eager}); angularService(name, fn, {$inject:inject, $creation:eager});
@ -26,7 +25,7 @@ function angularServiceInject(name, fn, inject, eager) {
<input ng:init="greeting='Hello World!'" type="text" name="greeting" /> <input ng:init="greeting='Hello World!'" type="text" name="greeting" />
<button ng:click="$window.alert(greeting)">ALERT</button> <button ng:click="$window.alert(greeting)">ALERT</button>
*/ */
angularServiceInject("$window", bind(window, identity, window), [], EAGER_PUBLISHED); angularServiceInject("$window", bind(window, identity, window), [], EAGER);
/** /**
* @workInProgress * @workInProgress
@ -39,7 +38,7 @@ angularServiceInject("$window", bind(window, identity, window), [], EAGER_PUBLIS
*/ */
angularServiceInject("$document", function(window){ angularServiceInject("$document", function(window){
return jqLite(window.document); return jqLite(window.document);
}, ['$window'], EAGER_PUBLISHED); }, ['$window'], EAGER);
/** /**
* @workInProgress * @workInProgress
@ -382,7 +381,7 @@ angularServiceInject("$log", function($window){
return logFn; return logFn;
} }
} }
}, ['$window'], EAGER_PUBLISHED); }, ['$window'], EAGER);
/** /**
* @workInProgress * @workInProgress
@ -406,7 +405,7 @@ angularServiceInject('$exceptionHandler', function($log){
return function(e) { return function(e) {
$log.error(e); $log.error(e);
}; };
}, ['$log'], EAGER_PUBLISHED); }, ['$log'], EAGER);
/** /**
* @workInProgress * @workInProgress
@ -531,7 +530,7 @@ angularServiceInject("$invalidWidgets", function(){
} }
return invalidWidgets; return invalidWidgets;
}, [], EAGER_PUBLISHED); }, [], EAGER);

View file

@ -382,10 +382,12 @@ extend(angularValidator, {
cache.current = input; cache.current = input;
var inputState = cache.inputs[input]; var inputState = cache.inputs[input],
$invalidWidgets = scope.$inject('$invalidWidgets');
if (!inputState) { if (!inputState) {
cache.inputs[input] = inputState = { inFlight: true }; cache.inputs[input] = inputState = { inFlight: true };
scope.$invalidWidgets.markInvalid(scope.$element); $invalidWidgets.markInvalid(scope.$element);
element.addClass('ng-input-indicator-wait'); element.addClass('ng-input-indicator-wait');
asynchronousFn(input, function(error, data) { asynchronousFn(input, function(error, data) {
inputState.response = data; inputState.response = data;
@ -393,14 +395,14 @@ extend(angularValidator, {
inputState.inFlight = false; inputState.inFlight = false;
if (cache.current == input) { if (cache.current == input) {
element.removeClass('ng-input-indicator-wait'); element.removeClass('ng-input-indicator-wait');
scope.$invalidWidgets.markValid(element); $invalidWidgets.markValid(element);
} }
element.data($$validate)(); element.data($$validate)();
scope.$root.$eval(); scope.$root.$eval();
}); });
} else if (inputState.inFlight) { } else if (inputState.inFlight) {
// request in flight, mark widget invalid, but don't show it to user // request in flight, mark widget invalid, but don't show it to user
scope.$invalidWidgets.markInvalid(scope.$element); $invalidWidgets.markInvalid(scope.$element);
} else { } else {
(updateFn||noop)(inputState.response); (updateFn||noop)(inputState.response);
} }

View file

@ -271,7 +271,7 @@ function valueAccessor(scope, element) {
formatterName = element.attr('ng:format') || NOOP, formatterName = element.attr('ng:format') || NOOP,
formatter = angularFormatter(formatterName), formatter = angularFormatter(formatterName),
format, parse, lastError, required, format, parse, lastError, required,
invalidWidgets = scope.$invalidWidgets || {markValid:noop, markInvalid:noop}; invalidWidgets = scope.$inject('$invalidWidgets') || {markValid:noop, markInvalid:noop};
if (!validator) throw "Validator named '" + validatorName + "' not found."; if (!validator) throw "Validator named '" + validatorName + "' not found.";
if (!formatter) throw "Formatter named '" + formatterName + "' not found."; if (!formatter) throw "Formatter named '" + formatterName + "' not found.";
format = formatter.format; format = formatter.format;

View file

@ -515,38 +515,38 @@ BinderTest.prototype.testValidateForm = function() {
var items = [{}, {}]; var items = [{}, {}];
c.scope.$set("items", items); c.scope.$set("items", items);
c.scope.$eval(); c.scope.$eval();
assertEquals(3, c.scope.$get("$invalidWidgets.length")); assertEquals(3, c.scope.$inject('$invalidWidgets').length);
c.scope.$set('name', ''); c.scope.$set('name', '');
c.scope.$eval(); c.scope.$eval();
assertEquals(3, c.scope.$get("$invalidWidgets.length")); assertEquals(3, c.scope.$inject('$invalidWidgets').length);
c.scope.$set('name', ' '); c.scope.$set('name', ' ');
c.scope.$eval(); c.scope.$eval();
assertEquals(3, c.scope.$get("$invalidWidgets.length")); assertEquals(3, c.scope.$inject('$invalidWidgets').length);
c.scope.$set('name', 'abc'); c.scope.$set('name', 'abc');
c.scope.$eval(); c.scope.$eval();
assertEquals(2, c.scope.$get("$invalidWidgets.length")); assertEquals(2, c.scope.$inject('$invalidWidgets').length);
items[0].name = 'abc'; items[0].name = 'abc';
c.scope.$eval(); c.scope.$eval();
assertEquals(1, c.scope.$get("$invalidWidgets.length")); assertEquals(1, c.scope.$inject('$invalidWidgets').length);
items[1].name = 'abc'; items[1].name = 'abc';
c.scope.$eval(); c.scope.$eval();
assertEquals(0, c.scope.$get("$invalidWidgets.length")); assertEquals(0, c.scope.$inject('$invalidWidgets').length);
}; };
BinderTest.prototype.testValidateOnlyVisibleItems = function(){ BinderTest.prototype.testValidateOnlyVisibleItems = 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>', undefined, jqLite(document.body));
c.scope.$set("show", true); c.scope.$set("show", true);
c.scope.$eval(); c.scope.$eval();
assertEquals(2, c.scope.$get("$invalidWidgets.length")); assertEquals(2, c.scope.$inject('$invalidWidgets').length);
c.scope.$set("show", false); c.scope.$set("show", false);
c.scope.$eval(); c.scope.$eval();
assertEquals(1, c.scope.$invalidWidgets.visible()); assertEquals(1, c.scope.$inject('$invalidWidgets').visible());
}; };
BinderTest.prototype.testDeleteAttributeIfEvaluatesFalse = function() { BinderTest.prototype.testDeleteAttributeIfEvaluatesFalse = function() {

View file

@ -53,19 +53,9 @@ describe('injector', function(){
it('should autostart eager services', function(){ it('should autostart eager services', function(){
var log = ''; var log = '';
providers('eager', function(){log += 'eager;';}, {$creation: 'eager'}); providers('eager', function(){log += 'eager;'; return 'foo'}, {$creation: 'eager'});
inject(); inject();
expect(log).toEqual('eager;'); expect(log).toEqual('eager;');
expect(scope.eager).not.toBeDefined(); expect(inject('eager')).toBe('foo');
}); });
});
it('should return a list of published objects', function(){
var log = '';
providers('eager', function(){log += 'eager;'; return 'pub'; }, {$creation: 'eager-published'});
inject();
expect(log).toEqual('eager;');
expect(scope.eager).toEqual('pub');
});
});

View file

@ -128,7 +128,7 @@ describe('Validator:asynchronous', function(){
it("should not make second request to same value", function(){ it("should not make second request to same value", function(){
asynchronous.call(self, "kai", function(v,f){value=v; fn=f;}); asynchronous.call(self, "kai", function(v,f){value=v; fn=f;});
expect(value).toEqual('kai'); expect(value).toEqual('kai');
expect(self.$invalidWidgets[0]).toEqual(self.$element); expect(self.$inject('$invalidWidgets')[0]).toEqual(self.$element);
var spy = jasmine.createSpy(); var spy = jasmine.createSpy();
asynchronous.call(self, "kai", spy); asynchronous.call(self, "kai", spy);

View file

@ -24,7 +24,7 @@ describe("service", function(){
it("should inject $window", function(){ it("should inject $window", function(){
expect(scope.$window).toEqual(window); expect(scope.$inject('$window')).toEqual(window);
}); });
xit('should add stylesheets', function(){ xit('should add stylesheets', function(){
@ -44,11 +44,12 @@ describe("service", function(){
function warn(){ logger+= 'warn;'; } function warn(){ logger+= 'warn;'; }
function info(){ logger+= 'info;'; } function info(){ logger+= 'info;'; }
function error(){ logger+= 'error;'; } function error(){ logger+= 'error;'; }
var scope = createScope({}, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}, $document:[{cookie:''}]}); var scope = createScope({}, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}, $document:[{cookie:''}]}),
scope.$log.log(); $log = scope.$inject('$log');
scope.$log.warn(); $log.log();
scope.$log.info(); $log.warn();
scope.$log.error(); $log.info();
$log.error();
expect(logger).toEqual('log;warn;info;error;'); expect(logger).toEqual('log;warn;info;error;');
}); });
@ -56,19 +57,21 @@ describe("service", function(){
var logger = ""; var logger = "";
function log(){ logger+= 'log;'; } function log(){ logger+= 'log;'; }
var scope = createScope({}, angularService, {$window: {console:{log:log}}, $document:[{cookie:''}]}); var scope = createScope({}, angularService, {$window: {console:{log:log}}, $document:[{cookie:''}]});
scope.$log.log(); var $log = scope.$inject('$log');
scope.$log.warn(); $log.log();
scope.$log.info(); $log.warn();
scope.$log.error(); $log.info();
$log.error();
expect(logger).toEqual('log;log;log;log;'); expect(logger).toEqual('log;log;log;log;');
}); });
it('should use noop if no console', function(){ it('should use noop if no console', function(){
var scope = createScope({}, angularService, {$window: {}, $document:[{cookie:''}]}); var scope = createScope({}, angularService, {$window: {}, $document:[{cookie:''}]}),
scope.$log.log(); $log = scope.$inject('$log');
scope.$log.warn(); $log.log();
scope.$log.info(); $log.warn();
scope.$log.error(); $log.info();
$log.error();
}); });
describe('Error', function(){ describe('Error', function(){
@ -112,7 +115,7 @@ describe("service", function(){
it('should log errors', function(){ it('should log errors', function(){
var error = ''; var error = '';
$log.error = function(m) { error += m; }; $log.error = function(m) { error += m; };
scope.$exceptionHandler('myError'); scope.$inject('$exceptionHandler')('myError');
expect(error).toEqual('myError'); expect(error).toEqual('myError');
}); });
}); });
@ -263,25 +266,26 @@ describe("service", function(){
scope = compile('<input name="price" ng:required ng:validate="number"></input>'); scope = compile('<input name="price" ng:required ng:validate="number"></input>');
jqLite(document.body).append(scope.$element); jqLite(document.body).append(scope.$element);
scope.$init(); scope.$init();
expect(scope.$invalidWidgets.length).toEqual(1); var $invalidWidgets = scope.$inject('$invalidWidgets');
expect($invalidWidgets.length).toEqual(1);
scope.price = 123; scope.price = 123;
scope.$eval(); scope.$eval();
expect(scope.$invalidWidgets.length).toEqual(0); expect($invalidWidgets.length).toEqual(0);
scope.$element.remove(); scope.$element.remove();
scope.price = 'abc'; scope.price = 'abc';
scope.$eval(); scope.$eval();
expect(scope.$invalidWidgets.length).toEqual(0); expect($invalidWidgets.length).toEqual(0);
jqLite(document.body).append(scope.$element); jqLite(document.body).append(scope.$element);
scope.price = 'abcd'; //force revalidation, maybe this should be done automatically? scope.price = 'abcd'; //force revalidation, maybe this should be done automatically?
scope.$eval(); scope.$eval();
expect(scope.$invalidWidgets.length).toEqual(1); expect($invalidWidgets.length).toEqual(1);
jqLite(document.body).html(''); jqLite(document.body).html('');
scope.$eval(); scope.$eval();
expect(scope.$invalidWidgets.length).toEqual(0); expect($invalidWidgets.length).toEqual(0);
}); });
}); });