AngularJS UI Router Update Sibling View - angularjs

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.

Related

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

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.

Master detail with sidemenu ionic

I'm trying to understand the master detail (MD) pattern in ionic with a sidemenu. The example code has 'Playlists' as master, and 'Playlist' as detail.
The states looks like this:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app.playlists', {
url: '/playlists',
views: {
'menuContent': {
templateUrl: 'templates/playlists.html',
controller: 'PlaylistsCtrl'
}
}
})
.state('app.single', {
url: '/playlists/:playlistId',
views: {
'menuContent': {
templateUrl: 'templates/playlist.html',
controller: 'PlaylistCtrl'
}
}
});
$urlRouterProvider.otherwise('/app/playlists');
});
But first off all, I don't understand why the Playlist state is called 'app.single'. I don't see the word 'single' anywhere else in the code. What if I would want to have a master detail pattern on page 1, and another different master detail pattern on page 2 (selected through the menu)? How would I call the detail state there?
And then my other question is related, I can't seem to get my own MD pattern to work. These are the codes that matter:
app.js:
.state('app.master', {
url: '/master',
templateUrl: 'templates/master.html',
controller: 'MasterCtrl'
})
.state('app.detail', {
url: '/master/:id',
templateUrl: 'templates/detail.html',
controller: 'DetailCtrl'
})
master.html:
<ion-view title="Master">
<ion-content>
<ion-list>
<ion-item class="item" ng-repeat="person in people">
{{person.text}}
</ion-item>
</ion-list>
</ion-content>
</ion-view>
menu.html:
....
<ion-item menu-close href="#/app/playlists">
Playlists
</ion-item>
<ion-item menu-close href="#/app/master">
Master
</ion-item>
....
The Playlists MD works all the way, but my own MD doesn't. I do get the Master list with items (controller and services work fine), but when I click an item in the list, nothing happens. Then when I swipe back (this should open the sidemenu), the detail page 'sort of' shows up without css or data. So somewhere there's a glitch. I just don't seem to understand the whole state/url thing, while I read enough documentation..
I guess, you are new to Ionic without any knowledge of AngularJS and Angular UI Router.
First, the dot-notation in state is to mark some children which will inherit from one specified parent view. As you read in the www/js/app.js, you will see a state .state('app'), and this is the parent view, menu.html, and the children views will be replaced in this tag, or so called Angular directive,
<ion-nav-view name="menuContent"></ion-nav-view>
The children or detail views needs to specify two things,
first, parent name, the word before the dot, as in example of sidemenu, app.[child_name]
second, the child view should specify which part of parent view to be fit in, through the views property, as in example above,
you see this menuContent,
views: {
'menuContent': {
templateUrl: 'templates/playlist.html',
controller: 'PlaylistCtrl'
}
}
Let's talk about Master-Detail Pattern, it is just a concept of how to display a list of items and the ability to access a single item from the list.
The sidemenu is an a complicated example for beginner.
The simple example is to have only two pages with no tie of anything (no sidemenu, no tabs...).
.state('cats', {
url: '/cats',
templateUrl: 'templates/catlist.html',
controller: 'CatListCtrl'
})
.state('cat', {
url: '/cats/:catId',
templateUrl: 'templates/catsingle.html',
controller: 'CatSingleCtrl'
})
About templates/catlist.html
<ion-view view-title="Cat Collection">
<ion-content>
<ion-list>
<ion-item ng-repeat="cat in cats" href="#/cats/{{cat.id}}">
{{cat.name}}
</ion-item>
</ion-list>
</ion-content>
</ion-view>
and templates/catsingle.html
<ion-view view-title="Cat">
<ion-content>
<h1>{{cat.name}}</h1>
<p>{{cat.description}}</p>
</ion-content>
</ion-view>
The point of accessing a single item from a list, is to specify the direction to the resource, in this case, the direction is via URL, #/cats/{{cat.id}}. So if the cat has id = 5 then the URL will be #/cats/5; ok cool, let's access the single page that displays information of the cat with ID is 5. That's how you interpret it in reality.
My suggestion for learning Ionic,
First, get to know basic AngularJS, https://angularjs.org/
Second, get in touch with Angular UI Router navigation system, http://angular-ui.github.io/ui-router/
Third, practice to create with very very small Ionic apps, make a complicated Hello Ionic.
Guess that my answer is what you're looking for.
The Playlists MD works all the way, but my own MD doesn't. I do get
the Master list with items (controller and services work fine), but
when I click an item in the list, nothing happens. Then when I swipe
back (this should open the sidemenu), the detail page 'sort of' shows
up without css or data. So somewhere there's a glitch. I just don't
seem to understand the whole state/url thing, while I read enough
documentation..
I can see that you read, but do you ask yourself if you know the stuff required for it?
On front page of Ionic framework, I can see AngularJS, I can see ngCordova, I also see SASS (which in case, you will ask the same questions as how your custom CSS won't work and reload)...Ooops, lots of pieces to know before getting your hand in Ionic, I guess.
One thing worth to point out that if you're not coming from Angular background, you might not know about the navigation system in Ionic since the Ionic docs don't tell developers about Angular UI Router, you only get to know about it from the Ionic Learn page, which looks more advanced for beginners.
I know, it is hard; I was in your situation before, I felt the same way.
Anyway, have fun learning Ionic :)
#ekussberg:
My final setup included:
.state('app.playlists', {
url: '/playlists',
templateUrl: 'templates/playlists.html',
controller: 'PlaylistsCtrl'
})
.state('app.playlist', {
url: '/playlists/:playlistId',
templateUrl: 'templates/playlist.html',
controller: 'PlaylistCtrl'
});
Notice how the playlist state is called and the url differs from yours.
#Cake:
i have already tried like this and it works, but the transition on iOS and Web are not same smooth master-detaul as in the default way. I think it depends on the "views" element of the routing.
This code for example works, but transition not:
.state('app.todo', {
url: '/todo',
views: {
'menuContent': {
templateUrl: 'templates/todo-list.html',
controller: 'TodoListCtrl'
}
}
})
.state('app.todo.single', {
url: '/:todoId',
views: {
'menuContent#app': {
templateUrl: 'templates/todo-single.html',
controller: 'TodoSingleCtrl'
}
}
})

Using same State for multiple Menu Items in AngularJS

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});

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.

AngularJS navigation bar directive with a refresh button

I looked online for a solution but I couldn't find one that suites my needs. Tried this and looked into this with no help.
My case is, I designed a mobile app with AngularJS and the app has a navigation bar. Every page has this navigation bar because of that, I decided to move the code to a directive. The navigation bar has a button inside of it and this button will be used to force a refresh on the app's data (similar to facebook's pull to refresh). The navigation bar is displayed in all pages and whenever the user are, if he click's on the button the app has to execute an update and redirects to a certain page to display the results.
The problem is, since the navigation is inside of a directive and the user can click on any screen how do I manage capturing the click on the button, calling the refresh function and redirecting to a page?
Here is my directive and one of the pages I used it.
myAppDirectives.directive('headerMenu', function() {
return {
restrict: 'A',
templateUrl: 'partials/templates/header-menu.html'
};
});
<div class="topcoat-navigation-bar">
<div class="topcoat-navigation-bar__item left quarter">
<a class="topcoat-icon-button--quiet" snap-toggle>
<span class="topcoat-icon icon-menu-stack"></span>
</a>
</div>
<div class="topcoat-navigation-bar__item right three-quarters">
<a class="topcoat-icon-button--quiet" href="" > <!-- THIS IS THE REFRESH BUTTON -->
<span class="topcoat-icon icon-refresh"></span>
</a>
</div>
Page example:
<div header-menu></div>
<div> MAIN CONTENT </div>
Thank you!
Update 2: I do appreciate the answers about using ui-router. Since my project is already running and almost ready, structural changes like that are not in question. The accepted answer was the one that solved my problem although the others might fit better other people.
Just put a ngClick on this button:
<a class="topcoat-icon-button--quiet" ng-click="redirect()">
And provide a redirect function on the directive's scope:
myAppDirectives.directive('headerMenu', function() {
return {
scope:{},
restrict: 'A',
templateUrl: 'partials/templates/header-menu.html',
link: function(scope){
scope.redirect = function(){
// redirect to...
}
}
};
});
When I need to do this, I use ui-router, and create a view hierarchy that keeps the navigation bar loaded for every page. That way I can have for example, a navbar.html partial, and a state setup like the following:
$stateProvider
.state('header', url: '', templateUrl: 'header.html', controller: 'HeaderCtrl' })
.state('header.page', url: '/somepage', templateUrl: 'page.html', controller: 'PageCtr' })
.state( ...
I agree with Matt Way, ui-router is great solution for syncing nav bars. Although, for my projects I've been using Multiple Named Views config with ui-router, and your able to get real-time sync between main app interactions and the nav bar, negating the need for "refresh buttons".
In addition, if the user wants to return to the previous page, ui-router will maintain app "state", forms/UX/etc will still be as before.
Have a look at the ui-router wiki https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views.
Basic demo with header sync http://plnkr.co/edit/kYPGZw?p=preview
Basic multi view config (viewC is Nav):
$stateProvider
.state('state1', {
url: "/",
views: {
"viewA": {
template: "1st Tab, index.viewA"
},
"viewB": {
template: '1st Tab, index.viewB<br><a ui-sref=".list">Show List</a>' +
'<div ui-view="viewB.list"></div>'
},
"viewC": {
template: '1st Tab, index.viewC <div ui-view="viewC.list"></div>'
}
}
})
.state('state1.list', {
url: 'list',
views: {
"viewB.list": {
template: '<h2>Nest list viewB</h2><ul>' +
'<li ng-repeat="thing in tab1things">{{thing}}</li></ul>',
controller: 'Tab1ViewBCtrl',
data: {}
},
"viewC.list": {
template: '1st Tab, list.viewC'
}
}
})

Resources