ui-sref not working in nested views - angularjs

Homework I've done - reviewed almost 8-9 ui-sref related questions (how to, complex examples, etc.) on this site, the related plunks, Scotch.io, etc. I can't seem to figure why my code is not working. I've spent 6-7 hours debugging the app and I can't seem to find the issue.
My page is divided into
a) layout.html - this forces the bootstrap grid system
b) app-navbar - nav bar on top (code is cut-n-paste of bootstrap example for now...)
c) app-accordion - uses the ui bootstrap accordion to display n number of items
d) app-right - right side comprises some UI elements and a ui-grid
The ui-view for all elements render correctly ... i.e., the "index" state is being called in my controller. I added a couple of simple buttons in app-navbar.html just to debug...
<ul class="nav nav-pills">
<li role="presentation" class="active"><a ui-sref="view1">View1</a></li>
<li role="presentation"><a ui-sref="view2">View2</a></li>
</ul>
<div ui-view="view-test"></div>
here's a snippet of my controller code:
.state('view1', {
url: '/view1',
views: {
'view-test': {
templateUrl: 'view1.html',
controller: 'View1Ctrl'
}
}
})
When I hover over the buttons, I see that the .../view1 and 2 show up on the browser's footer but when I click them nothing happens. For now, the accordion still uses href but it stops working if I use ui-sref. Once I get past this, that's the next question ... how do you use ui-sref for controls like accordion?
Any ideas on what might be going on? The plunk: http://plnkr.co/edit/c89j3eFvYyguMt0QznAI

There is updated working plunker. I made 2 changes.
Firstly I upgraded your version to UI-Router 0.2.13 (fixes some issues, simply always use the latest)
Secondly, I added the missing piece in state defintion: who is my parent
.state('view1', {
url: '/view1',
parent: 'index', // here is explicit setting who is parent
views: {
'view-test': {
templateUrl: 'view1.html',
controller: 'View1Ctrl'
}
}
})
Check it here
EXTENDED, based on the comment below, with few cites from documentation
Methods for Nesting States
States can be nested within each other. There are several ways of nesting states:
Using 'dot notation'. For example .state('contacts.list', {}).
Use the ui-router.stateHelper to build states from a nested state tree. Courtesy of #marklagendijk.
Using the parent property with the parent name as string. For example: parent: 'contacts'
Using the parent property with the parent object. For example parent: contacts (where 'contacts' is a stateObject)
...
Parent Property using State Name String
Alternately, you can specify the parent of a state via the parent property.
$stateProvider
.state('contacts', {})
.state('list', {
parent: 'contacts'
});

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.

Multiple named views with dynamic routing in angularjs

Edit: Here is the complete code at Plunker. Though I can not c anything in execution but same code working at local. However gives a console error though
It all works perfect. But due to :id in /news/:id/, i am getting jquery/angular errors in console which can not be tracked anywhere in my code
I can not c What i am doing wrong.
Edit: Solved plunker https://plnkr.co/edit/FWcuBgGpVdMj3CroFrYJ
First of all you are trying to use ui-router but you're including ngRoute script in your plunker. Change it to
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js"></script>
Then everything should work fine!
I suggest you a few changes...
1. Use ui-sref instead of href because it's much easier to define
ui-sref="post({id:1})" which turns into href="#/news/1"
If you would like to change url some day, then you will have to just change your route file, not each href.
$stateProvider
.state('post', {
url: "news/:id"
or
$stateProvider
.state('post', {
url: "archive/:id"
or
$stateProvider
.state('post', {
url: "whatever/:id"
2. Use abstract state
In your example it's a way better to define abstract state which holds header, content and footer - it's a typical use case.
ui-router
Abstract States
An abstract state can have child states but can not get activated
itself. An 'abstract' state is simply a state that can't be
transitioned to. It is activated implicitly when one of its
descendants are activated.
Some examples of how you might use an abstract state are:
To prepend a url to all child state urls. To insert a template with
its own ui-view(s) that its child states will populate. Optionally
assign a controller to the template. The controller must pair to a
template. Additionally, inherit $scope objects down to children, just
understand that this happens via the view hierarchy, not the state
hierarchy. To provide resolved dependencies via resolve for use by
child states. To provide inherited custom data via data for use by
child states or an event listener. To run an onEnter or onExit
function that may modify the application in someway. Any combination
of the above. 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:
"".
Here's a plunker which shows how I would do it.
https://plnkr.co/edit/5FvJaelyxdl5MuALt5VY?p=preview
Hope it helps.
Look at the documentation for ui router named views,
You can use following syntax for using multiple views
$stateProvider
.state('state',{
url: '',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'headerCtrl'
},
'content': {
template: '<div ui-view=" "></div>', //<-- child templates loaded to here
},
'footer': {
templateUrl: 'views/footer.html',
controller: 'footerCtrl'
}
}
})
.state('state.post', {
url: 'news/:id/:KeyWords'
templateUrl: 'views/post.html' //<-- This goes into content's ui-view
});
I'm guessing you want to keep the header and footer and change content views.
You can achieve this by making this state as parent to all other states
suppose
.state('main',{
abstract: true,
views: {
'header': ... ,
'content': {
template: '<ui-view></ui-view>',
}
'footer': ...
}
})
then all the child views will load their views in the ,
ex: in main.child etc, your template will load in the content's <ui-view></ui-view> tag
If you need to use a custom template depending on keywords you can do the following:
.config(['$routeProvider',
function($routeProvider, $routeParams) {
$routeProvider
.when('/news/:id/:keyWords', {
template: '<div ng-include="url"></div>',
controller: "exampleController"
})
then in the exampleController
function($routeParams, $scope) {
$scope.url = $routeParams.keyWords;
}

angularjs ui-router - How to toggle `ui-view`

I want to have a button on parent-view to turn on and off the ui-view. Just like how ng-show works. How can I do that?
One idea is to have empty.html(that is empty) as child view and call it when we want to hide ui-view. But I believe it should be better than that.
As you expected and as you suggested - the native solution with UI-Router is - state nesting. So, either parent or some child will just hide that view:
Parent
.state('parent', {
views: {
'': { templateUrl: parent.html},
'toggle#parent': {templateUrl: notEmpty.html },
}
...
Child
.state('parent.child', {
views: {
'toggle': {templateUrl: Empty.html }, // now it is hidden
}
...
There is similar Q & A with working example
Using UI-Router - Is it possible to set only single ui-view using regular syntax?
Have you tried actually using ng-show? You could apply ng-show to the div that houses the ui-view and just reference a function in your controller that sets it to false on the ng-click of the button.

Nested Views VS Separate

I'm trying to decide on an Angular Structure for my app. I started with the Angular Full Stack Generator.
In UI-Router, you can nest states. It seems logical for me to nest related states. ex: Registration, Registration.form, Registration.confirmation.... but those nested states seem to rely on each child view and parent view being viewed on the same page.
Meaning the parent will have an <ui-view> tag that will insert the child view when called.
Is it possible to have a nested state replace the parent?
Or do I have just make unnested states?
Or is this a case for an abstract state?
There is a working example
Child replacing parent is almost "native" UI-Router solution. Let's have this state definitions:
.state('parent', {
url: "/parent",
templateUrl: 'tpl.html',
})
.state('parent.child', {
url: "/child",
templateUrl: 'tpl.html',
controller: 'ChildCtrl',
})
And this would be the tpl.html, in our case used for both states:
<div ui-view>
<h3>current state name: <var>{{$state.current.name}}</var></h3>
...
</div>
So, what we can see, parent root HTML element, does have attribute ui-view. That means, that this element also plays a role of a target for a child.
Check it in action here

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.

Resources