I would like to know how i should keep $stateParams variables after a reload of page.
.state('proSearch', {
url: '/proSearch/:city',
params: {
location: null
},
templateUrl: 'imports/client/ui/proSearch/proSearch.html',
controller: 'proSearchController',
controllerAs: 'vm'
})
When the page is reloaded my controller has lost $stateParams.location data. How can i save it ?
You can save the $stateParams data on a $scope variable instead of a local variable. A $scope variable persists on your scope.
$scope.city = $stateParams.location;
You can access $scope variables on other controllers, as long as you reference $scope.
I guess you don't need to reload the page. Two-way data binding is a cool concept that encourages not having to reload a page to see data update.
you can reach it in the controller with $stateParams service.
function MainController($scope, $stateParams, $state) {
var city = $stateParams.location
state.go($state.current, {location: city}, {reload: true});
}
This code is keep your location params at the city variable and second row is refresh the page with current city parameter. I hope it's help
Related
I trying to create a search application using angularJS.I am facing the issue in binding $scope values to view when router Url changes.
I have a search field in /main.When I write the query and click on search button, the function does the data fetch and assign to a scope variable.The router URL will change to '/Result' and the respective view is displayed.But the view doesn't have the scope values bound. /main and /Result uses the same controller.
router code in main module :
$routeProvider.
when('/main', {
templateUrl: '/views/search.html',
controller: 'searchCtrl'
}).when('/Result',{
templateUrl:'/views/Result.html',
controller: 'searchCtrl' ,
reloadOnSearch: false
}).otherwise({
redirectTo: '/main'
});
Controller :
On button click from /main
$scope.fetchResult=function(searchWord){
shService.fetchResultDocumentsList(searchWord).data.then(function(response){
//service call here-data fetch is successfull.
$scope.docResultList=response[0];
$scope.docResultList=response[0];
$scope.documents = $scope.docResultList.data.documentEntities;
$location.path('/Result');
}
When the respective view is changing, the binding is not done.But when i replace the $scope.documents with $rootScope.documents binding is successful.
I have read the use of $scope is encouraged over $rootScope.
The controller and $scope gets re initialized when you move from one page to another page. if you want to use $scope , you should consider using service to share the data across controllers.
Create a service, that will hold your variable.
angular.service("dataService", function() {
this.value1 = ""
});
reference that service in your controllers,
angular.controller("myCntrl1", function($scope, dataService) {
$scope.val= dataService.value1 ;
});
angular.controller("myCntrl2", function($scope, dataService) {
$scope.val= dataService.value1 ;
});
As Sajeetharan said, $scope get reinitialized when you update location.
Using angularJs, you don't need to change location. You simply update $scope in the same view you used for searching.
But if you really need to use another view, and assuming your search returns only strings, you could try to pass data through url, and grab it from your controller.
Something like this (not tested):
Solution 1 (Angular way)
Controller
$scope.documents = ""; // init your results to empty/null, as you need
$scope.fetchResult=function(searchWord){
shService.fetchResultDocumentsList(searchWord).data.then(function(response){
//service call here-data fetch is successfull.
$scope.docResultList=response[0];
$scope.docResultList=response[0];
$scope.documents = $scope.docResultList.data.documentEntities;
$location.path('/Result');
}
View
<!-- search div is over here -->
<!-- results goes here -->
<div ng-if="$scope.documents">
{{$scope.documents}}
</div>
Solution 2 (your way)
Router
$routeProvider.
when('/main', {
templateUrl: '/views/search.html',
controller: 'searchCtrl'})
.when('/Result/{data:[a-z]{1,}}',{ //here we specify that data expected format is only lowercase letters
templateUrl:'/views/Result.html',
controller: 'searchCtrl' ,
reloadOnSearch: false
})
.otherwise({
redirectTo: '/main'
});
Controller
// dont forget to inject $stateParams service in your controller
if( !$stateParams.data){
$scope.data = $stateParams.data;
}
Given the following state in ui-router:
.state('some.state', {
url: '/some/:viewType',
templateUrl: 'myTemplate.html',
controller: 'SomeStateController',
controllerAs: 'vm',
data: {
authorizedFor: [SOME_ROLE]
}
}
I'm trying to use the "data" object for a state to help control access to authorized states. Separately, I handle the $stateChangeStart event to look at data.authorizedFor and act accordingly.
The problem, though, is that the list of authorized roles might change based on the value of :viewType. I thought I could let data:{} be a function, inject $stateParams, and handle the logic there...but that won't do.
So, I tried using the params object instead, but at the $stateChangeStart time, the :viewType is not yet accessible from $state.params or $stateParams.
Stepping through in dev tools, I noticed that $state.transitionTo.arguments is populated, but it seems awfully hacky to go that route.
params: {
authorizedFor: function($state) {
console.log($state.transitionTo.arguments[1].viewType); // has value I need
}
}
Any suggestions?
My suggestion is to use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.
If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $stateChangeSuccess event is fired.
for example:
$stateProvider
.state('profile', {
url: '/profile',
templateUrl: 'profile.html',
resolve:{
'ProfileService': function(ProfileService){
return ProfileService.promise_skillRecommendation_mock;
}
}
})
The profileService code:
var app = angular.module('app').service("ProfileService", function($http){
var myData = null;
var promise_skillRecommendation_mock =
$http.get('Mock/skillRecommendation-mock.json')
.success(function(data){
myData = data;
});
return{
promise_skillRecommendation_mock: promise_skillRecommendation_mock,
get_skillRecommendation: function(){
return myData;
}
};
});
and the controller code which will use this service is:
angular.module('app').controller('ProfileController', function($scope, $http, ProfileService){
$scope.skillRecommendation = ProfileService.get_skillRecommendation();
The object in resolve below must be resolved (via deferred.resolve() if they are a promise) before the controller is instantiated. Notice how each resolve object is injected as a parameter into the controller.
by using this code, the page will be displayed only after that the promise will be resolved.
for more info please view this page: https://github.com/angular-ui/ui-router/wiki
I want to get the value of one of the properties in the $stateParams object in my controller. I seem to be able to get the $stateParams object as a whole but I can't get a specific property.
$rootScope.params = $stateParams; // this gets me the object
$rootScope.myVar = $stateParams.fooParam + ' some msg'; // this gets me undefined
So this is how I setup my $stateProvider...
$stateProvider
.state('parent', {
url: "/parent",
templateUrl: 'tpl.html',
params: {
fooParam: 'foo defult',
barParam: 'bar defult'
},
controller: 'ParentCtrl'
})
And then in my html ui-sref route, I pass some stuff to the param.
<a ui-sref="parent({
fooParam:'foo parent',
barParam:'bar parent'
})">parent</a>
Then in my controller I want to access those params. Here is where Is truggle to access members of the $stateParams object.
$rootScope.myVar = $stateParams.fooParam + ' some msg';
In my HTML if I call {{myVar}}, I just get "undefined some msg"
Basically in this particular example I want to get the value of the fooParam in my controller. I don't understand how to do that.
Here's a Plunker of the example of my issue:
https://plnkr.co/edit/dXTgKMpBTHiv2Bt5nFxC?p=preview
Actually, I was wrong. You do need to include params in the url as below:
$stateProvider
.state('parent', {
url: "/parent/:fooParam/:barParam",
params: {
fooParam: 'foo defult',
barParam: 'bar defult'
},
templateUrl: 'tpl.html',
controller: 'ParentCtrl'
})
But the other issue is that you need to access $stateParams from the controller registered for that state, which is listed in the gotchas section of the documentation.
See updated plunker showing myVar from $stateParams injected in controller (works fine) and MyVar2 from $stateParams injected in app.run() function (doesn't work).
You can inject $stateParams directly into controller. Change your controller as below.
.controller('ParentCtrl', ['$scope','$stateParams', function($scope,$stateParams)
Here is the Plunker
https://plnkr.co/edit/PVXGjFLMVQdvxUHp1rgs?p=preview
With ui-router, it's possible to inject either $state or $stateParams into a controller to get access to parameters in the URL. However, accessing parameters through $stateParams only exposes parameters belonging to the state managed by the controller that accesses it, and its parent states, while $state.params has all parameters, including those in any child states.
Given the following code, if we directly load the URL http://path/1/paramA/paramB, this is how it goes when the controllers load:
$stateProvider.state('a', {
url: 'path/:id/:anotherParam/',
controller: 'ACtrl',
});
$stateProvider.state('a.b', {
url: '/:yetAnotherParam',
controller: 'ABCtrl',
});
module.controller('ACtrl', function($stateParams, $state) {
$state.params; // has id, anotherParam, and yetAnotherParam
$stateParams; // has id and anotherParam
}
module.controller('ABCtrl', function($stateParams, $state) {
$state.params; // has id, anotherParam, and yetAnotherParam
$stateParams; // has id, anotherParam, and yetAnotherParam
}
The question is, why the difference? And are there best practices guidelines around when and why you should use, or avoid using either of them?
The documentation reiterates your findings here: https://github.com/angular-ui/ui-router/wiki/URL-Routing#stateparams-service
If my memory serves, $stateParams was introduced later than the original $state.params, and seems to be a simple helper injector to avoid continuously writing $state.params.
I doubt there are any best practice guidelines, but context wins out for me. If you simply want access to the params received into the url, then use $stateParams. If you want to know something more complex about the state itself, use $state.
Another reason to use $state.params is for non-URL based state, which (to my mind) is woefully underdocumented and very powerful.
I just discovered this while googling about how to pass state without having to expose it in the URL and answered a question elsewhere on SO.
Basically, it allows this sort of syntax:
<a ui-sref="toState(thingy)" class="list-group-item" ng-repeat="thingy in thingies">{{ thingy.referer }}</a>
EDIT: This answer is correct for version 0.2.10. As #Alexander Vasilyev pointed out it doesn't work in version 0.2.14.
Another reason to use $state.params is when you need to extract query parameters like this:
$stateProvider.state('a', {
url: 'path/:id/:anotherParam/?yetAnotherParam',
controller: 'ACtrl',
});
module.controller('ACtrl', function($stateParams, $state) {
$state.params; // has id, anotherParam, and yetAnotherParam
$stateParams; // has id and anotherParam
}
There are many differences between these two. But while working practically I have found that using $state.params better. When you use more and more parameters this might be confusing to maintain in $stateParams. where if we use multiple params which are not URL param $state is very useful
.state('shopping-request', {
url: '/shopping-request/{cartId}',
data: {requireLogin: true},
params : {role: null},
views: {
'': {templateUrl: 'views/templates/main.tpl.html', controller: "ShoppingRequestCtrl"},
'body#shopping-request': {templateUrl: 'views/shops/shopping-request.html'},
'footer#shopping-request': {templateUrl: 'views/templates/footer.tpl.html'},
'header#shopping-request': {templateUrl: 'views/templates/header.tpl.html'}
}
})
I have a root state which resolves sth. Passing $state as a resolve parameter won't guarantee the availability for $state.params. But using $stateParams will.
var rootState = {
name: 'root',
url: '/:stubCompanyId',
abstract: true,
...
};
// case 1:
rootState.resolve = {
authInit: ['AuthenticationService', '$state', function (AuthenticationService, $state) {
console.log('rootState.resolve', $state.params);
return AuthenticationService.init($state.params);
}]
};
// output:
// rootState.resolve Object {}
// case 2:
rootState.resolve = {
authInit: ['AuthenticationService', '$stateParams', function (AuthenticationService, $stateParams) {
console.log('rootState.resolve', $stateParams);
return AuthenticationService.init($stateParams);
}]
};
// output:
// rootState.resolve Object {stubCompanyId:...}
Using "angular": "~1.4.0", "angular-ui-router": "~0.2.15"
An interesting observation I made while passing previous state params from one route to another is that $stateParams gets hoisted and overwrites the previous route's state params that were passed with the current state params, but using $state.params doesn't.
When using $stateParams:
var stateParams = {};
stateParams.nextParams = $stateParams; //{item_id:123}
stateParams.next = $state.current.name;
$state.go('app.login', stateParams);
//$stateParams.nextParams on app.login is now:
//{next:'app.details', nextParams:{next:'app.details'}}
When using $state.params:
var stateParams = {};
stateParams.nextParams = $state.params; //{item_id:123}
stateParams.next = $state.current.name;
$state.go('app.login', stateParams);
//$stateParams.nextParams on app.login is now:
//{next:'app.details', nextParams:{item_id:123}}
Here in this article is clearly explained: The $state service provides a number of useful methods for manipulating the state as well as pertinent data on the current state. The current state parameters are accessible on the $state service at the params key. The $stateParams service returns this very same object. Hence, the $stateParams service is strictly a convenience service to quickly access the params object on the $state service.
As such, no controller should ever inject both the $state service and its convenience service, $stateParams. If the $state is being injected just to access the current parameters, the controller should be rewritten to inject $stateParams instead.
I have these routes defined:
.state('sport',
url: '/sport'
templateUrl: '/templates/sport'
controller: 'SportCtrl'
)
.state('sport.selected'
url: '/:sport'
templateUrl: '/templates/sport'
controller: 'SportCtrl'
)
And I have this controller trying to use the :sport param given by sport.selected state.
angular.module('myApp')
.controller('SportCtrl', ['$scope', 'ParseService',
'$stateParams', function ($scope, ParseService, $stateParams) {
var sportURL = $stateParams.sport;
...
});
For some reason, it returns undefined when I call $stateParams.sport in the controller, even though I think I defined it in the routes.
Why is this the case?
Thanks for your help!
When you access the URL /sport/12, the SportCtrl will be instantiated twice: once for the state sport, and once for the state sport.selected. And for the first state, there is no parameter associated with the state, so $stateParams.sport is undefined.
Note that it's quite strange to use the same template for a state and a sub-state. You'll have the template embedded inside the ui-view div of the same template.