function delegateService(methodNames) { if (methodNames.indexOf('$getByHandle') > -1) { throw new Error("Method '$getByHandle' is implicitly added to each delegate service. Do not list it as a method."); } function trueFn() { return true; } return ['$log', function($log) { var delegate = this; var instances = this._instances = []; this._registerInstance = function(instance, handle, filterFn) { instance.$$delegateHandle = handle; instance.$$filterFn = filterFn || trueFn; instances.push(instance); return function deregister() { var index = instances.indexOf(instance); if (index !== -1) { instances.splice(index, 1); } }; }; this.$getByHandle = function(handle) { if (!handle) { return delegate; } return new InstanceForHandle(handle); }; /* * Creates a new object that will have all the methodNames given, * and call them on the given the controller instance matching given * handle. * The reason we don't just let $getByHandle return the controller instance * itself is that the controller instance might not exist yet. * * We want people to be able to do * `var instance = $ionicScrollDelegate.$getByHandle('foo')` on controller * instantiation, but on controller instantiation a child directive * may not have been compiled yet! * * So this is our way of solving this problem: we create an object * that will only try to fetch the controller with given handle * once the methods are actually called. */ function InstanceForHandle(handle) { this.handle = handle; } methodNames.forEach(function(methodName) { InstanceForHandle.prototype[methodName] = function() { var handle = this.handle; var args = arguments; var matchingInstancesFound = 0; var finalResult; var result; //This logic is repeated below; we could factor some of it out to a function //but don't because it lets this method be more performant (one loop versus 2) instances.forEach(function(instance) { if (instance.$$delegateHandle === handle && instance.$$filterFn(instance)) { matchingInstancesFound++; result = instance[methodName].apply(instance, args); //Only return the value from the first call if (matchingInstancesFound === 1) { finalResult = result; } } }); if (!matchingInstancesFound) { return $log.warn( 'Delegate for handle "' + this.handle + '" could not find a ' + 'corresponding element with delegate-handle="' + this.handle + '"! ' + methodName + '() was not called!\n' + 'Possible cause: If you are calling ' + methodName + '() immediately, and ' + 'your element with delegate-handle="' + this.handle + '" is a child of your ' + 'controller, then your element may not be compiled yet. Put a $timeout ' + 'around your call to ' + methodName + '() and try again.' ); } return finalResult; }; delegate[methodName] = function() { var args = arguments; var matchingInstancesFound = 0; var finalResult; var result; //This logic is repeated above instances.forEach(function(instance, index) { if (instance.$$filterFn(instance)) { matchingInstancesFound++; result = instance[methodName].apply(instance, args); //Only return the value from the first call if (matchingInstancesFound === 1) { finalResult = result; } } }); return finalResult; }; }); }]; }