Build ui.sref for a child state of a child state - angularjs

What I am trying to achieve:
In the template of the main state (/model), I have links to a child state phase (/phase), but I also want to be able to link from the main state directly to a given child state of a given phase (/phase/1/task1).
My config:
$stateProvider
.state('model', {
url: '/model',
views: {
'': {
templateUrl: 'model.tpl.html',
controller: 'modelController'
}
},
})
.state('model.phase', {
url: '/phase/{phaseId:int}',
views: {
'#main.model': {
templateUrl: 'model.phase.tpl.html',
controller: 'phaseController'
}
}
})
.state('model.phase.task', {
url: '/task/{taskId:int}',
controller: 'taskController'
})
};
In model.tpl.html I list out all the phases with general information, and for each phase, I list out tasks from that phase like so:
<div ng-repeat="task in tasks | filter:{primary:true, knowledgeAreaId:area.id, phaseId:phase.id}:true" class="task" ng-class="{ active: task.active }">
<div class="task__content" ui.sref="LINKHERE"></div>
</div>
And it is here I want to build a link that goes directly from the main model page to a given child page for a given phase.
What I have tried in model.tpl.html is to add ui.sref like so
<a ui.sref=".phase({ phaseId: task.phaseId}.task({ taskId: task.taskId})">
But that gives me an error, so it seems that you cant 'chain' states with properties.
If I only wanted to link to a phase, then the following works just fine:
<a ui.sref=".phase({ phaseId: task.phaseId}">
Is there any 'correct' soloution in ui.router for this scenario, or do I have to manually build a string for the href value. That way I change the url to the route I want instead of changing to a given state. It seems wrong to do that, when everything else works by switching states.
Something like:
http://localhost/#/model/phase/1/task1

This should work:
ui.sref=".phase.task({ phaseId: task.phaseId, taskId: task.taskId })"

Related

parameterised first level state in ui route

I am building a application which has different modules. and two modules can have same pages. So based on url i am making the appropriate ajax call to load data. So I am tring to setup my states in below way:
$stateProvider.state('login', {
url: '/login',
templateUrl: 'login.html',
controller: 'LoginController as LoginController'
}).state('logout', {
url: '/logout',
templateUrl: '',
controller: 'LogoutController as LogoutController'
}).state('module', {
url: '/:module',
params: {
module: DataService.getCurrentModule()
}
}).state('module.cover', {
url: '/cover',
templateUrl: 'cover.html',
params: {
module: 'leads'
}
}).state('module.leads', {
url: '/leads',
templateUrl: 'leads.html',
controller: 'LeadsController as ctrl',
abstract: true
})
Given that at the time of login I will fetch all modules and save it in DataService, which is happening. Then after login two things will be done. One navigation urls which i have formatted in below way:
<a href={'#/'+ module.code +"/" + (menu.type|| menu)}>
<i className={classes}></i> <span >{menu.name || menu }</span>
</a>
which is setting the correct url, and second in app.js in "run" I am checking if login is done them I am doing :
$location.path(DataService.getCurrentModule() + "/" + (home.type || home) );
which is also happening, but issue is desired controller and html page is not being loaded. Am I missing something here. Or should I have done things little differently?
Thanks for help in advance.
Avoid href when working with ui.router. To navigate to the required states use:
In HTML: ui-sref="myStateName({param1: 1, param2: 2})"
In Javascript inject the service $state and do: $state.go('myStateName', {param1: 1, param2: 2});
In your case, lets assume that there are 2 modules in an array in the $scope:
$scope.myModules = [{code: 'modA'},{code: 'modB'}];
Now in the HTML, to go to the module.cover state you would do:
<a ui-sref="module.cover({module: myModules[0].code})">My Link</a>
If you want to do it for all modules, put it inside an ng-repeat:
<a ng-repeat="mod in modules" ui-sref="module.cover({module: mod.code})">My Link</a>
Also, for state configuration, consider:
ALL STATES NEED A TEMPLATE: even if they are abstract states, they require a template to work properly. If the parent state doesn't have a template, not even one of its childs is gonna show. In this case, the state module doesn't have a template, so it will never work. define a template for it as simple as template: '<div ui-view></div>'
When you define a parameter in the URL, there's no need to define it again in with a params property. That is used only when you need parameters that you don't want to show in the URL

UI Router update parent view from child

I'm new to using Angular UI Router and I seem to be having difficulty being able to update a parent view from it's child view.
I have the following HTML structure (restructured for easier reading, obviously views are in separate html files).
<div ui-view="main">
{ main content }
<div ui-view="tab">
{ tabbed content }
</div>
</div>
Inside tab I have the following sref:
<span ui-sref="silverstone.platforms.view({_id: platform._id})">{{platform.name}}</span>
And here are my states: (I'm using webpack, hence require)
$stateProvider
.state('silverstone', {
url: '/silverstone',
views: {
'main': {
controller: 'SilverstoneCtrl',
template: require('./templates/index.html')
}
}
});
$stateProvider
.state('silverstone.platforms', {
url: '/platforms',
views: {
'tab': {
controller: 'SilverstoneCtrl',
template: require('./templates/platforms.html')
}
}
});
$stateProvider
.state('silverstone.platforms.view', {
url: '/:_id',
views: {
'main': {
controller: 'SilverstoneCtrl',
template: require('./templates/platform-view.html')
}
}
});
When the above sref is clicked, the "main" view needs to be updated. The URL is updating but the views aren't...?
I was missing # in my silverstone.platforms.view state to explicitly address the parent view.
$stateProvider
.state('silverstone.platforms.view', {
url: '/:_id',
views: {
'main#': {
controller: 'SilverstoneCtrl',
template: require('./templates/platform-view.html')
}
}
});
Use reload: true parameter in yout ui-sref links like this.
ui-sref="silverstone.platforms.view({_id: platform._id})" ui-sref-opts="{reload: true}"
Edit:
Maybe your problem are caused by nested states combined with flat template structure.
Other solution may be to properly nest your templates, as states.
For example we have states app and app.substate, then we have two templates for both states. Tempalte of app state contains ui-view directive. (that means every state contains new ui-view directive for injecting of substate template). States are nested by default, this would represent appropriately nested templates.

How to use AngularJS's ui.router to implement a create/edit tabbed form?

I created a tabbed edit form like this:
<ul class="nav nav-tabs">
<li role="presentation" ui-sref-active="active">
<a ui-sref="products.edit.general">General</a>
</li>
... more tabs ...
</ul>
My routes look like:
$stateProvider
.state('products.edit', {
abstract: true,
url: '/{product_id:int}/edit',
templateUrl: 'partials/products.edit.html',
controller: 'ProductsEditController'
})
.state('products.edit.general', {
url: '',
templateUrl: 'partials/products.edit.general.html'
})
I have several files like partials/products.edit.general.html for each tab. each of them contains different form fields.
Now, I'm looking to extend this code to create products using the same form fields and just switching between POST/PUT on the controller. To do so, I created the products.new set of routes, but I'm failing to implement them on the HTML above. Or should I set up my .new rules differently?
You can pass data or promises to the controller from the .state configuration as follows
.state('products.edit', {
abstract: true,
url: '/{product_id:int}/edit',
templateUrl: 'partials/products.edit.html',
controller: 'ProductsEditController',
resolve: {
action: function () {
return 'edit';
}
}
});
This will resolve as a dependency (or dependencies if multiple properties used in resolve) of the controller:
angular.module('myApp').controller(function( action, /*other dependencies...*/){
if(action === 'edit'){
// editing specific code or variables
}else{
// create specific code
}
});
See UI Router docs for more details

AngularJS UI Router: ui-sref not updating URL in address bar because parent state has StateParams?

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.

ui-sref with multiple parameters to child view not working

I'm using Angular's ui-router on my application to try and route to child views of a main view. Both the main and the child have their own associated IDs. Currently I can navigate to the parent, but my link to the child is not working.
In my Application.js
$stateProvider
//Working Route
.state('Project', {
url: '/Project/{projectId}',
views: {
"ContentMain" : {
templateUrl: "/Scripts/Dashboard/templates/MainContent/ProjectMainContent.html",
controller: function ($stateParams) {
console.log("Project state hit!");
}
},
...
}
})
//Non-Working Route
.state('Project.ViewResource', {
url: '/Resource/:resourceId',
parent: 'Project',
views: {
"ContentMain" : {
templateUrl: "/Scripts/Dashboard/templates/MainContent/ProjectResourceViewContent.html"
controller: function ($stateParams) {
console.log("Project.ViewResource state hit!");
}
},
...
}
});
In my HTML:
<!-- Working Link-->
<a ui-sref="Project({ projectId: 5 })"><h3> My Projects </h3></a>
<!-- Non-working Links -->
<a ui-sref="Project.ViewResource({ projectId: 5, resourceId: 3 })">View Project Resource. </a>
<a ui-sref="Project.ViewResource({ resourceId: 3})">I'm a Resource Image. </a>
The first link works, however when I click either of the "non-working" child links my browser updates to: "Home/Index/#/Project/5/Resource/3" which is the desired route, however the page content
Any idea where I'm going wrong?
Edit1: To add the lines of code in the 'views' object which should be swapping out.
Edit2: To further demonstrate the issue, I've added the controller code blocks. When I hit the first html link, my console outputs "Project state hit!" When I click either of the non-working links, there is no new output to the console. Ie, the route is likely not being hit.
Figured out what was happening. After taking a closer look at the document on multiple named views here, I realized that my child view was searching for ui-view tags within the parent template, rather than the root template. Essentially, my child was trying to nest within my parent, while my desired behavior was to replace the parent views.
So, to target ui-views within the root, my solution looked like:
.state('Project.Resource', {
url: '/Resource/{resourceId}',
parent: 'Project',
views: {
"MainControls#": { templateUrl: "/Scripts/Dashboard/templates/MainControls/MainControls.html" },
"ContentMain#": {
...
},
...
}
})

Resources