Executing second controller's "this" in first controller through angular service - javascript

I have two controllers and a service, i am executing first controller "self/this" in the second controller. I wanted to know is this an antipattern or are there any chances of memory leak?
SelfService -- this service stores "this" of controllers in selfContainer Object.
The setSelf method takes two parameters -- name of the this/self controller and this/self itself. -- it stores them as key value pair in selfContainer Object.
The executeWithSelf method also takes two parameters -- name of the this/self and a callback. the callback is executed with the this/self as parameter.
angular.module('app')
.factory('SelfService', function () {
var selfContainer = {};
function setSelf (selfname , self) {
selfContainer[selfname] = self;
}
function executeWithSelf (selfname, callback) {
callback(selfContainer[selfname]);
}
return {
setSelf: setSelf,
executeWithSelf: executeWithSelf
};
});
FirstController
angular.module('app').controller('FirstController', function(QueryService){
var self = this;
self.counter = 0;
QueryService.setSelf('FirstController', self);
})
Second Controller
angular.module('app').controller('SecondController', function(QueryService){
var self = this;
//execute QueryService.executeWithSelf on any event and only after first controller has loaded
QueryService.executeWithSelf('FirstController', function(firstcontrollerself){
firstcontrollerself.counter++;
});
})

Related

Angular calling a parent method from an extended controller

I been spinning on this for days with no luck. I have BaseController with an init method among other things. I then wish to extend this controller and call the parents 'init' method from within the childs 'init' method.
The general answer for this is to call $scope.$parent.init($settings_object). However this is returning Error: $scope.$parent.init is not a function.
Generally the extended controller works fine being able to access the perents function and settings without issues. Just this example calling the same parent method from the child fails.
BaseController
(function( ){
var mainApp = angular.module("MyAppModule");
mainApp.controller('BaseController',function($scope, $rootScope,GLOBAL_CONFIG, ajaxRESTful,sharedValues, messageDisplay) { //base contorller
var bvm = this; //base vm
this.init = function($settings_object){
var keys = Object.keys($settings_object);
for(i=0;i<keys.length;i++){
bvm[keys[i]] = $settings_object[keys[i]];
}
}
//code remved to keep simple
});
})();
Extended controller
(function( ){
var app = angular.module('MyAppModule');
app.controller('RoleEdit', function($scope, $rootScope,$controller) {
angular.merge(this, $controller('BaseController', {$scope: $scope}));
var vm = this;
vm.newRoleFormData = [];
vm.role_id = null;
vm.mode = 'create';
vm.role = null;
vm.init = function ($object) {
console.log(vm);
console.log($scope.$parent);
$scope.$parent.init($object);
if(vm.role_id != null){
vm.loadInRole( );
}
};
}) //end contoller
})();//end of app
Why doesn't this work?
Is there a better way to do this?
The controller object (this) and the $scope object aren't directly related. There is no automatic wiring between them. $scope.$parent doesn't return a controller, it returns the parent scope. And since you registered your parent method in this.init instead of the usual $scope.init, you can't expect to find it using scopes.
You may circumvent this in a great number of ways, but as others have suggested, if you have functionality that is shared by many controllers, try to put it in a service instead. Maybe your BaseContoller itself should be a service.
You should do:
var parentInit = this.init.bind(this);
this.init = function ($object) {
parentInit($object);
if(vm.role_id != null){
vm.loadInRole( );
}
}

How can i pass scope in template in angular js

I have one BaseController with common functions which my all other controllers inherit .
The controller is like this
function BaseController () {
this.defaultFilters = {};
this.doStuff = function ($scope) {
$scope.myobj.value = 1;
this.otherfunction();
};
I inherit that in my controller like this
BaseController.call($scope);
Now in my do stuff function i need to pass $scope because myobj is only visible there.
Now i want to know that how can i pass that in my template because i want to call that function when some click on some button
ng-click="doStuff(scope)"
Everything that you associate with your controller's scope, so you just associate your scope with some variable and i guess that will do the job.
Something like this :
app.controller(function($scope) {
$scope.scope = $scope;
});
But if you go by some standard approach, i suggest moving these common functions inside some service, injecting this service into each controller and using it in the views.
Something like this :
app.service("constantService", function() {
this.data = {}; // This will represent your common data.
this.commonFunction = function() {
};
});
app.controller(function() {
$scope.constantService = constantService;
// You can now use $scope.constantService.data as your reference for data, and then can copy it to some local $scope variable whenever required.
});
ng-click="constantService.commonFunction()"

http.jsonp and callbacks in javascript

I am working on a calculator that will consider AWS instance costs. I am pulling the data from a .js file on amazon and I would like to read it into an object but i keep getting an error "Uncaught ReferenceError: callback is not defined" .. here is my .js file.
(function() {
var app = angular.module('formExample', []);
var ExampleController = function($scope, $http) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
$scope.GetAws();
};
$scope.reset = function() {
$scope.user = "";
};
function callback(data) {
$scope.aws = data;
}
$scope.GetAws = function() {
var url = "http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js?callback=callback";
$http.jsonp(url);
};
$scope.reset();
};
app.controller('ExampleController', ['$scope', '$http', ExampleController]);
}());
It is weird that the aws link you are using supports jsonp but it does not take custom callback function name. (Atleast you can look up to find out if the query string they are looking for is callback or not). angular handles it when we provide callback=JSON_CALLBACK it gets translated to angular.callbacks_x which are exposed globally temporarily by angular to handle the request and resolve the promise accordingly. But for this the endpoint must take the callback argument and wrap the response in the same string and function invocation. However this endpoint does not seem to consider it and even without any callback it automatically wraps into default callback function invocation. So you would need to inject $window (Correct DI way) object and set callback function to it and ?callback=callback is irrelevant.
var ExampleController = function($scope, $http, $window) {
$scope.master = {};
//....
$window.callback = function(data) {
$scope.aws = data;
}
$scope.GetAws = function() {
var url = "http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js?callback=callback";
$http.jsonp(url);
};
$scope.reset();
};
app.controller('ExampleController', ['$scope', '$http', '$window', ExampleController]);
Plnkr
It is because the AWS script is looking to call a function called "callback" on the global scope (outside of Angular). Since your function is within the scope of another (IIFE) function, it cannot be accessed.
What I've done in a case like this is simply put the function in the global scope.
In cases where an application requires some API to have loaded before Angular can do it's magic and has a callback similar to your situation, I have done the following, manually bootstrapping Angular:
index.html
<script src="http://www.example.com/api?callback=myCallbackFunction"></script>
app.js
// callback function, in global (window) scope
function myCallbackFunction() {
// manually bootstrap Angular
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
}
// your IIFE
(function() {
})();
Notice callback should be set in window scope.
So,one solution is like:
$scope.reset = function() {
$scope.user = "";
};
window.callback = function(data) {
$scope.aws = data;
}
$scope.GetAws = function() {
var url = "http://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js?callback=callback";
$http.jsonp(url);
};

Returning function from AngularJS factory

I'm trying to understand what the purpose is of the return part of this AngularJS factory method means?
return {
getMessages: getMessages
};
What happens if we added a new method to this factory called getAnotherMessage(), would we need to update this return segment?
myModule.factory('HelloWorld', function($q, $timeout) {
var getMessages = function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(['Hello', 'world!']);
}, 2000);
return deferred.promise;
};
return {
getMessages: getMessages
};
});
factory is a provider constructor:
factory(fn) - registers a service factory function, fn, that will be
wrapped in a service provider object, whose $get property will contain
the given factory function.
Thus when the factory is first loaded by Angular it executes the function that's passed in and stores whatever is returned as the provider.
In other words, the following is run once, and only once- during bootstrapping:
var getMessages = function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(['Hello', 'world!']);
}, 2000);
return deferred.promise;
};
return {
getMessages: getMessages
};
The above gets a reference to the getMessage function and attaches it to the property getMessages inside the returned singleton object.
When the provider is then injected into your code, that returned object is what is passed in giving you access to the HelloWorld.getMessages function (and any other properties in the returned object).
So, yes, if you want to associate another property, such as a function, with the provider (that the factory constructs) you need to include it as a property of the returned singleton object:
return {
getAnotherMessage: function() { ... },
getMessages: getMessages
};
You can also declare an empty object first and add functions into the object
and finally return the object.
myModule.factory('HelloWorld', function($q, $timeout) {
var myobject = {};
myobject.getMessages = function() { ... };
myobject.getAnotherMessages = function() { ... };
return myobject;
});

accessing object properties with object method

I am trying to build an application called myApp which has the property regularNameErrors and the Method populateJSON which uses an AJAX call to get a JSON object which is then added to the property declared as one of the arguments to the method.
The function to get the data definitely works because the alert at position 3 gives the correct list of keys. however the alert at 1 returns undefined and the alert at 2 blank.
When I replace the line destination = data with myApp.regularNameErrors = data again it works so I assume that I have misunderstood how to pass object properties to object methods. However I want to use the method populateJSON several times and need to know the correct way to pass it properties.
var myApp = {
init: function () {
myApp.populateJSON(myApp.regularNameErrors, 'data/spelling.json').done(function () {
alert(myApp.regularNameErrors['kingscross']); //1
});
},
regularNameErrors: {}, //Object of spelling mistake:proper key:value pairs
populateJSON: function (destination, source) {
var def = $.Deferred();
$.getJSON(source, function (data) {
destination = data;
alert(Object.keys(myApp.regularNameErrors)); //2
alert(Object.keys(data)); //3
def.resolve();
});
return def.promise();
},
};
With the ability to use the deferred properties of the returned getJSON object the functions can be reduced to a few lines such that it is not necessary to create a new function called populateJSON
var myApp = {
init: function () {
$.getJSON('data/spelling.json').done(function(data) {
myApp.regularNameErrors = data;
});
},
regularNameErrors: {},
};
destination is no "pointer" to the property that you passed into the function, but a variable holding the value which the property evaluated to. When you assign to destination, you just write to the local variable of your populateJSON function.
If you want to be able to declare the destination, you would need to pass a base object (or always use myApp) and a property name:
var myApp = {
init: function () {
myApp.populateJSON('regularNameErrors', 'data/spelling.json', function() {
alert(Object.keys(myApp.regularNameErrors)); //2
alert(myApp.regularNameErrors['kingscross']); //1
});
},
regularNameErrors: {}, //Object of spelling mistake:proper key:value pairs
populateJSON: function (destinationName, source, callback) {
$.getJSON(source, function (data) {
myApp[destinationName] = data;
alert(Object.keys(data)); //3
callback();
});
}
};
However, I see you are using the promise pattern. A promise should not be used as a simple notifier ("now the data has arrived somewhere"), but it should represent the data itself ("now, here's the data") - resolve it with the data instead of storing the data in a global variable/property and resolving with nothing. Actually, the $.ajax does already return such a promise so you don't have to do much for it:
var myApp = {
init: function () {
myApp.regularNameErrors = $.getJSON('data/spelling.json');
myApp.regularNameErrors.done(function(data) {
alert(Object.keys(data)); //3
alert(data['kingscross']); //1
});
}
};

Categories

Resources