Controller 'Init' Function Executed More Than Once - angularjs

This is my controller
//init
var init = function () {
$scope.getAlbumList();
};
$scope.getAlbumList = function () {
AlbumService.getAlbumList()
.then(function (data) {
$scope.albumList = data;
});
};
$scope.viewAlbum = function () {
AlbumService.getAlbum()
.then(function (data) {
$scope.album = data;
$location.path("/album");
});
};
init();
and this is my routeProvider
when('/albums', {
templateUrl: 'WebApp/albums.html',
controller: 'AlbumController'
}).
when('/album', {
templateUrl: 'WebApp/albumview.html',
controller: 'AlbumController'
}).
So the AlbumController handles both the albums and albumview pages.
When the controller is created, the init function is called which in turns calls the getAlbumList function.
The problem is that the controller is created again when the user clicks an album to go to its albumview page. This ends up executing the init function again which in turns causes an undesired second call to getAlbumList.
So basically, I need getAlbumList to be called when the controller is created for '/albums' but not '/albumview'.
How can I achieve this?
Obviously, I could solve this problem by creating a second controller for /albumview but this seems unnecessary and I'd rather have everything regarding albums in one controller.

You have 2 solutions:
in your controller test if location.path() == 'albums'
in the html page of albums ng-init="getAlbumList()"

Probably the quickest option is to check the path to see if you're in album or albums. Here is sample code of how to do that:
if ($location.url().toLowerCase() === '/albums') {
// call to getAlbumList here
}

It will happen if you have controller: 'AlbumController' in router and your html have ng-controller = "AlbumController". Use only one to avoid double initilization.

Related

$rootScope is not updated in SPA

My first controller:
angular.module('application')
.controller('FirstController',['$rootScope',function($rootScope) {
var data=[0,1,2];
$rootScope.items=data;
}]);
My second controller:
angular.module('application')
.controller('SecondController',['$rootScope',function($rootScope) {
$rootScope.items[0]=3;
console.log($rootScope.items); // [3,1,2]
}]);
When the second controller is running, its corresponding view is changed; however not the same happens when going back to the corresponding view of the first controller (both views are bound to $rootScope.items). Why that happens? I am using ui-router and FirstController has to do with the main page of the SPA and SecondController with another page. Moreover, by keeping track of $rootScope.items with:
<pre>
{{$root.items | json}}
</pre>
in both templates the second one is renewed to [3,1,2] and the first one remains [0,1,2].
Passing the same $scope between the two controllers isn't an ideal way of maintaining a single data model, and what you need to do is to establish a service module (or a factory) to manage the data for you, so that both controllers can talk to the factor for your data.
This is how you set up the factory
app.factory("Data",
function () {
var storage = [0,1,2]; // where your data is
return {
get: function () {
return storage;
},
set: function (toSet) {
storage = toSet;
return storage;
}
};
});
to let the controllers know where the data is, use
app.controller ("FirstController",
function ($scope, Data)
{
console.log(Data); // [0,1,2]
Data.set( [3,1,2]); // demoing change
}
same is for the second controller
app.controller ("FirstController",
function ($scope, Data)
{
console.log(Data); // [3,1,2]
}

AngularJS: How to initialize data through $http before executing other part of the code?

I have below code
$scope.init = function (){
console.log($scope.languageFilePath);
$http.get($scope.languageFilePath) //languageFilePath contain url
.then(function(res){
console.log('scueese');
$scope.translate = res.data;
console.log($scope.translate.SERVICE);
// $scope.eqipment = $scope.translate['COLOR'];
//console.log("Data String "+equip);
//$scope.eqipment="ghsh"
});
};
$scope.init();
Now what is my issue this method calling properly but before initializing $scope.translate its executing other part of code.
What changes i have to do in the code so first its initialize $scope.translate then only it call some other methods of file .
Note:- $http.get() method calling when this method invoked after that other methods are executing and code inside then executing after those method called.
If your problem is to execute the code after $scope.init() invocation, you could try to use the http.get promise, like this:
$scope.init = function (){
console.log($scope.languageFilePath);
return $http.get($scope.languageFilePath) //languageFilePath contain url
.then(function(res){
console.log('scueese');
$scope.translate = res.data;
console.log($scope.translate.SERVICE);
});
};
$scope.init().then(function(){
//this code will be executed after the http get...
});
EDIT 1
after receiving some more code from OP, you should modify your .js file as shown here: http://pastebin.com/mFUWSU0N
this way, ALL the code below init() will be executed after the $http.get completion.
may be you should out the wanted function in your init:
$scope.init = function (){
console.log($scope.languageFilePath);
return $http.get($scope.languageFilePath) //languageFilePath contain url
.then(function(res){
console.log('scueese');
$scope.translate = res.data;
console.log($scope.translate.SERVICE);
myFunc();
myFunc2();
});
};
What you also could do is make it a dependency of your controller.
Lets say for instance you are using the routeprovider to navigate to your page you could then do this:
myApp.config(['$routeProvider',function ($routeProvider) {
$routeProvider.when('../../goto/yourpage', {
templateUrl: '/yourpage.html',
controller: 'myController',
resolve: {
initializeFunction: function($http) {
// do some logic you want executed before anything else
return $http.get("blablabla").$promise;
});
}
}
});
}]);
then in your controller that is active on yourpage.html you can do:
myApp.controller('myController',function ($scope,initializeFunction) {
$scope.possibleReturnFromTheFunction = initializeFunction;
});

AngularJS proper way of refreshing data in Controller

I have a testing "hello" view showing "Hello {{username}}!" or "Hello anonymous!".
This view has its own controller and is accesible via url (configure by ui.router).
Then I have a UserModel with methods setUsername(newUsername) and getUsername().
There is also logging view with a controller that uses setUsername() method on logging in success and then navigates to "hello" view.
The code looks like this:
HelloController:
anguler.module('hello', ...
.config(function($stateProvider){
$stateProvider
.state('hello', {
url: '/hello',
views: {
'main#': {
controller: 'HelloController as helloController',
templateUrl: 'app/hello/hello-tmpl.html'
}
},
});
})
.controller('HelloController', function (UserModel) {
var helloController = this;
helloController.username = UserModel.getUsername();
})
There is also a "log out" button in the top bar. So in order to show the changes in "hello" view I added a list of function that UserModel would call when user state changes:
.service('UserModel', function() {
var model = this;
var username = '';
var onChangesFunctions = [];
function onChange() {
onChangesFunctions.forEach(function(f) {
f();
});
}
model.onChange = function(f) {
onChangesFunctions.push(f);
};
model.setUsername = function(newUsername) {
username = newUsername;
onChange();
};
model.clearUserData = function() {
username = '';
onChange();
};
and added a code in HelloController to add a listener to the UserModel.onChangesFunctions.
The problem with that approach is that HelloController is initialized many times (everytime that user navigates to it) and every time it is registering new function as the listener.
Is there any better way to refresh user data?
The problem of your approach is memory leaks. As you said when your controller is destroyed and the new one is created your function will still persist in the service and the controller which should have been killed is still most likely in the memory because of the function.
I don't clearly understand what your goal is; however what you can do is destroying the functions when the controller is destroyed:
.controller('HelloController', function (UserModel, $scope) {
var helloController = this;
helloController.username = UserModel.getUsername();
$scope.$on('$destroy', function() {
// either destroy all functions you add to the service queue
// or
// simply call the controller specific logic here, this will be called when your controller is destroyed
});
});
Another approach is listening on '$stateChangeStart' / '$stateChangeSuccess' event.
Regardless of the way you choose I would highly recommend to avoid binding services to the controller instance specific logic. This is a way to hell

Angular - How to show modal reject reason in table?

I have small problem to solve.
I have modal controller rejectIssueModalCtrl.js
(function () {
'use strict';
function rejectIssueModalCtrl($modalInstance, issue, $rootScope) {
var self = this;
self.cancel = function () {
$modalInstance.dismiss('cancel');
};
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
}
rejectIssueModalCtrl.$inject = ['$modalInstance', 'issue', '$rootScope'];
angular
.module('app')
.controller('rejectIssueModalCtrl', rejectIssueModalCtrl);
})();
When I click the button I can open this modal and write a reason. I want to show this reject reason in table in other controller.
Here's my code from other controller issueDetailsCtrl.js
$scope.reasonoo = $rootScope.reasono;
function rejectIssue() {
var rejectModal = $modal.open({
templateUrl: '/App/Issue/rejectIssueModal',
controller: 'rejectIssueModalCtrl',
controllerAs: 'rejectModal',
size: 'lg',
resolve: {
issue: self.issueData
}
});
rejectModal.result.then(function (reason) {
issueSvc
.rejectIssue(self.issueData.id, reason)
.then(function (issueStatus) {
changeIssueStatus(issueStatus.code);
getIssue();
}, requestFailed);
});
};
and html code
<div>
<span class="right" ng-bind="$root.reasono"></span>
</div>
As you can see I tried to use $rootScope. I can console.log the reason but I cant make it to show in this html. Any help?
We're missing some context, but I believe this is your problem:
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
Assuming self.reason is bound to the input in your modal, it won't be defined outside of the reject callback - that's the nature of async. You're able to log to console because you're doing so within the callback.
Define $rootScope.reasono inside of the callback like so:
self.reject = function () {
$modalInstance.close(self.reason);
console.log(self.reason);
$rootScope.reasono = self.reason;
};
Edited to show that $rootScope should be removed as a named parameter in the reject function definition.
Using root scope is not recommended. For this reason it is recommended to create a service for intercommuncation with variable to store reject reason, then inject this service for each controller - that way you will be able to read/write reason from different controllers.

communication between controllers in angularjs

The 1st controller has an Id.
The 2nd controller has a function that takes the id and opens a modal.
I want to pass the id from the 1st controller to the 2nd controller and open the 2nd controller's modal while staying in the 1st controller.
There is no relationship between these controllers.
I tried to use $rootScope.broadcast but things are not working as expected.
How can I achieve this feature ?
EDIT:
1st controller:
$scope.getId = function(id){
$scope.id = id;
$rootScope.$broadcast("info", $scope.id);
}
2nd controller:
$scope.viewModal = function(id) {
$scope.id = id;
var modalInstance = $modal.open( {
templateUrl: 'src/web/viewModal.html',
controller: 'viewCtrl',
size: 'lg',
resolve: {
id: function () {
return $scope.id;
}
}
} );
}
$rootScope.$on("info", function(event, id) {
$scope.id = id;
$scope.viewModal(id);
});
I am not sure how the modal from 2nd controller gets invoked while I am clicking on $scope.getId from 1st controller.
I am kind of apprehensive to use Services at this stage because that will involve lot of code change to the existing setup.Please advise.
Communicating between controllers should always be done through a service.
For the modal part you could either figure out your own logic or you could simply use UI-Boostrap Modal. It supports throwing data at it so you could basically have an async request populating in your modal based on an ID you sent it. This is done by giving the modal its own scope and it each modal will have a resolve that you can use to make sure the modal has the correct data in its scope.

Resources