Angular: Call method using string value for name - angularjs

So I have Googled this quite allot today, and I'm afraid the answers I got, did not satisfy the problem I'm facing. I'm trying to call a method within a controller (controller as syntax) by only using a string value.
I tried window['someFunctionName'](); as well as this['someFunctionName'](); and vm['someFunctionName'](), but non of them seems to work. I'm guessing it's because the function I'm calling is not in a global space, but rather local to the controller - example:
/* #ngInject */
function dashboardController(logger, _, moment) {
var vm = this;
vm.foo = foo
function getSomeData() {
// do something....
}
function foo() {
window['getSomeData']();
this['getSomeData']();
vm['getSomeData']();
}
}
It feels so trivial ... I could do this soooooo easily in C#, but struggling with something that feels so silly!
Any advice?
Thanks!

If you don't want your function to be accessible from your template, you can still create a kind of container for your functions :
var functionContainer = {};
functionContainer.getSomeData = function() {
// Do some stuff
}
Going further, your "get data" function should be in a service that you will inject into your controller.
angular.module('myApp').factory('getDataService', function(){
return {
'getSomeData': getSomeData,
'getOtherData': getOtherData
};
function getSomeData() {
// do some stuff
}
function getOtherData() {
// do other stuff
}
});
/* #ngInject */
function dashboardController(logger, _, moment, getDataService) {
var vm = this;
vm.foo = function () {
getDataService['getSomeData']();
};
}

Can you try
/* #ngInject */
function dashboardController(logger, _, moment) {
var vm = this;
vm.foo = foo
vm.getSomeData = function() {
// do something....
}
function foo() {
vm.getSomeData();
}
}

Just simply call getSomeData(); from foo.
Like:
function foo() {
getSomeData();
}
See this fiddle

Related

AngularJS Factory Data Not Updating In Controller

I'm not sure what I've done wrong this is how I've setup other factories to share data as far as I can see, but even though the array should be passed by reference and I don't overwrite the array so the reference isn't lost the template never loads with the data I can see coming down from the find all request. Can anyone see what it is that I have/haven't done to get this factory to work and shared between controllers?
I originally set this up not using a service so I know the data returned from the request does work with the view, and if you look at my stubs the data is there (and it looks the same), but the view never updates. If this is something other than a glaring error that I've missed and don't seem to understand I'd appreciate a quick comment as well since I assumed it was the fact objects/arrays are passed by reference that makes this all work.
Contacts Controller
// Stripped down for brevity...
(function () {
'use strict';
function ContactsController(ContactsFactory,
ContactsResource) {
var vm = this;
vm.contacts = ContactsFactory.get();
vm.findAll = function () {
ContactsResource
.all()
.then(function (response) {
ContactsFactory.add(response.contacts); // <-- an array of objects for example: [{id: 5, username: "Richard39", email: "Sanford.Marilyne#Gibson.org",…},…]
});
};
vm.remove = function (contact, index) {
ContactsResource
.remove()
.then(function (response) {
ContactsFactory.remove(index);
});
};
function init() {
vm.findAll();
}
init();
}
ContactsController.$inject = [
'ContactsFactory',
'ContactsResource'
];
angular
.module('app')
.controller('ContactsController', ContactsController);
})();
Contacts Service
// Stripped down for brevity...
(function () {
'use strict';
function ContactsFactory() {
var contacts = [];
function get() {
return contacts;
}
function add(contacts) {
console.log(contacts) // <-- array of objects
angular.forEach(contacts, function(contact) {
console.log(contact) // <-- object
contacts.list.push(contact);
});
console.log(contacts) // <-- objects pushed onto array [Object, Object, Object, Object, Object,…]
// But no update in view, which worked with same resource
// request, prior to switching to factory to share between
// controllers
}
function remove(index) {
contacts.splice(index, 1);
}
// ---
// PUBLIC API.
// ---
return {
get: get,
add: add,
remove: remove
};
}
ContactsFactory.$inject = [];
angular
.module('app')
.factory('ContactsFactory', ContactsFactory);
})();
UPDATE
Using #Rob's suggest makes this work, and I noticed if I added service.contacts as the return value to the getter it also works, and I verified contacts could be removed. So I'm still interested in knowing why this works versus just using an array for contacts like the examples above. Are arrays not passed by reference as I originally thought, and it's only objects? I'd just like to put a name to what I did wrong so it's memorable.
(function () {
'use strict';
function ContactsFactory() {
var service = {
contacts: []
};
function get() {
return service.contacts;
}
function add(contacts) {
angular.forEach(contacts, function(contact) {
service.contacts.push(contact);
});
console.log(contacts);
}
function remove(index) {
contacts.splice(index, 1);
}
return {
contacts: service.contacts, // <-- Rob's suggestion
get: get, // <-- this also works now as well
add: add,
remove: remove
};
}
ContactsFactory.$inject = [];
angular
.module('app')
.factory('ContactsFactory', ContactsFactory);
})();

AngularJS — Explicitly returning a value from a controller

I ran into the codeblock below the other day, and I am trying to figure out why exactly the author may have chosen to return init(); rather than doing something like
$scope.init = function() {...};
And calling it with an ng-click, or something or other.
Here's the code:
myApp.controller("PlaylistCtrl", [
"$scope", "tracksService", function($scope, tracksService) {
var init;
init = function() {
$scope.addTrack("Jay-Z - 99 Problems");
return $scope.tracks = $scope.getTracks();
};
$scope.getTracks = function() {
return tracksService.tracks();
};
$scope.addTrack = function(trackTitle) {
if (trackTitle !== "") {
return tracksService.addTrack(trackTitle);
}
};
return init();
}
]);
So the question as stands is: what is the purpose of returning a value from a controller? What additional functionality does it exploit? Is this causing init() to be ran once the controller is initiated—if not, what is the specific purpose here? Any help is appreciated. Thank you.
There's no specific meaning to it and as is removing the return wouldn't change anything.
If you look at the other functions, it seems that that developper has the habit to return from every function, even when not necessary.
That is probably just a habit. In somes instances it could help a developper make sure that is the end of the initializations block, putting the functions declarations below, such as in this small example:
// Initialization
var foo = 'bar';
doFoo();
doBar();
return; // <- end of declarative block
// Methods
function doFoo() { ... }
function doBar() { ... }

AngularJS Controller instance method

I have an angular js controller that I am writing in a way so other controllers can inherit from it. Instead of defining the function as a single one in the angular's controller function I am writing it in the following way:
function SomeCtrl($scope)
{
this.some_field = "1234";
this.scope.externalMethod = angular.bind(this, this.externalMethod);
this.scope.anotherMethod = angular.bind(this, this.anotherMethod);
return this;
}
SomeCtrl.prototype.externalMethod = function()
{
//Do something
//....(Don't worry about this method, its just to highlight that I can call this method from $scope.externalMethod)
}
//These are the methods of interest
SomeCtrl.prototype.instanceMethodOne = function()
{
//Do something....
}
SomeCtrl.prototype.anotherMethod = function()
{
this.instanceMethodOne(); //---> Problem here!
//Carry on
//....
}
angular.module('some_module') //Previously defined
.controller('SomeCtrl', SomeCtrl)
So the problem that I am having now is to have a reference (this) inside the method "anotherMethod", which calls a class instance method "instanceMethodOne". This resolves to null as the self reference "this" is not resolved at that point. Is there any way to reference an object inside its instance method like in this case?
The problem has been resolved.
UPDATE AND FIXED:
Okay so I have looked into this problem. I should actually update the problem. It actually looks more like this:
function SomeCtrl($scope)
{
this.some_field = "1234";
this.scope.externalMethod = angular.bind(this, this.externalMethod);
this.scope.anotherMethod = angular.bind(this, this.anotherMethod);
return this;
}
SomeCtrl.prototype.externalMethod = function()
{
//Do something
//....(Don't worry about this method, its just to highlight that I can call this method from $scope.externalMethod)
}
//These are the methods of interest
SomeCtrl.prototype.instanceMethodOne = function()
{
//Do something....
}
SomeCtrl.prototype.anotherMethod = function()
{
var aCallBack = function()
{
//Some stuff...
this.instanceMethodOne(); //---> Problem here!
}
//Carry on
//....
}
angular.module('some_module') //Previously defined
.controller('SomeCtrl', SomeCtrl)
So the fix was to first create a reference to the object's method. That way I could call it within the callback function. The "this" in the callback function was referring to that function itself. Sorry for false alarm...This is fixed now! Thanks #sma for looking into it.

Backbone.js RequireJS Mixin

I have create a mixin component using requirejs like so:
define(function() {
'use strict';
var MyClass = function () {
console.log('Hello World');
};
return {
doSomething: function () {
var m = new MyClass();
return m;
}
}
});
My question is that is it better to write this mixin where the return is a function that can be included without accessing the returned object?
So the above would be rewritten like:
define(function() {
'use strict';
var MyClass = function () {
console.log('Hello World');
};
return function doSomething() {
var m = new MyClass();
return m;
}
});
And then when you include this module, you can then just call doSomething() versus having to include the module and access the method like myModule.doSomething().
I know that either way is acceptable, but just wanted to get an opinion on implementation.
I think it depends on agreed conventions in your project as well as on code structure. The second example probably seems more SOLID to me since every module or class should be just single responsibility. Retrurning object notation could tend to more functions on the module returning level. Just a thought though.

Running AngularJS initialization code when view is loaded

When I load a view, I'd like to run some initialization code in its associated controller.
To do so, I've used the ng-init directive on the main element of my view:
<div ng-init="init()">
blah
</div>
and in the controller:
$scope.init = function () {
if ($routeParams.Id) {
//get an existing object
});
} else {
//create a new object
}
$scope.isSaving = false;
}
First question: is this the right way to do it?
Next thing, I have a problem with the sequence of events taking place. In the view I have a 'save' button, which uses the ng-disabled directive as such:
<button ng-click="save()" ng-disabled="isClean()">Save</button>
the isClean() function is defined in the controller:
$scope.isClean = function () {
return $scope.hasChanges() && !$scope.isSaving;
}
As you can see, it uses the $scope.isSaving flag, which was initialized in the init() function.
PROBLEM: when the view is loaded, the isClean function is called before the init() function, hence the flag isSaving is undefined. What can I do to prevent that?
When your view loads, so does its associated controller. Instead of using ng-init, simply call your init() method in your controller:
$scope.init = function () {
if ($routeParams.Id) {
//get an existing object
} else {
//create a new object
}
$scope.isSaving = false;
}
...
$scope.init();
Since your controller runs before ng-init, this also solves your second issue.
Fiddle
As John David Five mentioned, you might not want to attach this to $scope in order to make this method private.
var init = function () {
// do something
}
...
init();
See jsFiddle
If you want to wait for certain data to be preset, either move that data request to a resolve or add a watcher to that collection or object and call your init method when your data meets your init criteria. I usually remove the watcher once my data requirements are met so the init function doesnt randomly re-run if the data your watching changes and meets your criteria to run your init method.
var init = function () {
// do something
}
...
var unwatch = scope.$watch('myCollecitonOrObject', function(newVal, oldVal){
if( newVal && newVal.length > 0) {
unwatch();
init();
}
});
Since AngularJS 1.5 we should use $onInit which is available on any AngularJS component. Taken from the component lifecycle documentation since v1.5 its the preferred way:
$onInit() - Called on each controller after all the controllers on an
element have been constructed and had their bindings initialized (and
before the pre & post linking functions for the directives on this
element). This is a good place to put initialization code for your
controller.
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope) {
//default state
$scope.name = '';
//all your init controller goodness in here
this.$onInit = function () {
$scope.name = 'Superhero';
}
});
Fiddle Demo
An advanced example of using component lifecycle:
The component lifecycle gives us the ability to handle component stuff in a good way. It allows us to create events for e.g. "init", "change" or "destroy" of an component. In that way we are able to manage stuff which is depending on the lifecycle of an component. This little example shows to register & unregister an $rootScope event listener $on. By knowing, that an event $on bound on $rootScope will not be unbound when the controller loses its reference in the view or getting destroyed we need to destroy a $rootScope.$on listener manually.
A good place to put that stuff is $onDestroy lifecycle function of an component:
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function ($scope, $rootScope) {
var registerScope = null;
this.$onInit = function () {
//register rootScope event
registerScope = $rootScope.$on('someEvent', function(event) {
console.log("fired");
});
}
this.$onDestroy = function () {
//unregister rootScope event by calling the return function
registerScope();
}
});
Fiddle demo
Or you can just initialize inline in the controller. If you use an init function internal to the controller, it doesn't need to be defined in the scope. In fact, it can be self executing:
function MyCtrl($scope) {
$scope.isSaving = false;
(function() { // init
if (true) { // $routeParams.Id) {
//get an existing object
} else {
//create a new object
}
})()
$scope.isClean = function () {
return $scope.hasChanges() && !$scope.isSaving;
}
$scope.hasChanges = function() { return false }
}
I use the following template in my projects:
angular.module("AppName.moduleName", [])
/**
* #ngdoc controller
* #name AppName.moduleName:ControllerNameController
* #description Describe what the controller is responsible for.
**/
.controller("ControllerNameController", function (dependencies) {
/* type */ $scope.modelName = null;
/* type */ $scope.modelName.modelProperty1 = null;
/* type */ $scope.modelName.modelPropertyX = null;
/* type */ var privateVariable1 = null;
/* type */ var privateVariableX = null;
(function init() {
// load data, init scope, etc.
})();
$scope.modelName.publicFunction1 = function () /* -> type */ {
// ...
};
$scope.modelName.publicFunctionX = function () /* -> type */ {
// ...
};
function privateFunction1() /* -> type */ {
// ...
}
function privateFunctionX() /* -> type */ {
// ...
}
});

Resources