I'm dynamically creating my routes in .run here:
.run(function($http, $rootScope) {
var mainInfo = null;
$http.get('thegoods.json').success(function(data) {
$rootScope.HomeGateway = data.HomeGateway;
var pages = $.merge($rootScope.HomeGateway.questions, $rootScope.HomeGateway.messages);
for (var i = 0; i < pages.length; i++) {
$routeProviderReference.when(pages[i].url, {
templateUrl: pages[i].view,
controller: pages[i].controller,
data: pages[i].data
})
}
});
How can I access the data (defined as data: pages[i].data above). Note, I'm not sure if this is completely correct -- but it should serve as a demonstration of what I'm trying to accomplish.
Basically, I grab a JSON file and store it in $rootScope. I then use the $rootScope to dynamically create all the URLs. (Please see http://blog.brunoscopelliti.com/how-to-defer-route-definition-in-an-angularjs-web-app/ if you are interested in doing this yourself) I want to be able to access data specific to certain pages without having to search through the JSON data again. I understand that it is an inexpensive operation but I'd like to do it the right way if possible.
Another way of phrasing my question would be.. how can I define each pages $scope at .run when the app first starts?
If all, written in article, you share is true, and I correctly understand all you want, then you should use resolve option of second parameter of .when function as described here. And write smth like:
$routeProviderReference.when(pages[i].url, {
templateUrl: pages[i].view,
controller: pages[i].controller,
resolve: {
data: function(){
return pages[i].data;
}
}
});
And then in controllers:
function QuestionController($scope, data) {
$scope.data = data.someProp;
}
Related
Okay, I have three relevant files for my question/s..
App.js - app.config
In my app.config passing value in the resolve using $stateParams is possible and I attain it. But how about the value is from the controller? I need the provCode value to use it in my php select query.
.state("editStudent", {
abstract: true,
url:"/student/:id",
controller: "EditStudentController",
templateUrl: "views/students/editStudent.html",
resolve: {
dataservice: 'dataservice',
student: function(dataservice, $stateParams){
var data ={
id : $stateParams.id
};
return dataservice.getStudent(data);
},
provinceData: function(dataservice){
return dataservice.getProvince();
},
municipalityData: function(dataservice){
var provCode = {
provCode : '0456' // <--------------------- this one
}
return dataservice.getCity(provCode);
}
}
})
EditStudentController.js - app.controller
In my app.controller, I use the angular.forEach to find province code or id to use it php select query. As you can see I tried to create function app.getCity but it is not working as I expected.
console.log(provinceData);
$scope.provinceData = provinceData;
var retProv = $scope.studentInfo.province;
angular.forEach(provinceData, function(value, key) {
if(value.provDesc == retProv){
$scope.studentInfo.province = provinceData[key];
$scope.provCode = provinceData[key].provCode;
console.log($scope.provCode);
}
}); // angular.forEach
var provCode = $scope.provCode;
// Test if it is working but sadly, it is not
// $scope.getCity = function(){
// dataservice.getCity(provCode);
// }
Dataservice.js - app.factory
In my app.factory there are currently 3 functions. The other 2 functions works expectedly but the factory.getCity() is my problem right now. As you can see there is a parameter provCode, this parameter is from my app.config state resolve and it has been assigned a hardcoded/static value.
factory.getStudent = function(data){
return $http.post("php/students/viewOneStudent.php",data).then(function(response){
student = response.data[0];
console.log(student);
return student;
})
}
factory.getProvince = function(){
return $http.get('php/address/province.php').then(function(response){
provinceData = response.data;
console.log(provinceData);
return provinceData;
})
}
factory.getCity = function(provCode){
return $http.post('php/address/cities.php', provCode).then(function(response){
municipalityData = response.data;
console.log(municipalityData);
return municipalityData;
})
}
My Question is:
If it is possible to pass a value from controller to a resolve?
If it is possible, how can I do that? because I tried my very best in trial and error approach.
If it is not possible, what is my option/s to achieve what I want?
Note
I tried to do it without resolve, getting data and posting again for php querying, it is also works BUT the problem is sometimes data doesn't load when the view is loaded. As far as I know, read and understand from different sources in the internet, there is a time that view is loaded but the "work on processing the data" is not finished yet..
I am pretty new and still learning AngularJs. When I am practicing and learning AngularJs coding, I love how it works especially the routing, I am impressed with it. So, I am trying my very best to understand AngularJs and its complexity.
UPDATE
I tried different approach of how I can get my expected results. But I can't get to it, I encountered an error. This is the link of my other approach.
Undefined property: stdClass: in using AngularJS + PHP
I have an interesting situation, which I fully admit I may not be handling correctly.
I want to load directives in to an Angular app using AngularAMD, but the information on the directives is coming from a database/API call. Here's how I'm handing it at the moment:
app.run(function ($rootScope, $state, $http) {
$rootScope.$state = $state;
$rootScope.rootPages = JSON.parse(localStorage.pages);
$http.get('api/Directives').then(function (resp) {
for (var i = 0; i < resp.data.length; i++) {
var dirInfo = resp.data[i];
var dirConfig = JSON.parse(dirInfo.Configuration);
angularAMD.directive(dirInfo.Name, function () {
return {
restrict: 'E',
controller: eval(dirConfig.controller),
templateUrl: dirConfig.templateUrl,
scope: true,
}
});
}
}, function (error) {
alert('error');
});
});
At the moment this generates two directives, and I have two links on my page, one for each module (using angular-ui-router and router-ui-extras to load those based on another database table/API call). Theoretically clicking link 1 should bring up directive 1, and link 2 should bring up directive 2. However, when I click link 1, directive 2 comes up. It's almost as if the only directive that registers is directive 2.
Here's the information I get from the database:
dirInfo1: {
Name: directiveOne,
templateUrl: /html/directiveOne.html,
controller: controllerOne
}
dirInfo2: {
Name: directiveTwo,
templateUrl: /html/directiveTwo.html,
controller: controllerTwo
}
In this case, only directiveTwo.html is shown, and only controllerTwo fires.
Any help would be greatly appreciated.
It't a usual problem using for loop with delayed/async operations.
By the moment that first angularAMD.directive is resolved var dirInfo = resp.data[i]; inside of the loop is is equal to the last item in the list.
You need to use either Promises or anonymous function inside of a loop to keep the link to a right index inside of async function. Check simple example of what's happening in your case here: https://stackoverflow.com/a/13977142/405623
for ( i = 0; i < items.length; i++) {
(function(thisItem) {
setTimeout(function() {
console.log(thisItem);
}, i * 1000);
)(items[i]);
}
am using angular ui-router to manage states of my SPA.
I have this route:
.state('index.administration.security.roleEdit', {
url: '/roleEdit',
templateUrl: 'app/administration/security/role/roleEdit.html',
controller: 'roleEditCtrl',
controllerAs: 'roleEditCtrl',
params: { data: null },
resolve: {
role: function ($stateParams) {
return angular.fromJson($stateParams.data);
},
modules: function (securityService) {
return securityService.getAllModules();
}
}
})
Also, I'm passing 'data' parameter as json object to the state.
Now when i first load this state, everything is fine.
But when i do browser refresh (F5 key) the $stateParams.data is null in the resolve function of the state.
How can I solve this?
I see these possible solutions:
1. persist somehow parameters
2. override browser refresh (don't know how) to stop the browser refreshing the app.
3. on refresh goto other sibling state.
Please help
UPDATE
Ok, I set data like this:
vm.editRole = function(roleId){
var role = dataService.getRoleById(roleId).then(function(result){
$state.go('roleEdit', {data:angular.toJson(result)});
});
}
UPDATE 2
The roleEdit Controller looks like this:
(function(){
angular.module('app.administration').controller('roleEdit',
['role','modules', '$scope', 'securityService', '$state', roleEditCtrl]);
function roleEditCtrl('role', 'modules',$scope, securityService, $state){
var vm = this;
vm.roles = roles;
vm.originalRole = angular.copy(role);
vm.modules=modules;
vm.saveChanges = _saveChanges;
vm.cancel = _cancel;
return vm;
function _saveChanges(){
securityService.UpdateRole(vm.role).then(function(result){
$staste.go('^.roles');
}
}
function _cancel(){
vm.role = angular.copy(vm.originalRole);
$sscope.roleEditForm.$setPristine();
}
}
})();
Had the same problem, leaving this here in case someone else needs it. Solved it by using localStorage.
I've set this as a part of app's run method
$rootScope.$on('$stateChangeSuccess', function (event, toState) {
localStorage.setItem('__stateName', toState.name);
});
Now, depending on your app's structure you might want to consider putting this someplace else, but in my case I had parent AppController surrounding all child controllers, so this part of the code went there.
var previousState = localStorage.getItem('__stateName');
previousState && $state.go(previousState) || $state.go(<SOME_OTHER_ROUTE>);
Basically, when user hits browser refresh, AppController get initialized from the start (before child controllers), firing the state transition immediately if there was one. Otherwise, you'd go to some other state of yours.
I would stay away from option 2. You don't want to mess up a browser's expected behavior.
Maybe you could refactor your data into your resolve object ala:
resolve: {
role: function ($stateParams) {
return angular.fromJson($stateParams.data);
},
modules: function (securityService) {
return securityService.getAllModules();
},
data: function (dataService) {
return dataService.retrieveStoredData();
}
Where your dataService would be a service you use to store that data if it really is that important (through cookies or localstorage).
There is no reasonable way to expect that field to be populated on browser refresh if you previously executed javascript to pass stateful values to it.
I've been with Angularjs a few days and I'm struggling with a few aspects of it. I'll do my best to try and explain what the issue is, and I'd really appreciate any help anyone can give me about it.
My situation (simplified) is this:
I have a service which loads some info from a json and stores it in an object. It also have some functions to be used for other controllers to retrieve that information.
var particServices = angular.module('particServices', []);
particServices.service('particSrv', function() {
var data = {};
this.updateData = function(scope) {
data = // http call, saves in data
}
this.getName = function(code) {
return data.name;
}
});
I have an html page assisted by a controller, which uses a directive board (no params, really simple). This is the controller:
var bControllers = angular.module('bControllers', []);
bControllers.controller('bController', ['$scope', 'particSrv', function ($scope, particSrv) {
$scope.getName = function(code) {
return particSrv.getName(code);
};
particSrv.updateData($scope);
}]);
As you can see, the controller makes the call to initialize the object in the service. As this is a singleton, I understand once that info is loaded no other call needs to be make to updateData and that info is available to others using the getters in the service (getName in this case).
I have a really simple directive board (which I simplified here), which uses another directive bio.
angular.module('tsDirectives', [])
.directive('board', ['dataSrv', 'particSrv', function(dataSrv, particSrv) {
return {
restrict: 'E',
replace: true,
scope: true,
controller: function($scope) {
$scope.getName = function(code) {
return particSrv.getName(code);
};
dataSrv.updateData($scope, 'board', 'U');
},
templateUrl: '<div class="board"><div bio class="name" partic="getName(code)"/></div></div>'
};
}]);
And this is the bio directive:
angular.module('gDirectives', [])
.directive('bio', function() {
return {
scope: {
partic: '&'
},
controller: function($scope) {
$scope.name = $scope.partic({code: $scope.athid});
},
template: '<a ng-href="PROFILE.html">{{name}}</a>'
};
})
Now, what I expected is that in the bio directive the info retrieved from party was displayed, but apparently this directive is processed before the partic is initialized in the main controller.
I was under the impression that even though this information was still not loaded when the directive is processed, as soon as the service finishes and the info is ready, automagically it would appear in my directive, but that does not seem to work like that. I've been reading about $watch and $digest, but I fail to see why (and if) I would need to call them manually to fix this.
Any hint will be much appreciated. I could provide more technical details if needed.
Directive will initialise when app is loaded and user opens the page where that directive is, if you have some property that is set later (from api for example), it will update that property in directive but that directive will not be reinitialised ($scope.partic({code: $scope.athid}) wont be called).
If you want for directive to wait for initialisation you should use ng-if. Something like this:
<div data-directive-name data-some-property="someProperty" data-ng-if="someProperty"></div>
In this case directive will be initialised when (if) you have some value in $scope.someProperty. But this is not very good if you can have false values for someProperty.
In that case you would need to use some kind of loaded flag.
You have not included "particServices" as a dependency in other modules which use the services of "particServices". Your modules should look like:
var bControllers = angular.module('bControllers', ['particServices']);
angular.module('tsDirectives', ['particServices']);
angular.module('gDirectives', ['particServices']);
I'm building a pretty simple app where I have a GlobalController (on body element), and will have another sub-controller below. This is a templated site with multiple, physical pages such that the sub-controller will be different, but at most there will only be a top-level Global one and a single sub-one.
I'm trying to make global functions that any sub-controller can use to run code that each needs to run without having to duplicate the functionality in each sub-controller.
One way I could do this would be to include $rootScope and then emit() messages to the GlobalController who is listening for them using $on().
I gather this is not a "good" way to do this. Rather, I've learned that it's better to use a service for this. I'm now stuck on how to implement this service.
I currently have a Global Controller like so:
var globalModule = angular.module('DoSquareStuff', ["ngRoute","list", "savings-video"]);
// there will be a long list of modules that will be added after "savings-video"
globalModule.factory('squareMgr', function() {
var squares = SUYS.squares; // global obj with earned[] and placed[]
return {
getSquaresEarned: function() {
return squares.earned;
},
getSquaresPlaced: function() {
return squares.placed;
},
setThisSquareEarned: function(value) {
squares.earned.push(value);
},
setThisSquarePlaced: function(value) {
squares.placed.push(value);
},
earnedThisSquare: function(squareNum) {
return ~squares.earned.indexOf(squareNum);
},
placedThisSquare: function(squareNum) {
return ~squares.placed.indexOf(squareNum);
}
}
});
globalModule.controller('GlobalController', function($scope, $squareMgr){
// this would be easy... but it doesn't work
// $rootScope.$on('signal.missionComplete', function (event, missionId) {
// console.log("parentScope receive notice that mission " + missionId + " is complete.");
// });
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned()); //broken
});
Then, reading the docs, I tried:
globalModule.controller('GlobalController', ['squareMgr', function($squareMgr){
// but then how do I get $scope in there?
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned());
}]);
In your last code block, you need to inject $scope as well. You can inject any number of services that you need:
globalModule.controller('GlobalController', ['squareMgr', '$scope',
function($squareMgr, scope){
// but then how do I get $scope in there?
log('GlobalController loaded');
// log($squareMgr.getSquaresEarned());
}]);
And a minor point, I wouldn't put a $ in front of squareMgr, the $ implies it is a built in angular service.
Try
globalModule.controller('GlobalController', ['squareMgr', '$scope', function($scope, squareMgr){ .....
The $ sign is used to differentiate between Angular services and your own