From 1dd7675133beb771b81f4677cb9dceccb65f3fcf Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Mon, 24 Mar 2014 17:29:30 -0600 Subject: [PATCH] delegates: allow multiple delegates of same handle Addresses #877 --- js/ext/angular/src/service/delegateService.js | 44 ++++++++++++------- .../test/service/delegateService.unit.js | 40 +++++++++++++---- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/js/ext/angular/src/service/delegateService.js b/js/ext/angular/src/service/delegateService.js index 740ebc0702..663ad17db4 100644 --- a/js/ext/angular/src/service/delegateService.js +++ b/js/ext/angular/src/service/delegateService.js @@ -3,13 +3,18 @@ function delegateService(methodNames) { return ['$log', function($log) { var delegate = this; - this._instances = {}; + var instances = this._instances = []; this._registerInstance = function(instance, handle) { handle || (handle = ionic.Utils.nextUid()); - delegate._instances[handle] = instance; + + instance.$$delegateHandle = handle; + instances.push(instance); return function deregister() { - delete delegate._instances[handle]; + var index = instances.indexOf(instance); + if (index !== -1) { + instances.splice(index, 1); + } }; }; @@ -41,26 +46,33 @@ function delegateService(methodNames) { } methodNames.forEach(function(methodName) { InstanceForHandle.prototype[methodName] = function() { - var instance = delegate._instances[this.handle]; - if (!instance) { - return $log.error( - 'Delegate with handle "'+this.handle+'" could not find a', + var handle = this.handle; + var instancesToUse = instances.filter(function(instance) { + return instance.$$delegateHandle === handle; + }); + if (!instancesToUse.length) { + return $log.warn( + 'Delegate for handle "'+this.handle+'" could not find a', 'corresponding element with delegate-handle="'+this.handle+'"!', methodName, 'was not called!'); } - return instance[methodName].apply(instance, arguments); + return callMethod(instancesToUse, methodName, arguments); }; delegate[methodName] = function() { - var args = arguments; - var returnValue; - angular.forEach(delegate._instances, function(instance) { - var result = instance[methodName].apply(instance, args); - if (!angular.isDefined(returnValue)) { - returnValue = result; + return callMethod(instances, methodName, arguments); + }; + + function callMethod(instancesToUse, methodName, args) { + var finalResult; + var result; + instancesToUse.forEach(function(instance) { + result = instance[methodName].apply(instance, args); + if (!angular.isDefined(finalResult)) { + finalResult = result; } }); - return returnValue; - }; + return finalResult; + } }); }]; } diff --git a/js/ext/angular/test/service/delegateService.unit.js b/js/ext/angular/test/service/delegateService.unit.js index 510f81c9c0..fd03e8c507 100644 --- a/js/ext/angular/test/service/delegateService.unit.js +++ b/js/ext/angular/test/service/delegateService.unit.js @@ -2,14 +2,13 @@ describe('DelegateFactory', function() { function setup(methods) { var delegate; inject(function($log, $injector) { - $log.error = jasmine.createSpy('error'); delegate = $injector.instantiate(delegateService(methods || [])); }); return delegate; } it('should have properties', function() { - expect(setup()._instances).toEqual({}); + expect(setup()._instances).toEqual([]); expect(setup()._registerInstance).toEqual(jasmine.any(Function)); expect(setup().forHandle).toEqual(jasmine.any(Function)); }); @@ -18,9 +17,10 @@ describe('DelegateFactory', function() { var delegate = setup(); var instance = {}; var deregister = delegate._registerInstance(instance, 'handle'); - expect(delegate._instances['handle']).toBe(instance); + expect(instance.$$delegateHandle).toBe('handle'); + expect(delegate._instances[0]).toBe(instance); deregister(); - expect(delegate._instances['handle']).toBeUndefined(); + expect(delegate._instances.length).toBe(0); }); it('should allow reg & dereg of instance, and make its own handle', function() { @@ -31,9 +31,10 @@ describe('DelegateFactory', function() { var instance = {}; var deregister = delegate._registerInstance(instance); expect(ionic.Utils.nextUid).toHaveBeenCalled(); - expect(delegate._instances['uid']).toBe(instance); + expect(instance.$$delegateHandle).toBe('uid'); + expect(delegate._instances[0]).toBe(instance); deregister(); - expect(delegate._instances['uid']).toBeUndefined(); + expect(delegate._instances.length).toBe(0); }); it('should have given methodNames on root', function() { @@ -119,9 +120,12 @@ describe('DelegateFactory', function() { expect(delegate.forHandle('two').a).toEqual(jasmine.any(Function)); expect(delegate.forHandle('invalid').a).toEqual(jasmine.any(Function)); }); - it('should do nothing if calling for a non-added instance', function() { + it('should noop & warn if calling for a non-added instance', inject(function($log) { + spyOn($log, 'warn'); expect(delegate.forHandle('one').a()).toBeUndefined(); - }); + expect($log.warn).toHaveBeenCalled(); + })); + it('should call an added instance\'s method', function() { delegate._registerInstance(instance1, '1'); delegate._registerInstance(instance2, '2'); @@ -137,5 +141,25 @@ describe('DelegateFactory', function() { expect(instance1.a).not.toHaveBeenCalled(); expect(result).toBe('a2'); }); + + it('should call multiple instances with the same handle', function() { + var instance3 = { + a: jasmine.createSpy('a3') + }; + delegate._registerInstance(instance1, '1'); + delegate._registerInstance(instance2, '1'); + delegate._registerInstance(instance3, 'other'); + + var delegateInstance = delegate.forHandle('1'); + + expect(instance1.a).not.toHaveBeenCalled(); + expect(instance2.a).not.toHaveBeenCalled(); + expect(instance3.a).not.toHaveBeenCalled(); + var result = delegateInstance.a('1'); + expect(instance1.a).toHaveBeenCalledWith('1'); + expect(instance2.a).toHaveBeenCalledWith('1'); + expect(instance3.a).not.toHaveBeenCalled(); + expect(result).toBe('a1'); + }); }); });