using resolve doesnot change state : Angular ui router - angularjs

i m using angular ui-router and to get data before initiating Controller m using resolve
.state('Equipment', {
url: "/equipment",
templateUrl: "/Scripts/app/equipment/templates/content.html",
resolve: {
equipmentService: 'EquipmentService',
data: function (equipmentService) {
return equipmentService.getEquipment().$promise;
}
},
controller: 'EquipmentController'
})
and in equipmentService i m doing
resource.equipment.getEquipment().$promise.then(function (response) {
_equipment.collection = _.map(response.data, function (item) {
var obj = {
id: item.id,
name: item.name,
groupId: item.equipmentGroupId,
groupName: item.equipmentGroupName,
description: item.description
}
return obj;
});
return _equipment.collection;
});
on response from server service object gets updated but state doesn't change neither controller gets initiated
and if i change it with
return resource.equipment.getEquipment();
now on server response controller gets initiated
but service doesnt as i m using service to share data between controllers. i have to update service object literal
can some one help me what i m doing wrong

I think there is a better option here. You can use this service as a model once the data is retrieved from the server and for that define a variable(property) in service. check out the following question. ui-route resolve not working

Related

ui-router 1.x.x change $transition$.params() during resolve

Trying to migrate an angularjs application to use the new version of angular-ui-router 1.0.14 and stumbled upon a problem when trying to change $stateParams in the resolve of a state.
For example, previously (when using angular-ui-router 0.3.2) modifying $stateParams worked like this:
$stateProvider.state('myState', {
parent: 'baseState',
url: '/calendar?firstAvailableDate',
template: 'calendar.html',
controller: 'CalendarController',
controllerAs: 'calendarCtrl',
resolve: {
availableDates: ['CalendarService', '$stateParams', function(CalendarService, $stateParams) {
return CalendarService.getAvailableDates().then(function(response){
$stateParams.firstAvailableDate = response[0];
return response;
});
}]
}
})
The problem is firstAvailableDate is populated after a resolve and I do not know how to update $transition$.params() during a resolve when usign the new version of angular-ui-router 1.0.14.
I have tried, and managed to update the url parameter with
firing a $state.go('myState', {firstAvailableDate : response[0]}) but this reloads the state, so the screen flickers
modified $transition$.treeChanges().to[$transition$.treeChanges().length-1].paramValues.firstAvailableDate = response[0]; to actually override the parameters. I have done this after looking through the implementation on params() for $transition$.
Although both those options work, they seem to be hacks rather than by the book implementations.
What is the correct approach to use when trying to modify parameters inside a resolve?
Approach with dynamic parameter:
Take a look at this document: params.paramdeclaration#dynamic. Maybe thats what you are looking for: ...a transition still occurs....
When dynamic is true, changes to the parameter value will not cause the state to be entered/exited. The resolves will not be re-fetched, nor will views be reloaded.
Normally, if a parameter value changes, the state which declared that the parameter will be reloaded (entered/exited). When a parameter is dynamic, a transition still occurs, but it does not cause the state to exit/enter.
This can be useful to build UI where the component updates itself when the param values change. A common scenario where this is useful is searching/paging/sorting.
Note that you are not be able to put such logic into your resolve inside your $stateProvider.state. I would do this by using dynamic parameters to prevent the state reload. Unfortunally, the dynamic rules doesn't work when you try to update your state (e.g. by using $stage.go()) inside the resolve part. So I moved that logic into the controller to make it work nice - DEMO PLNKR.
Since userId is a dynamic param the view does not get entered/exited again when it was changed.
Define your dynamic param:
$stateProvider.state('userlist.detail', {
url: '/:userId',
controller: 'userDetail',
controllerAs: '$ctrl',
params: {
userId: {
value: '',
dynamic: true
}
},
template: `
<h3>User {{ $ctrl.user.id }}</h3>
<h2>{{ $ctrl.user.name }} {{ !$ctrl.user.active ? "(Deactivated)" : "" }}</h2>
<table>
<tr><td>Address</td><td>{{ $ctrl.user.address }}</td></tr>
<tr><td>Phone</td><td>{{ $ctrl.user.phone }}</td></tr>
<tr><td>Email</td><td>{{ $ctrl.user.email }}</td></tr>
<tr><td>Company</td><td>{{ $ctrl.user.company }}</td></tr>
<tr><td>Age</td><td>{{ $ctrl.user.age }}</td></tr>
</table>
`
});
Your controller:
app.controller('userDetail', function ($transition$, $state, UserService, users) {
let $ctrl = this;
this.uiOnParamsChanged = (newParams) => {
console.log(newParams);
if (newParams.userId !== '') {
$ctrl.user = users.find(user => user.id == newParams.userId);
}
};
this.$onInit = function () {
console.log($transition$.params());
if ($transition$.params().userId === '') {
UserService.list().then(function (result) {
$state.go('userlist.detail', {userId: result[0].id});
});
}
}
});
Handle new params by using $transition.on* hooks on route change start:
An other approach would be to setup the right state param before you change into your state. But you already said, this is something you don't want. If I would face the same problem: I would try to setup the right state param before changing the view.
app.run(function (
$transitions,
$state,
CalendarService
) {
$transitions.onStart({}, function(transition) {
if (transition.to().name === 'mySate' && transition.params().firstAvailableDate === '') {
// please check this, I don't know if a "abort" is necessary
transition.abort();
return CalendarService.getAvailableDates().then(function(response){
// Since firstAvailableDate is dynamic
// it should be handled as descript in the documents.
return $state.target('mySate', {firstAvailableDate : response[0]});
});
}
});
});
Handle new params by using $transition.on* hooks on route change start via redirectTo
Note: redirectTo is processed as an onStart hook, before LAZY resolves.
This does the same thing as provided above near the headline "Handle new params by using $transition.on* hooks on route change start" since redirectTo is also a onStart hook with automated handling.
$stateProvider.state('myState', {
parent: 'baseState',
url: '/calendar?firstAvailableDate',
template: 'calendar.html',
controller: 'CalendarController',
controllerAs: 'calendarCtrl',
redirectTo: (trans) => {
if (trans.params().firstAvailableDate === '') {
var CalendarService = trans.injector().get('CalendarService');
return CalendarService.getAvailableDates().then(function(response){
return { state: 'myState', params: { firstAvailableDate: response[0] }};
});
}
}
});

Ionic send data when routing another page

I havea problem: I want to send data from my home.html page to map.html page. So I am trying to pass the data from HomeController to MapController. But I don't want to use Service..
I am looking for a solution sending data simply by using $state.go or anything else...
var myObject = {
x: "asdsad",
y: "skdfj",
....
}
$state.go('map', {"test":myObject})
You've started totally right.
In your state, define the params expected, example:
.state('tab.compare-info', {
url: '/compare/info',
params: {
map: null
},
template: '<p></p>',
controller: 'Ctrl'
}
and call the route with
$state.go('tab.compare-info', {map: object});
Inside the destination state controller, inject $stateParam and get the parameter:
function Ctrl($stateParams) {
var map = $stateParams.map;
}

Angular ui router view loaded but not passing parameters

I'm working on a website with angular ui-router. There is a page which needs to pass some parameters to another view. I defined my states like this:
.state('locaties', {
url: "/locaties",
data: {rule: function($cookieStore) {} },
controller: "FranchisesCtrl",
templateUrl: "view/locaties.html"
})
.state('locaties.detail', {
params: {
locatieID: 1,
locatieName: "Derptown",
locatieLat: 50,
locatieLong: 50
},
url: "/:locatieName",
controller: "LocatieDetailCtrl",
templateUrl: "view/locatie.html",
resolve: {
locatiedetail:
function ($stateParams, $http){
var url ="http://website/api/franchises/" + $stateParams.locatieID + "/nl.json";
return $http.get(url).then(function(res){
return res.data;
});
}
}
})
Inside LocatieDetailCtrl there's this
angular.module('PremiumMeat').controller('FranchisesDetailCtrl',
function ($scope, $window, franchisedetail) {
$scope.franchiseDetail = franchisedetail;
});
The "Locaties" (plural) view works properly and when I click on a specific "locatie" (single), the url changes and the view gets loaded within the locaties view and no parameters are passed. On the image you can see the top 2 items from the "locaties" view. Then a single locatie is loaded under the "locaties" view. This should be a new page (view) with the parameters from the clicked locatie. Can anyone help me / explain, I'm rather new to angular, thank you.
Solution
The parameters where hard-coded, to make them dynamic, syntax needed adjustment according to angular docs.
params: {
locatieID: {value : "1"},
locatieName: {value : "Stad"},
locatieDescr: {value : "Beschrijving"},
locatieLat: {value: 51.2},
locatieLong: {value : 4.4}
},
Where parameters are passed with ui-href like this
<a ui-sref="locaties.detail({
locatieID: item.id,
locatieName: item.name,
locatieDescr: item.description,
locatieLat: item.location[0].lat,
locatieLong: item.location[0].long
})"
class="detail">Bekijk detail >></a>
The 'params' defined should return the key-value pair object.
But it is a better practice if you are passing values from one state to another to use 'data' instead of appending everything in the URL.
The following code should work :
//The following values are default values of the parameters
.state('locaties.detail', {
params: {
locatieID: '1',
locatieName: 'Derptown',
locatieLat: '50',
locatieLong: '50'
}, ........
This should work. The values expected are of string type and not number.
As far as your LocatieDetailCtrl is concerned, you need to inject what you have in the resolve of the 'locaties.detail' state (i.e. 'locatiedetail'). So your 'LocatieDetailCtrl' should look like following:
angular.module('PremiumMeat').controller('FranchisesDetailCtrl',
function ($scope, $window, franchisedetail, locatiedetail) {
$scope.franchiseDetail = franchisedetail; //make sure you have franchiseDetail as well.
$scope.locatiedetail = locatiedetail;
});
I hope that will work.

Angular UI Router Reload Controller on Back Button Press

I have a route that can have numerous optional query parameters:
$stateProvider.state("directory.search", {
url: '/directory/search?name&email',
templateUrl: 'view.html',
controller: 'controller'
When the user fills the form to search the directory a function in the $scope changes the state causing the controller to reload:
$scope.searchDirectory = function () {
$state.go('directory.search', {
name: $scope.Model.Query.name,
email: $scope.Model.Query.email
}, { reload: true });
};
In the controller I have a conditional: if($state.params){return data} dictating whether or not my service will be queried.
This works great except if the user clicks the brower's forward and/or back buttons. In both these cases the state (route) changes the query parameters correctly but does not reload the controller.
From what I've read the controller will be reloaded only if the actual route changes. Is there anyway to make this example work only using query parameters or must I use a changing route?
You should listen to the event for succesful page changes, $locationChangeSuccess. Checkout the docs for it https://docs.angularjs.org/api/ng/service/$location.
There is also a similar question answered on so here How to detect browser back button click event using angular?.
When that event fires you could put whatever logic you run on pageload that you need to run when the controller initializes.
Something like:
$rootScope.$on('$locationChangeSuccess', function() {
$scope.searchDirectory()
});
Or better setup like:
var searchDirectory = function () {
$state.go('directory.search', {
name: $scope.Model.Query.name,
email: $scope.Model.Query.email
}, { reload: true });
$scope.searchDirectory = searchDirectory;
$rootScope.$on('$locationChangeSuccess', function() {
searchDirectory();
});
Using the above, I was able to come up with a solution to my issue:
controller (code snippet):
...var searchDirectory = function (searchParams) {
if (searchParams) {
$scope.Model.Query.name = searchParams.name;
$scope.Model.Query.email = searchParams.email;
}
$state.go('directory.search', {
name: $scope.Model.Query.name,
email: $scope.Model.Query.email,
}, { reload: true });
};...
$rootScope.$on('$locationChangeSuccess', function () {
//used $location.absUrl() to keep track of query string
//could have used $location.path() if just interested in the portion of the route before query string params
$rootScope.actualLocation = $location.absUrl();
});
$rootScope.$watch(function () { return $location.absUrl(); }, function (newLocation, oldLocation) {
//event fires too often?
//before complex conditional was used the state was being changed too many times causing a saturation of my service
if ($rootScope.actualLocation && $rootScope.actualLocation !== oldLocation && oldLocation !== newLocation) {
searchDirectory($location.search());
}
});
$scope.searchDirectory = searchDirectory;
if ($state.params && Object.keys($state.params).length !== 0)
{..call to service getting data...}
This solution feels more like a traditional framework such as .net web forms where the dev has to perform certain actions based on the state of the page. I think it's worth the compromise of having readable query params in the URL.

How to implement path aliases in ui-router

I'm trying to find a way to implement route aliases in Angular.js using ui-router.
Let's say I have a state with the url article/:articleId and I want to have /articles/some-article-title redirected (internally) to /article/76554 without changing the browser location.
Could this be done with ui-router?
I. Doubled state mapping (reuse of controller, views)
NOTE: This is original answer, showing how to solve the issue with two states. Below is another approach, reacting on the comment by Geert
There is a plunker with working example. Say we have this two objects (on a server)
var articles = [
{ID: 1, Title : 'The cool one', Content : 'The content of the cool one',},
{ID: 2, Title : 'The poor one', Content : 'The content of the poor one',},
];
And we would like to use URL as
// by ID
../article/1
../article/2
// by Title
../article/The-cool-one
../article/The-poor-one
Then we can create this state definitions:
// the detail state with ID
.state('articles.detail', {
url: "/{ID:[0-9]{1,8}}",
templateUrl: 'article.tpl.html',
resolve : {
item : function(ArticleSvc, $stateParams) {
return ArticleSvc.getById($stateParams.ID);
},
},
controller:['$scope','$state','item',
function ( $scope , $state , item){
$scope.article = item;
}],
})
// the title state, expecting the Title to be passed
.state('articles.title', {
url: "/{Title:[0-9a-zA-Z\-]*}",
templateUrl: 'article.tpl.html',
resolve : {
item : function(ArticleSvc, $stateParams) {
return ArticleSvc.getByTitle($stateParams.Title);
},
},
controller:['$scope','$state','item',
function ( $scope , $state , item){
$scope.article = item;
}],
})
As we can see, the trick is that the Controller and the Template (templateUrl) are the same. We just ask the Service ArticleSvc to getById() or getByTitle(). Once resolved, we can work with the returned item...
The plunker with more details is here
II. Aliasing, based on native UI-Router functionality
Note: This extension reacts on Geert appropriate comment
So, there is a UI-Router built-in/native way for route aliasing. It is called
$urlRouterProvider - .when()
I created working plunker here. Firstly, we will need only one state defintion, but without any restrictions on ID.
.state('articles.detail', {
//url: "/{ID:[0-9]{1,8}}",
url: "/{ID}",
We also have to implement some mapper, converting title to id (the alias mapper). That would be new Article service method:
var getIdByTitle = function(title){
// some how get the ID for a Title
...
}
And now the power of $urlRouterProvider.when()
$urlRouterProvider.when(/article\/[a-zA-Z\-]+/,
function($match, $state, ArticleSvc) {
// get the Title
var title = $match.input.split('article/')[1];
// get some promise resolving that title
// converting it into ID
var promiseId = ArticleSvc.getIdByTitle(title);
promiseId.then(function(id){
// once ID is recieved... we can go to the detail
$state.go('articles.detail', { ID: id}, {location: false});
})
// essential part! this will instruct UI-Router,
// that we did it... no need to resolve state anymore
return true;
}
);
That's it. This simple implementation skips error, wrong title... handling. But that is expected to be implemented anyhow... Check it here in action

Resources