I have a page in which there is a drop down, My goal is when I select any value from drop down and click on Next,It is switching to Next page using ngRouteProvider and doing $http.post , I need to pass data as payback load.
.when('/xyz/step2', {
templateUrl: partial('replacStep2.html'),
controller: 'xyzCtrl',
resolve: {
cardTypeDetails: ['$http', function ($http) {
return $http.post(appContext('xyz.json'),{});
}]
}
})
You can create a factory and set a variable inside it, later you can inject that factory inside your resolve block and access your variable through it.
resolve: {
cardTypeDetails: ['$http','sharedFactory', function ($http,sharedFactory) {
console.log(sharedFactory.sharedVariable); //use it like this
return $http.post(appContext('xyz.json'),{});
}]
}
and factory
angular.module('demoApp').factory('sharedFactory',function(){
return {
sharedVariable:null
};
});
Or in same way you can use $rootScope to pass variables (but not great for performance if you do for large no. of variables)
Passing variables from one controller to another with $rootscope
Related
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]
}
I want to combine multiple requests to one, so it won't do n requests but one.
Problem is that I don't know how to do a resolve to an object of names, which are variables that are injected in ctrl.
For example, now I have resolved:
resolve: {
regions : ["$http", function($http){
return $http({...});
}],
coordinators: ["$http", function($http){
return $http({...});
}],
....
}
What I want (not working) are same variables(n names) and only one request, which return n values to an object of names:
resolve: {
[regions,coordinators,...] : ["$http", function($http){
return $http({... return values;});
}]
}
Any ideas how?
You can achieve this by having one service endpoint that gathers your several pieces of information into an object literal:
function gatherResolves() {
return { region1.promise, region2.promise, region3.promise };
}
Then in the resolve you have just one call:
.state('foo', {
resolve: regions: ['$http', function($http) {
return $http.gatherResolves({ ... });
}]
})
Once in the controller, and injected into it, you can then split the injectable into the various pieces you want via destructuring (assuming you're using ES6) or manually assigning the object literal back into variables with your individual calls.
constructor(regions) {
this.splitRegions(this.regions);
}
splitRegions(regions) {
this.region1 = regions['region1'];
this.region2 = regions['region2'];
}
I might have mixed up some syntax as I'm mixing ES5/ES6 I think. But you get the idea, it is possible to distill it all down to 1 resolve call. The real question is, why, as you're just shifting logic around. Also, one of the benefits is that child states inherit resolves, so you can structure your code to make use of this. Having just one resolve with everything in it means you're carrying that around on all child states of that parent.
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 have a scenario where I want to render the template only when some condition is met(on the basis of data we get from the REST call). So, I did the following :
My Code (by injecting the factory into the template function):
.state('dashboard.deal.xyz', {
url: "/xyz/:dealId",
resolve: {
permissions: function(dealUnit, $state) {
console.log('Inside the resolve of permissions :::', $state.params.dealId);
return dealUnit.getUnitPermissions('123412341234DealId');
}
},
views: {
"viewA": {
template: function(dealUnit) {
//Here I want to inject either 'permissions' (the resolve object)
//or the 'dealUnit' factory which gets me the permissions from the server directly.
return '<h1>return the template after getting the permissions resolve object</h1>';
}
},
"viewB": {
template: "viewB"
}
}
})
My 'dealUnit' factory is working fine and returns an object when I use it inside the 'resolve' of the state. But, that factory is not being injected when I inject it inside the template function of the nested view.
Am I doing it correctly? And if not then how should I go about doing it ?
In case, we want to do some "magic" before returning the template... we should use templateProvider. Check this Q & A:
Trying to Dynamically set a templateUrl in controller based on constant
Because template:... could be either string or function like this (check the doc:)
$stateProvider
template
html template as a string or a function that returns an html template as a string which should be used by the uiView directives. This property takes precedence over templateUrl.
If template is a function, it will be called with the following parameters:
{array.} - state parameters extracted from the current $location.path() by applying the current state
template: function(params) {
return "<h1>generated template</h1>"; }
While with templateProvider we can get anything injected e.g. the great improvement in angular $templateRequest. Check this answer and its plunker
templateProvider: function(CONFIG, $templateRequest) {
console.log('in templateUrl ' + CONFIG.codeCampType);
var templateName = 'index5templateB.html';
if (CONFIG.codeCampType === "svcc") {
templateName = 'index5templateA.html';
}
return $templateRequest(templateName);
},
Basically we have a lot of controllers and templates and we wanted to dynamically load both the controller and the template for a given route.
Something along the lines of:
$routeProvider.when('/page/:page', {
templateUrl: fuction(params) {
return '/templates/pages/' + params.page;
},
controller: function(params) {
return params.page + 'Controller';
}
})
However we don't receive the params argument for the controller function. Is there any way to achieve this kind of flexibility?
I guess you can use the $injector.instantiate function which will invoke the given function using new with the dependencies injected. Since a controller is used as a constructor you just need to wrap the controller function like this:
controller: function () {
return $injector.instantiate(function (params) { ... });
}
This is just a guess, I don't guarantee it will work.