UI to edit a list item within the current template - angularjs

My UI includes nested tabs (using Bootstrap). The tab in question is a template with its own controller, currently specified on a view with UI-Router:
.state('editBase.edit', {
url: '/:id',
views: {
'fooTab#editBase.edit': {
templateUrl: 'foo.html',
controller: 'FooController'
},
// more views...
The template uses ng-repeat to create a list of links. Each link goes to an editor child state:
<a ui-sref="editBase.edit.subEdit({itemId: item.id})">{{item.name}}</a>
What I want to happen when the link is clicked is for the contents of the tab (the list) to be replaced with the editor. On clicking a save button, the list would reappear refreshed.
I'm not looking for an in-place editor for a list; I want to hide the entire list, but leave the surrounding templates/tabs intact. In other words, I don't want to replace the root view. How can I do this?
Note: I found this post that explains multiple nested views and view names; this helped me a lot.

In the edit state, create a view that targets the list's <ui-view>
A parent state, just for demonstration. The parent state has a ui-view that the list state plugs into
$stateProvider.state({
name: 'app',
template: '<div ui-view="list"></div>'
});
The list state targets the <ui-view="list">, which was created by the parent state
$stateProvider.state({
name: 'app.list',
url: '/objects',
views: {
list: {
controller: ...
template: `<ul><li ng-repeat="..."></li></ul>
}
}
});
The edit state targets the <ui-view="list"> that was created by the app state. The ui-view was previously filled in by the app.list state. However, the child state's view targeting that ui-view takes precedence over the parent state view.
$stateProvider.state({
name: 'app.list.edit',
url: '/edit/:id',
views: {
"list#app": {
controller: ...
template: "<a ui-sref="^">Go back...</a><form>...</form>"
}
}
}) ;
Here's a working plunker that demonstrates it: http://plnkr.co/edit/Uc39R2ZHUg2Ru3xyEkfe?p=preview

Related

Creating layouts with multiple nested views using angular-ui-router

I have tried several examples on using the ui-router and the state manager. My nested views and routes are not working as I hoped. Here is an example of how I am configuring the states:
$stateProvider
.state("main", {
abstract: true,
url: "/main",
views: {
"layout": {
templateUrl: "main.index.html"
},
"mainNavigation#main": {
templateUrl: "main-navigation-partial.html"
}
},
onEnter: function() {
console.log("enter main");
}
})
.state("main.dashboard", {
url: "/dashboard",
views: {
"container#main": {
templateUrl:"main-dashboard.html"
}
}
});
As you can see, I have an abstract state named main. All main views will use the mainNavigation view. There is also a container view area where the content for each section will reside. There is an index.html that will be used by all states. So, I may, down the road have an abstract state name client with accompanying states and views.
I can see the individual html files being loaded, but the views are not being populated in the correct named view areas. I have created a plunk that demonstrates how I want to manage my templates and views.
Your main state is loading main.index.html into a ui-view named layout. In your plunker, your root ui-view is unnamed. So to fix this, add a name to that ui-view.
http://plnkr.co/edit/xKDcuk99OACQR73LR0hf?p=preview
<div ui-view='layout'>
Or, you could leave the ui-view unnamed and change the view to reflect that.
"": {
templateUrl: "main.index.html"
}
For more on view naming, see the ui-router wiki.

Angular, set ui-view inside ui-view

I have a question about multiple views. It's difficult to explain but I'll try:
Navigating to state 'About' and setting template url for ui-view 'columnOne' and ui-view 'columnTwo' was done succesfully
.state('about', {
url: '/about',
views: {
// the main template will be placed here (relatively named)
'': { templateUrl: 'partial-about.html' },
// the child views will be defined here (absolutely named)
'columnOne#about': { template: 'Look I am a column!' },
// for column two, we'll define a separate controller
'columnTwo#about': {
templateUrl: 'table-data.html',
controller: 'scotchController'
}
}
});
Now I would like to set the templateUrl of view 'columTwo' from view 'columnOne'.
How is this done? Is it possible?
The example your taking from there website is parallel view example not nested view nested view can be made by making a page and than making other states with . like registration is a page and multistep form than the page containing registration uiview will we state registration and the parts with be like registration.contact,registration.acadamics, registration.extraactivities. And really man there documentation is quiet confusing

Nested parameterized routes with ui-router in AngularJS

I believe I'm on the right track with these terms, "nested parameterized routes", but haven't found what I'm looking for yet.
My objective is to create intuitive routes for my API, something like the following example:
/api/v1/project/list
/api/v1/project/1/item/list
/api/v1/project/1/item/1/edit
/api/v1/project/2/item/3/delete
It's relatively easy and clear how to setup project states, but not the item states within each project.
{
state: 'project'
config: {
url:'/project'
}
},
{
state: 'project.list'
config: {
url: '/list'
}
},
{
state: 'project.detail'
config: {
url: '/:project_id'
}
}
It's not clear to me where to go from there so that items are relative or nested within projects.
I'll assume you have a REST api (based on your example containing /api/v1) which you want to expose/parallel as a UI. I'll assume you want to allow the user to drill down some hierarchical data model.
Choices!
There are many ways you could organize your states, for this drill-down list/details pattern. None is the "correct" way, but some are probably better than others. I will highlight two approaches that I've used:
Sibling states for list and details
One approach is to keep the "item list" states and "item details" states as siblings. This is what you did with project.list and project.details. This approach can be seen in the UI-Router Extras Demos source code.
When taking this approach
you must take care to move the user from the list state to the detail state when drilling down.
This approach has the benefit of easy-to-understand nesting of UI-Views. The ui-view for the detail view replaces the ui-view for the list view, when drilling down, because you are navigating to a sibling state.
Your choice whether or not the detail for an entity also retrieves the list of sub-entities (does the detail for a project also show the items list for that product?)
States:
projectlist // template plugs into parent ui-view
projectdetail // template plugs into parent ui-view, replacing projectlist
projectdetail.itemslist // template plugs into parent ui-view (#projectdetail)
projectdetail.itemdetail // template plugs into parent ui-view (#projectdetail), replacing itemslist
Details state as a substate of List state
Another approach is to make the detail state a child of the list state. This is organized similar to your REST routes.
When taking this approach
States hierarchy closely resembles the REST routes being exposed
Drilling down is simple and intuitive
You must manage the visual display of list/detail.
When drilling down from list state to the details substate, you probably want to hide the list.
We use named views, and absolute naming in order to replace the parent list state's template with the template for the the detail state. This is called "view targetting".
States:
top // theoretical parent state
top.projects // lists projects. Plugs into parent ui-view (#top)
top.projects.project // details for project. Its named view targets the grandparent ui-view (#top), replacing the template from top.projects list state.
top.projects.project.items // lists items. Plugs into parent ui-view (#top.projects.project)
top.projects.project.items.item // details for item. Its named view targets the grandparent ui-view (#top.projects.project), replacing the template from top.projects.project.items list state.
Here's an example of using named view targeting to accomplish the second approach:
$stateProvider.state('top', {
url: '/',
template: '<ui-view/>',
});
$stateProvider.state('top.projects', {
url: '/projects',
resolve: {
projects: function(ProjectsRoute) {
return ProjectsRoute.getProjects();
}
},
controller: function($scope, projects) { $scope.projects = projects; },
template: '<li ng-repeat="project in projects"> <ui-view/>'
});
$stateProvider.state('top.projects.project', {
url: '/:projectid',
resolve: {
project: function(ProjectsRoute, $stateParams) {
return ProjectsRoute.getProject($stateParams.projectid);
}
}
views: {
'#top': {
controller: function($scope, project) { $scope.project = project; },
template: 'Project details: {{ project.name }} <a ui-sref=".items">view items</a> <ui-view/>'
}
});
$stateProvider.state('top.projects.project.items', {
url: '/projects',
resolve: {
items: function(ItemsRoute, project) {
return ItemsRoute.getItemsForProject(project.id);
}
},
controller: function($scope, items) { $scope.items = items; },
template: '<li ng-repeat="item in items"> <ui-view/>'
});
$stateProvider.state('top.projects.project.items.item', {
url: '/:itemid',
resolve: {
item: (ItemsRoute, $stateParams) {
return ItemsRoute.getItem($stateParams.itemid);
}
},
views: {
'#top.projects.project': {
controller: function($scope, item) { $scope.item = item; },
template: 'Item details: {{ item.name }}'
}
});
I checked the Github Wiki, the Abstract States is enough.

How do I prevent reload on named view, when state changes? AngularJS UI-Router

I'm using the excellent ui-router module in my application. As part of this, I'm using named views to manage the 'dynamic sub-navigation' I have in the app.
Consider the following:
$urlRouterProvider.otherwise('/person/list');
$stateProvider
.state('person', {
url: '/person',
abstract: true,
})
.state('person.list', {
url: '/list',
views: {
"main#": {
templateUrl: "person.list.html",
controller: 'PersonListController'
}
}
})
.state('person.details', {
url: '/{id}',
views: {
'main#': {
templateUrl: "person.details.html",
controller: 'PersonController'
},
'nav#': {
templateUrl: "person.nav.html",
controller: 'PersonNavController'
}
}
});
When users first visit the app, they are presented with a list of people. When they click on a person, they are taken to the details page. Pretty basic stuff. Here's the markup if it helps...
<div>
<aside ui-view="nav"></aside>
<div ui-view="main"></div>
</div>
However, the PersonNavController calls a REST service to get a list of people, so when viewing a person, the user is able to navigate sibling elements. Using the method above causes the template and controller to re-render, thus causing a delay after every click, despite the content never changing.
Is there a way to keep the 'nav#' view loaded, and only refresh the 'main#' view?
The way I am using ui-router in this scenarios is: move the views to the least common denominator.
Other words: In case that ui-view="nav" is shared among all the details and is the same for all of them (because it should be loaded only once) - it should be part of the list state (parent of the detail state)
the parent state defintion would be adjusted like this:
.state('person.list', {
url: '/list',
views: {
"main#": {
templateUrl: "person.list.html",
controller: 'PersonListController'
}
// here we target the person.list.html
// and its ui-view="nav"
'nav#person.list': {
templateUrl: "person.nav.html",
controller: 'PersonNavController'
}
}
So where is the trick? In the power of the angular ui-router. We can, during each state defintion, target the current view. Now, the nav view is part of the list state definition - i.e. it will not be reloaded during the detail switching (also check here for more explanation)
We just have to use the defined naming conventions, see:
View Names - Relative vs. Absolute Names
Few cited lines from the mentioned documentation:
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '#' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"info#contacts.detail" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : { }

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