How to change order in ui-router? - angularjs

I have a cenario where I have a list and want to reorder it. I'm still new on ui-router and I couldnt figure out how to do it.
My states is something like this (with resolve to pass the data):
$stateProvider
.state('shoppings-view', {
url: '/shoppings/:id',
templateUrl: 'shoppings/partial/shoppings-view/shoppings-view.html'
}).state('shoppings-view.order', {
url: '/:order',
templateUrl: 'shoppings/partial/shoppings-view/shoppings-view.html'
});
And here my controller:
angular.module('shoppings').controller('ShoppingsViewCtrl',function($scope, $stateParams){
$scope.order = $stateParams.order != undefined ? $stateParams.order : 'id';
}
And a simple view with use a ng-repeat to show them.
The problem is: if I'm in the url: /shoppings/1 and change the link to /shoppings/1/date the controller is not recalled and I can't change the order. So, how can I do something like that?

This scenario with ui-router could have two (if not even more) solutions. Check both of them here in this working example. We can firstly continue with your Paren-Child scenario, we just have to do few changes
// Parent - Child Scenario
.config(['$stateProvider',
function($stateProvider) {
// parent child
$stateProvider
.state('shoppings-view', {
url: '/shoppings/:id',
templateUrl: 'tpl.shoppings-view.html',
controller: 'ParentCtrl',
})
.state('shoppings-view.order', {
url: '/:order',
template: '<div>some child content if needed</div>',
controller: 'ChildCtrl',
});
}])
.controller('ParentCtrl', function($scope, $state, $stateParams, DataSvc) {
$scope.data = [];
// parent declares method on a $scope
$scope.load = function(params){
$scope.data = DataSvc.getAll(params)
}
$scope.load($stateParams);
})
.controller('ChildCtrl', function($scope, $state, $stateParams) {
// child scope has inherit that function
// and calls the parent to relaod with new params
$scope.load($stateParams);
})
What we can see here, is that child gets the $scope inherited (see Understanding Scopes) and therefore has access to a parent method $scope.load($stateParams);. Whenever there is new child state with new param invoked, it calls parent to relaod the data.
Maybe not the best here, but the concept of published methods on parent $scope, available for a child(ren) is a feature I do use a lot...
Second approach could be to move all that stuff into one simple state, with more params:
// scenario with Single state
.config(['$stateProvider',
function($stateProvider) {
// single state
$stateProvider
.state('shoppings', {
url: '/shoppings-single/:id/:order',
templateUrl: 'tpl.shoppings-single.html',
controller: 'SingleCtrl',
resolve : {
data : function($stateParams, DataSvc){
return DataSvc.getAll($stateParams)
}
}
});
}])
.controller('SingleCtrl', function($scope, $state, $stateParams, data) {
$scope.data = data;
$scope.orderBy = $stateParams.order;
})
There is nothing special, just we can see that one state can have more params (see URL Parameters)
All that together check here

You have the same template for the parent and child state, which doesn't make much sense. Are you nesting an identical template inside itself?
A child state includes its parent state. So anything in the parent state template is included in the child state.

Related

angular ui-router state how is controller being called

I have see people post things online about using Angular ui-router and i see these types are code that do not explicitly even show an controller name, so how is it even suppose to know how to call the controller?
state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.portfolioId;
}
})
Code of mine that I TRIED that does NOT list the controller like above
.state("deviceDetail", {
url: "/devices/:DeviceId", // param is required which specific device id
templateUrl: "app/devices/deviceDetailView.html", // ui elements
controller: function($scope,$stateParams) {
$scope.DeviceId = $stateParams.DeviceId;
}
});
Problem is the controller is NOT hit
But this code explicitly uses the controller name and the controller gets hit, thus i'm having trouble with how this type of code would hit a controller
controller: function($scope,$stateParams) {
$scope.DeviceId = $stateParams.DeviceId;
}
HOW does it know? (doesn't work for me )
You can declare controllers in two ways in UI router states:
//1
.state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.portfolioId;
}
})
//2
.state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: PortfolioController
})
.
.
.
//PortfolioController definition
In method #1, your controller is created for you inline, while in #2, UI Router is looking for an explicitly declared controller called PortfolioController

Return value from the controller of child modal to the controller of parent modal

I have two states in my application. In each of these states I open a modal dialogs, that has their own controllers: parentCtrl and childCtrl. I wanna return to the parent modal in select(config) function and return config value to the parent state, just into parentCtrl.
$stateProvider.state('parent', {
url: "...",
onEnter: function ($stateParams, $state, $uibModal) {
$uibModal.open({
templateUrl: '...',
controller: function ($scope, $uibModalInstance) {
...
},
controllerAs: 'parentCtrl'
});
}
});
$stateProvider.state('parent.child', {
url: "...",
onEnter: function ($stateParams, $state, $uibModal) {
$uibModal.open({
templateUrl: '...',
controller: function ($scope, $uibModalInstance) {
this.select = function (config) {
debugger;
alert("Hall:"+ config.hallName+", configuration:"+ config.name+", configId: "+ config.id);
$uibModalInstance.close({data: config});
};
},
controllerAs: 'childCtrl'
}).result.finally(function () {
debugger;
$state.go('^');
});
}
});
For bootstrap modals, the modal scope will be a child of the controller's scope and in angular scope are chained.
So if you initialize your parent controller with :
$scope.modal = {};
$scope.modal.newData = function(data){};
You should be able to do in modal controller :
$scope.modal.newData (data);
Note : the intermediary object modal is because of the limit of scope inheritance, you may have not problem with this javascript but you may have with templating so i always use interdiary objects when playing around with scope inheritance.
EDIT : didn't see it was for 2 independant modals. The best would be to use what i post and to close and open again the parent modal from parent scope data in order to refresh it.
Otherwise you can emit/listen for events in angularjs using $scope/$rootScope.$on/$emit.
For this kind of stuff, use $rootScope.$on to listen, and $rootScope.emit to send event.
The result of your child modal can pass parameters.
.result.then(function (data) {
$state.go('^', data);
});
this data is the parameter you entered in the .close() operation. You can catch those params in your state config, through adding the following on your parent state definition
params: {
data: {}
}

Inject a resolve provider from child state to controller

So i'm trying to pull some id from the url parameters.
Here are my states :
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'path/to/parent.html',
controller: 'lolController'
})
.state('parent.child', {
url: '/child-profile/:id',
templateUrl: 'path/to/child.html',
resolve: {
someId: function ($stateParams) {
// I cant print the id from here
console.log("PARAMS", $stateParams.id)
return $stateParams.id;
}
},
})
Controller
.controller('lolController',
['$scope', 'someId', function ($scope, someId) {
$scope.someId = someId;
}])
But whenever i'm trying to access the url /parent/child-profile/123abc i'm getting the error Unknown provider: someId See error here..
How do I fix this? Thanks.
EDIT
The answer provided by Jay Shukla helped me get this idea.
The parameter is undefined because I declared the controller on the parent state before actually calling the child state which contains the value from it's url. Here's a simple solution I came up with, with Jay Shukla's help.
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'path/to/parent.html',
})
.state('parent.child', {
url: '/child-profile/:id',
templateUrl: 'path/to/child.html',
controller: 'lolController'
})
I removed the controller declaration from the parent state and moved it to the child state. Since in my situation the parent state's template only contains a <div ui-view></div>.
The idea is :
Only state is nested but both html are separate so controller models will not be inherited - Jay Shukla
Each state can have their own controller.
Please add/edit more to improve this question.
Try to inject $stateParams then you will get id in that object.
Like this
.controller('lolController',
['$scope', '$stateParams', function ($scope, $stateParams) {
$scope.someId = $stateParams.id;
}])
You can also defined your parameters in different ways as below
url: '/child-profile/:id', // Inside stateparams you will get id as key
OR
url: '/child-profile/{id}',
OR
url: '/child-profile/:{id:int}', // Id with integer value

Angular ui.router. Deep nested routes

Here is an example to check http://embed.plnkr.co/uVMlkk/preview
When we navigate to 'page2' route there is a 'hey, I'm a subroute' note.
But once we navigate anywhere else that note will disappear forever.
The goal is to make some nested states to be shown right away (as a default ones).
I assume there should be some cases using $state.go(), but can't figure it out so far. Any help is highly appreciated.
State definition snippet:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController'
})
.state('root.page2.tab.subroute', {
url: '',
templateUrl: 'tpl.page2.tab.subroute.html'
})
the content of the 'tpl.page2.tab.subroute.html':
hey, I'm a subroute
related controller:
.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
$scope.tabId = $state.params.tabId;
$state.go('root.page2.tab.subroute');
}])
There is a fixed version.
I removed the url from the 'root.page2.tab.subroute'
.state('root.page2.tab.subroute', {
//url: '',
templateUrl: 'tpl.page2.tab.subroute.html'
})
And because the parent has defined paramater tabId:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController'
})
We have to pass that param inside of the redicrection:
.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
$scope.tabId = $state.params.tabId;
// instead of this
// $state.go('root.page2.tab.subroute');
// we need this
$state.go('root.page2.tab.subroute', $state.params);
}])
Check the working, fixed version here
ANOTHER approach - using redirectTo - there is a working plunker
One way, inspired by this:
Redirect a state to default substate with UI-Router in AngularJS
could be to add a very smart but small redirect code snippet:
.run(['$rootScope', '$state', function($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(evt, to, params) {
if (to.redirectTo) {
evt.preventDefault();
$state.go(to.redirectTo, params)
}
});
}])
And adjust our state like this:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController',
redirectTo: 'root.page2.tab.subroute',
})
Check it here
There is a trick how to handle scenarios:
Parent should trigger some action in case that
it is accessed, or
its reached again, when navigating back from child in a parent state
In that case, we can use the "target (ui-view) for a child" as a place where sits the special view, with special controller. This will be
injected into that position once parent is created and
re-injected into that position again, once child is left. In that case, it will be re-init.
Enough explanation. There is a working plunker. There is adjusted state:
.state('root.page2', {
url: '/page2',
views: {
'content#root': {
templateUrl: './tpl.page2.html',
controller: 'Page2Controller'
},
'#root.page2': {
template: '<div></div>',
controller: 'RedirectorController'
}
}
})
So, now we can do some magic inside of our 'RedirectorController'
.controller('RedirectorController', ['$scope', '$state',
function($scope, $state) {
$state.go('root.page2.tab', { tabId: $scope.activeTabId });
}])
Check it in action here
Read more about what that new view/controller get from the other (Scope Inheritance by View Hierarchy Only) one here
Nested states or views for layout with leftbar in ui-router?
How do I share $scope data between states in angularjs ui-router?

Controller on nested state not getting executed

I have nested states, with the parent and child state having a separate controller. But only the parent state is getting executed.
I have the url structure: #/restaurant/2/our-food
So, I want it to load the restaurant with the ID 2 and then the child controller load the 'our-food' content and take care of some other functions.
My code is:
var app = angular.module("app", ['ui.router']);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url: '/',
controller: function($scope, $stateParams) {
$scope.setRestaurant(0);
}
})
.state('restaurant', {
url: '/restaurant/:restaurantId',
controller: function($scope, $stateParams, $state) {
console.log('first');
if($stateParams.restaurantId > 0) {
$scope.setRestaurant($stateParams.restaurantId);
}
else {
$state.go('home');
}
}
})
.state('restaurant.our-food', {
url: '/our-food',
templateUrl: 'templates/our-food.html',
controller: function() {
console.log('second');
}
});
});
The controller for your 'restaurant.our-food' state is not being executed because its parent state has no template. This means there is no ui-view directive for it to attach its own template and controller. Even if your parent directive doesn't do anything other than setup some state, it needs to provide at the very least a minimal template.
Try adding the following to your 'restaurant' state and see if that makes it work for you:
template: "<div ui-view />"
This is documented in the ui-router docs:
Remember: Abstract states still need their own for their children to plug into. So if you are using an abstract state just to
prepend a url, set resolves/data, or run an onEnter/Exit function,
then you'll additionally need to set template: <ui-view />'.
https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views

Resources