How to target specific ui-view elements when nested ui-views exist - angularjs

Imagine some html as follows:
<body ng-app="blocksApp">
Some content goes here
<div ui-view="monty">
<div ui-view="dave">Aaa</div>
<div ui-view="pete">Bbb</div>
<div ui-view="steve">Ccc</div>
</div>
</body>
Using ui-router, is there any way to code a state that will set "dave" to a new snippet of html, whilst leaving everything else untouched.
e.g. I'd like to do this:
$stateProvider
.state('daveonly',{
url: "/dave",
views:{
'dave':{template:"Dave now has content"}
}
})
I can't get it to work. The reason I want to do this is that sometimes I'd like to replace 'Dave' with a partial update, other times I'd like to replace the entire 'monty' with a partial update. It seems that ui-router does not like having nested ui-views in the same snippet of html.

From one point of view I'd like to suggest:
move html code to '.tpl.html' files
use 'templateUrl' instead of 'template'
And check if the following is suitable for you:
$stateProvider.state("daveonly", {
views: {
"dave": {
templateUrl: 'daveonly.tpl.html',
},
"pete": {
templateUrl: 'pete.tpl.html',
},
"steve": {
templateUrl: 'steve.tpl.html',
},
}
});
Take a look at page1 and page2 for more details.
But from another point of view it could be more useful to use only one ui-view and to redesign current ui-views to become the appropriate directives with controllers/services: usage of directives with controllers/services could help to manage partial reload correctly and to write unit-tests.

Yes it can be done easily with the help of abstract states and yes you are correct ui-router doesn't like direct nested views directly but it works fine if the views are in any child template.
Now consider this main page(index.html)
<body ng-app="app">
<div ng-view=""></div>
</body>
Now this template which will appear in this unnamed view. (parent.html)
<h3>This is the parent template</h3>
<div ng-view="child1"></div>
<div ng-view="child2"></div>
Now the JS file
$stateProvide.state('home',{
url:'/',
abstract:true,
views:{
"":{
templateUrl:'parent.html'
}
}
})
.state('home.child',{
url:"",
views:{
'child1#home':{
templateUrl:'child1.html'
},
'child2#home':{
template:'Child2'
}
}
})
.state('home.child.child1',{
url:"child1#home.child",
views:{
'child1#home':{
templateUrl:'child1viewchange.html'
}
}
});
(Now the manipulation part)
(child1.html)
<button ui-sref="home.child.child1">Child</button>
Now child1viewchange.html pe jana padega and wo dekhne wali hai kaunsi kaisi thi/......
(child1viewchange.html)
<h3>Child1's view change</h3>
So now when we click on the button in child view1 the content in the first view changes and if we assign controllers then they can use them to control data.

Related

AngularJS UI Router Update Sibling View

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.

Different view depending on which state

I have an Angular page which has a login page, a register account page, and the rest of the page. Login/register looks different from the other pages, as they have no sidebar, toolbar, and so on. Totally different layout. I have an index page and an index controller, which determines which view to load currently. Something like this:
<body ng-app="myApp" ng-controller="IndexController as vm">
<div ui-view ng-if="$state.current.name === 'home'"></div>
<div ng-if="$state.current.name !== 'home'">
<!--TOOLBAR-->
<!--SIDEBAR-->
<div ui-view></div>
</div>
</body>
So basically if the current state is home, it should load the first view (with no toolbar, sidenav etc.). If the current state is not set to home, it should show the toolbar, sidebar, and so on. I thought this would work, but is there a better way to do it? I have all my states inside my app.js.
Let me know if there's a better way of separating the different views. Why I'm doing this, is because I want to keep all the includes (models, views, controllers) and other scripts on one page (my index.html which contains the above) and just change the view accordingly. Thanks.
Using the controller as syntax, i'm assuming $state isn't defined. You should add it to the controller scope (not $scope) (this.$state = $state) and call it using vm.$state.current....
Ideally you wouldn't do this though and use ui-routers nested states which solves this problem exactly.
https://github.com/angular-ui/ui-router/wiki/nested-states-%26-nested-views
There are a number of ways to do this. One is to fork the states so home does not inherit parent views.
$stateProvider.state('home', {})
.state('app', {}) // has parent views like menu
.state('app.foo', {}) // inherits views from app
Or used named views
<body>
<div ui-view="menu"></div>
<div ui-view="content"></div>
</body>
$stateProvider.state('app' { views: { menu: { ... } } } )
.state('app.home', { views: { 'menu#app': { /* inject empty tpl */ }, 'content#app' : { ... } } )
.state('app.foo', { views: { 'content#app' : { ... } } } );

How to implement two panel layout using Angular Ui-Router?

This is my code
app.config(['$stateProvider', function($stateProvider){
$stateProvider
.state('student',{
url: '/student',
views: {
'list': {
templateUrl: 'list.html',
controller: 'StudentsCtrl'
},
'edit':{
templateUrl: 'edit.html',
controller: 'StudentEditCtrl'
}
}
})
}]);
<html ng-app="app">
<div ui-view="list"></div>
<div ui-view="edit"></div>
</html>
I have layout with two panels side by side,i tried the above code but at the time of page loading two panels html pages displayed at a time. But i want to display first list.html in left side then user clicks add or edit buttons in list.html that time render the edit.html in right side of the panel.
Any reason why you can't combine the HTML and controller together? The StudentCtrl could toggle showing the edit html when the add or edit buttons are clicked.
I can't think of a simple way to make it work with how you have it designed now. You'd have to some coordinating controller in your main HTML and/or some sort of shared Angular service which seems messy.

Angular ui-router parallel view with child states

I've been looking around and perhaps I'm missing something, but I've been totally unable to figure out how to get this working. Basically, I want a state with three parallel views. Lets call them header, body, and footer. Header and footer work just fine as simple parallel views, but I haven't been able to figure out how to automatically render the body child state, so that I can use it to manage other views.
app.js
.state('main', {
url: '/',
views: {
mainModule: { templateUrl: 'partials/main.html'},
"header#main": {
templateUrl: "partials/header.html",
},
"footer#orders": {
templateUrl: "partials/footer.html",
},
}
})
.state('main.body',{
url:'/',
template:"<p>Test!</p>"
})
main.html
<div ui-view="header"></div>
<div ui-view></div>
<div ui-view="footer"></div>
I have a feeling that the ui-view section of the html is not the way to go, and that the answer might have to do with abstract states, but thus far I haven't managed to get it working. Any help would be appreciated.
I have also attempted to reference a view as if it were a state, but that also rendered nothing.
This answer seems to come close, but I haven't been able to get what is suggested in the comments working.
I've looked at other questions that are layout related, but none of the solutions I've come across have worked for me. Thanks!
There is a working plunker, showing all the small adjustments described below in action.
Firstly, we must be sure, that our index.html, the root view, has the place for our 'mainModule' view template. So this is a snippet of the index.html:
<body>
<div ui-view="mainModule" ></div>
...
That, means, that our core view (one of views defined in 'main' state) will be properly injected into root view.
Now, our header and footer should both be using absolute names, but their suffix must be 'main' (not 'orders' like above for footer). That is saying to UI-Router: place them inside of views defined in this state (main.html)
Also, we can (as part of this 'main' state) define some default content of the "body". It could be let's say some list view...
.state('main', {
url: '/',
views: {
// it could be "mainModule" as well... so it needs its: ui-view="mainModule"
mainModule: { templateUrl: 'tpl.main.html'},
"header#main": {
...
},
// wrong absolute name
// "footer#orders": {
// we need the 'main' as well
"footer#main": {
...
},
// even the body, unnamed view could have some default
"#main": {
templateUrl: "tpl.list.html", // e.g. list
},
}
Next, we can defined few more states as children of the 'main'. They will (by default) use the unnamed view of the main. And what's more - replace the list view used above:
.state('main.body',{
url:'/body',
...
})
.state('main.detail',{
url:'/detail:/id',
...
})
Observe it here, it should give all the answers...
The name of the views used in the ui-view directive should match the view names defined in your route configuration section:
<div ui-view="mainModule"></div>
<div ui-view="header#main"></div>
<div ui-view="footer#orders"></div>
I'm not entirely sure if the "#" symbol will give you trouble - if it does, try removing it from the view names.

Nested views without nested states

Following this tutorial: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views, i'm able to create an application with multiple views defined in a root template. I need to modify that scheme a bit by putting tabledata and graph to a child view called content. I want my views to look like that:
index.html
<body>
<div ui-view="header"></div>
<div ui-view="content"></div>
</body>
content.html
<div ui-view="tabledata"></div>
<div ui-view="graph"></div>
And my routes looks like that:
# ...
.state('videos',
url: '/videos'
templateUrl: 'content.html'
views:
'tabledata':
templateUrl: 'tabledata.html'
controller: '...'
'sidebar':
templateUrl: 'graph.html'
controller: '...'
)
However, when pointing my browser to /videos, tabledata.html and graph.html templates are not loaded to corresponding views. Everything works great though, if i'll put tabledata and graph views to index.html
I'm sure there is something really wrong with my code but i'm not able to figure out what exactly nor google anything up.
As far as I'm aware you can only have multiple ui-view's in multiple named views, i.e.when you explicitly declare a views property on your state definition. I'm not entirely sure what you're after, but if you would like to have control over where hese ui-views load their states then you can use an abstract state, from the link you provided:
Views override state's template properties
If you define a views object, your state's templateUrl, template and
templateProvider will be ignored. So in the case that you need a
parent layout of these views, you can define an abstract state that
contains a template, and a child state under the layout state that
contains the 'views' object.
This is what I suggest:
.state('videos',{
url: '/videos',
templateUrl: 'content.html',
abstract: true})
.state('videos.xyz',{
url: '/xyz',//you can leave this empty if you like
{
views:{
'tabledata':{
templateUrl: 'tabledata.html'
controller: '...'
},
'sidebar':{
templateUrl: 'graph.html'
controller: '...'
}
}
})
If you don't want that xyz appended to your url's, just pass in an empty string for the url property of the state videos.xyz. I use this approach all the time, let me know if it's what you're after.

Resources