I am working with angular and using ui router. The structure of my views are something like this:
<div ui-view>
<div ui-view="left">
-- list of items
</div>
<div ui-view="right">
-- details of item
</div>
</div>
Now if i select an item from left panel, an api is called to get the details of that item. Now want to load that information in right view.
All the view states have their own controller. Can someone please help me to pass the returned data from api tosecond view?
This is a pretty straightforward and common use case with ui-router. The main concept is of nested states and multiple named-views.
I would set this up with some states that look something like:
$stateProvider
.state('user', {
url: '/user',
views: {
'left': {
templateUrl: '/templates/states/user-list.tpl.html'
},
'right': {
template: '<p>This is default content that would appear in the right view until a user is selected. This is nice, but is completely optional.</p>'
}
}
})
.state('user.details', {
url: '/:userId',
views: {
'right': {
templateUrl: '/templates/states/user-details.tpl.html'
}
}
});
In the user-list template, you would use links something like:
<a ui-sref="user.details({userId: user.id})">{{user.name}}</a>
Because user.details is a child state of user, the user-list stays loaded into the left view while navigating through different users in the right view.
I've had no luck with this particular issue. Everything I've looked up has to do with people having issues 'trying' to refresh the page instead of my issue where it's refreshing every time I go to a certain state. This ONLY happens in IE not chrome.
Code:
app.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('box', {
templateUrl: templatePath + 'box.html',
controller: 'BoxController',
controllerAs: 'box',
url: '/Box/{folder:string}'
})
.state('compose', {
templateUrl: templatePath + 'compose.html',
controller: 'ComposeController',
controllerAs: 'compose',
url: '/Compose/{messageSentId:int}'
})
.state('view', {
templateUrl: templatePath + 'view.html',
controller: 'ViewController',
controllerAs: 'view',
url: '/Box/{folder:string}/{messageId:string}/{messageSentId:string}'
});
$urlRouterProvider.otherwise('/Box/');
});
The problem is with my compose state. If I go directly to that state like so:
vm.compose = function () {
$state.go('compose', { messageSentId: 0 }, { notify: true });
};
It works just fine. However, if I add a different number besides 0 and call from a different controller it does not work:
vm.reply = function() {
var id = Math.floor($stateParams.messageSentId);
$state.go('compose', { messageSentId: id }, { notify: true });
};
I thought maybe it was having issues because it thought id was a string and therefore didn't match to /Compose/{messageSentId:int} so I added the Math.floor() but that didn't help.
Something to note is that if I fire my compose function first and go to that state the reply function will work. However, if I attempt to navigate with my reply function first the page reloads.
Another thing that I can confirm is that my controller for that page and the page itself loads just fine. You can actually see the form pop up briefly. The problem is once the controller has loaded a refresh is triggered. No errors. No nothing. Simply fails in IE.
After many hours of research and having found nothing similar to my issue on the web I made a simple change. I moved my state change out of my controller and used ui-sref instead:
Code:
<div class="btn-group pull-right" ng-if="view.canReply()">
<a class="btn btn-primary" ui-sref="compose({messageSentId: view.replyMessageId})">
<i class="fa fa-reply"></i> Reply
</a>
</div>
Where 'view' is my controllerAs since I don't use $scope and I set my $stateparameter on the variable replyMessageId. Maybe this will help someone.
I am trying to use the Same State for managing all the Menu Items and the state is not being refreshed properly.
My main page has the following four sections.
<div ui-view="navbar" ng-cloak="" class="codrops-top"></div>
<div ui-view="leftnavbar"></div>
<div class="well" ui-view="content">
</div>
<div class="footer">
</div>
And i have configured Angular Module to handle the top navbar and the left nav bar using
$stateProvider.state('site', {
'abstract': true,
views: {
'navbar#': {
templateUrl: 'scripts/components/navbar/navbar.html',
controller: 'NavbarController'
}
,
'leftnavbar#' :{
templateUrl:'scripts/components/leftnavbar/leftnavbar.html',
controller: 'LeftNavbarController'
}
}
And i am trying to refresh the view "content" when a left nav item is clicked.
My Leftnav Controller has the following handler for nav item click.
$scope.manageEntity = function(code) {
// I am passing the code of the item clicked using a Service.
ElementModel.code = code;
$scope.toggle();
$state.go('instance',{reload: true});
};
And here is the code i am using for defining the state 'instance'
.config(function ($stateProvider) {
$stateProvider
.state('instance',{
parent: 'site',
url: '/instance',
views: {
'content#': {
templateUrl: 'scripts/app/entities/instance/instances.html',
controller: 'InstanceController'
}
}
How ever the first time the InstanceController is getting invoked and from then even though i am clicking on other left nav items the view is not getting refreshed. control is reaching $state.go('instance',{reload:true}); how ever it's not entering the InstanceController.
Not sure what i am doing wrong..
Appreciate your help.
Thanks.
In your handler function $scope.manageEntity" the call to $state.go should probably be like
$state.go('instance',{},{reload: true});
I am using Angular UI Router and seem to be experiencing an odd issue. When I click a link that has a ui-sref directive attached to it, it successfully loads the new view as I would expect, HOWEVER, it does not update the URL bar. I believe this is ocurring because the parent state's url is a dynamic StateParam /:room. How do I get around this issue?
Here is a snippet of my UI Router
// Room
.state({
name: 'room',
url: "/:room",
views: {
"main": {
templateUrl: "views/pages/chat.html",
controller: "RoomCtrl"
},
"login#room": {
templateUrl: "views/partials/_login.html"
},
"navigation#room": {
templateUrl: "views/partials/_navigation.html",
controller: "NavigationCtrl"
}
},
resolve: {
userLocation: function(geolocationFactory) {
return geolocationFactory;
}
}
})
// Share
.state({
name: 'room.share',
url: "/share",
views: {
"share#room": {
templateUrl: "views/partials/_share.html",
controller: "ShareCtrl"
}
}
});
ui-sref
<button id="share-button" ui-sref="room.share">Share</button>
I created a plunker to demonstrate what is happening
So we can navigate among rooms like this:
<a ui-sref="room({room:1})">room 1</a>
<a ui-sref="room({room:2})">room 2</a>
<a ui-sref="room({room:3})">room 3</a>
this will in fact creat the url like this
#/1 // where 1 represents the id of the :room
#/2
#/3
Now, we can navigate to the substate .share without specifying the :room id
<a ui-sref="room.share">room.share</a>
And what will happen? Firstly the place for :room will be empty ... no room is selected.
Secondly - the previously selected room (its :room id) won't be changed. So the resulting URL will depend on the already selected room. If we were in a room 2, the generated url will be:
#//share
but we will be redirected to
#/2/share
becuase there is still $stateParams.room === 2
Finally, we should always pass the complete state signature:
<a ui-sref="room.share({room:1})">room.share({room:1})</a>
<a ui-sref="room.share({room:2})">room.share({room:2})</a>
<a ui-sref="room.share({room:3})">room.share({room:3})</a>
Check that all here (click the top right corner blue icon to open the prview in sepearate window with url)
I had this problem because my parameter names did not match my URL definition.
For example, I had a parameter set like this:
{
page: {...},
results: {...},
sort: {...},
}
...and a URL like this:
url: '/foo?page&results&orderby'
After fixing the mismatch between sort and orderby, the address bar updated as expected.
The FAQ for ui-router has a section about integration with bootstrap $modals, but it doesn't mention anything about abstract views. I have 3 views under a single abstract view, so something like the following.
$stateProvider
.state('setup', {
url: '/setup',
templateUrl: 'initialSetup.html',
controller: 'InitialSetupCtrl',
'abstract': true
})
// markup for the static view is
<div class="wizard">
<div ui-view></div>
</div>
.state('setup.stepOne', {
url: '/stepOne',
controller: 'SetupStepOneCtrl',
onEnter: function($stateParams, $state, $modal) {
$modal.open{
backdrop: 'static',
templateUrl: 'setup.stepOne.html',
controller: 'SetupStepOneCtrl'
})
}
})
.state('setup.stepTwo', {
url: '/stepTwo',
controller: 'SetupStepTwoCtrl',
onEnter: function($stateParams, $state, $modal) {
$modal.open({
backdrop: 'static',
templateUrl: 'setup.stepTwo.html',
controller: 'SetupStepTwoCtrl'
})
}
})
.state('setup.stepThree', {
url: '/stepThree',
templateUrl: 'setup.stepThree.html',
controller: 'SetupStepThreeCtrl'
...
});
}]);
I've also tried to only add the onEnter block to the abstract state, and removed onEnter from each of the 3 child states. This actually seems to me like the right approach. The abstract state initializes and opens the $modal and the subsequent states should interpolate into , but when I tried this the ui-view container was empty.
I can think of some other hacky ways to workaround this but thought I'd ask to see if there's a canonical way of handling this.
Alternative way is to use ng-switch with ng-include combination inside $modal controller to dynamically load wizard step templates, that is if you don't mind sharing the same controller for all wizard steps:
<div ng-switch="currentStep.number">
<div ng-switch-when="1">
<ng-include src="'wizardModalStep1.html'"></ng-include>
</div>
<div ng-switch-when="2">
<ng-include src="'wizardModalStep2.html'"></ng-include>
</div>
<div ng-switch-when="3">
<ng-include src="'wizardModalStep3.html'"></ng-include>
</div>
</div>
Here is Plunker with working example: http://plnkr.co/edit/Og2U2fZSc3VECtPdnhS1?p=preview
Hope that helps someone !!
I used following approach to develop a wizard. this might be help for you.
I used states like below sample with parent property.
var home = {
name: 'home',
url: '/home',
controller: 'MainController',
templateUrl: '/html/main.html'
},
sampleWizard = {
name: 'sampleWizard',
url: '/sampleWizard',
controller: 'sampleWizardController',
templateUrl: '/html/sd/sample/sampleWizard.html'
},
sampleSectionOne = {
name: 'sampleSectionOne',
url: '/sampleSectionOne',
parent: sampleWizard,
controller: 'sampleSectionOneController',
templateUrl: '/html/sd/sample/sampleSectionOne.html'
},
sampleSectionTwo = {
name: 'sampleSectionTwo',
url: '/sampleSectionTwo',
parent: sampleWizard,
controller: 'sampleSectionTwoController',
templateUrl: '/html/sd/sample/sampleSectionTwo.html'
};
$stateProvider.state(home);
$stateProvider.state(sampleWizard);
$stateProvider.state(sampleSectionOne);
$stateProvider.state(sampleSectionTwo);
I'm not sure you want to fire the modal every single time you go to the next step.
I think all you have to do is create a modal view () then each step has a modal a templateUrl assigned to it.
each template should look like:
<div class="modal fade in" id="whatever" style="display:block">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<a ui-sref="next_page_route_id" type="button" class="btn btn-primary">Next</a>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal-backdrop fade in"></div>
On the last screen you can add a data-dismiss="modal" to the submit and you are done
I have dealt with similar scenario, where I had to create a wizard (which allows you to go through steps and finally hit on summary and save).
For this, I had created different views but with one controller.
Since controller scope dies after each re-routing, I had to save the scope of controller(Basically the model object associated with the wizard) in a service object for each routing in the wizard (Captured through location.path()) and load this object back from service object on load of controller.Something like below:-
// Saving data on routing
$scope.nextPage = function () {
service.ModelForWizard = $scope.ModelForWizard;
switch ($location.path()) {
case RouteOfPage1:
//Do some stuff
break;
case RouteOfPage2:
//Do some stuff
break;
default:
}
Service or factory is persisted throughout life time of user session and is ideal to hold user data.
One more thing which was helpful , was use of 'Resolve' in the routes.
It ensures that next page in not rendered until the required data(generally lookup data) is not loaded. Code of resolve is something like this:
.when('/RouteForWizardPage1', {
templateUrl: '/templates/ViewPage1Wizard.html',
caseInsensitiveMatch: true,
controller: 'wizardController',
resolve: {
wizardLookupDataPage1: function (Service) {
return service.getwizardModelLookupDataPage1().$promise;
}
},
})
I was running into the same thing.. What worked for me was emptying the url property of the first sub state. Hence for the first sub state, your code should look as follows:
.state('setup.stepOne', {
url: '',
controller: 'SetupStepOneCtrl',
onEnter: function($stateParams, $state, $modal) {
$modal.open{
backdrop: 'static',
templateUrl: 'setup.stepOne.html',
controller: 'SetupStepOneCtrl'
})
}
})
Also, incase you're not using the url property to call the other 3 sub states, and are calling them using the state name only, you don't necessarily need to mention a url property for them.
If you want to show a wizard in a modal dialog and have a separate state for each of the wizard's steps, you need to keep in mind that the modal dialog is rendered completely outside your view hierarchy. Therefore, you cannot expect any interaction between ui-router's view rendering mechanisms and the dialog contents.
A robust solution is to put the wizard contents manipulation logic onto the parent state scope
$scope.wizard = {
scope: $scope.$new(),
show: function (template) {
// open the $modal if not open yet
// purge scope using angular.copy()
// return $scope.wizard.scope
},
// private
open: function () {
$modal.open({
scope: $scope.wizard.scope,
// ...
});
}
};
and then manually show the appropriate content from each of the sub-states and manipulate the wizard's scope as needed
$scope.wizard.show('someTemplate');
$scope.wizard.scope.user = ...;
When we faced this problem in our project, we decided after some discussion, that we didn't actually need separate ui-router states for the wizard steps. This allowed us to create a wizard directive used inside the dialog template to read wizard configuration from scope (using a format similar to ui-router state definition), provide methods to advance the wizard, and render appropriate view/controller inside the dialog.
To create multi-step wizards, you can use this module (I am the author): https://github.com/troch/angular-multi-step-form.
It allows you to create steps like views, and you can enable navigation (back / forward button, url with an URL search parameter). Examples are available here.