Nested views with nested states in AngularJS - javascript

I'm trying to make nested states, but something is wrong and I can't figure out why.
I have these states in my angular app:
/client (list clients)
/client/:id (show client)
/client/new (new client)
And now, I'm trying to do:
/client/:id/task (list clients tasks)
/client/:id/task/new (create new task for this client)
/client/:id/task/:idTask (show the client task)
All the states are working, but the task states is not changing the content.
My index.html with the ui-view "main":
<section id="container">
<header></header>
<sidebar></sidebar>
<section class="main-content-wrapper" ng-class="{'main':collapse}">
<section id="main-content">
<div ui-view="main"></div>
</section>
</section>
</section>
My client.tpl.html with the ui-view "content":
<div class="row">
<div class="col-md-12">
<ul class="breadcrumb">
<li><a href ui-sref="home">Home</a></li>
<li class="active">Clients</li>
</ul>
</div>
</div>
<div ui-view="content"></div>
My app states:
$stateProvider
.state('/', {
url: '/',
templateUrl: '/app/application/application.tpl.html',
abstract: true
})
// CLIENT
.state('client', {
url: '/client',
abstract: true,
views: {
'main': {
templateUrl: '/app/client/client.tpl.html',
controller: 'ClientController'
}
}
})
.state('client.list', {
url: '/list',
views: {
'content': {
templateUrl: '/app/client/client.list.tpl.html',
controller: 'ClientListController'
}
}
})
.state('client.new', {
url: '/new',
views: {
'content': {
templateUrl: '/app/client/client.new.tpl.html',
controller: 'ClientNewController'
}
}
})
.state('client.show', {
url: '/:id',
views: {
'content': {
templateUrl: '/app/client/client.show.tpl.html',
controller: 'ClientShowController',
}
}
})
Tasks states
// TASKS
.state('client.details', {
url: '/:idClient',
abstract: true,
views: {
'content': {
templateUrl: '/app/task/task.tpl.html',
controller: 'TaskController'
}
}
})
.state('client.details.task', {
url: '/task',
views: {
'content': {
templateUrl: '/app/task/task.list.tpl.html',
controller: 'TaskListController'
}
}
})
.state('client.details.task.new', {
url: '/new',
views: {
'content': {
templateUrl: '/app/task/task.new.tpl.html',
controller: 'TaskNewController'
}
}
})
.state('client.details.task.show', {
url: '/:idTask',
views: {
'content': {
templateUrl: '/app/task/task.show.tpl.html',
controller: 'TaskShowController'
}
}
});
So, when I click to go to:
/client
/client/:id
/client/new
Everything works fine, the content change, but, when I click to go to:
/client/:id/task
/client/:id/task/:idTask
/client/:id/task/new
The content don't change, actually, the content gets empty.
UPDATE 1
The link to the task list is in my sidebar, sidebar is a directive:
Directive:
.directive('sidebar', [function () {
return {
restrict: 'E',
replace: true,
templateUrl: '/common/partials/sidebar.html'
};
}])
Template:
<aside class="sidebar" ng-class="{'sidebar-toggle':collapse}" ng-controller="SidebarController as sidebar">
<div id="leftside-navigation" class="nano">
<ul class="nano-content">
<li class="active">
<a href ui-sref="home"><i class="fa fa-dashboard"></i><span>Home</span></a>
</li>
<li class="sub-menu">
<a href ng-click="toggle()">
<i class="fa fa-users"></i>
<span>Clients</span>
<i class="arrow fa fa-angle-right pull-right"></i>
</a>
<ul style="height: {{height}}px; overflow: hidden;">
<li ng-repeat="client in session.clients">
<a href ui-sref="client.details.task({id:client.id})">{{client.name}}</a>
</li>
</ul>
</li>
</ul>
</div>
</aside>
The link in ui-sref is: /client/10/task

Solution there is surprisingly simple, but the concept behind could be a bit challenging.
So the state definition should be like this
Client root state is without any change. It does inject its view into ui-view="main" of the root state (index.html)
// CLIENT
.state('client', {
...
views: {
'main': {
templateUrl: '/app/client/client.tpl.html',
...
}
}
})
Now, we have first level children. They will target ui-view="content" of their parent (client and its template injected into ui-view="main")
.state('client.list', {
views: {
'content': {
....
})
.state('client.new', {
url: '/new',
views: {
'content': {
...
})
...
So until now, everything is working. Below is a change. We try again inject our templates into ui-view="content" - good. But it is not defined in our parent. It is in our grand-parent - a Client state. So we are skipping one level. We have to use absolute naming for view name targeting
// TASKS
.state('client.details.task', {
views: {
// wrong
'content': {
// correct
'content#client': {
})
.state('client.details.task.new', {
views: {
// wrong
'content': {
// correct
'content#client': {
}
})
...
Now it should be clear. If not, maybe this could help a bit. The first level children, would work even with this state definition
.state('client.list', {
views: {
// working
'content': {
// also working
'content#client': {
....
})
Because we just used absolute naming - where the it is done for us out of the box (syntactic sugar). For more and even better explanation, please, see documentation:
View Names - Relative vs. Absolute Names
small cite:
Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname#statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.
For example, the previous example could also be written as:
.state('report',{
views: {
'filters#': { },
'tabledata#': { },
'graph#': { }
}
})

Related

How to use ui-sref in nested Angular UI router?

Its has been a heavy hours research and applying the techniques however I am not able to solve the issue. My app has three top navigations which are home, sports and country.
<head>
<script src="js/angular-ui-router.min.js"></script>
<script src="js/angular.min.js"></script>
<script src="js/index.js"></script>
</head>
The navigation looks like:
HTML looks like below which is in index.html
<ul>
<li><a ui-sref="home">Home</a></li>
<li><a ui-sref="sports">Sports</a></li>
<li><a ui-sref="country">Country</a></li>
</ul>
<div ui-view></div>
In sports.html, there are other three navigation, which are not working (they are not even clickable).
<ul>
<li><a ui-sref="sports.football">Football</a></li>
<li><a ui-sref="sports.weightlifting">Weightlifting</a></li>
<li><a ui-sref="sports.swimming">Swimming</a></li>
</ul>
<div ui-view></div>
Angular looks like
$stateProvider
.state('home', {
url: '/',
templateUrl: 'index.html'
})
.state('sports', {
url: '/sports',
templateUrl: 'sports.html'
})
.state('country', {
url: '/country',
templateUrl: 'country.html'
})
.state('sports.football', {
url: '/football',
templateUrl: 'sports/football.html'
})
.state('sports.weightlifting', {
url: '/weightlifting',
templateUrl: 'sports/weightlifting.html'
})
.state('sports.swimming', {
url: '/swimming',
templateUrl: 'sports/swimming.html'
});
Basically, when a user opens the app, there should be top menu bar with Home, Sports and Country. When a user clicks Sports then there should be another view shown/ another sub navigation in this page showing Football, Weightlifting and Swimming. However these sub navigations are not clickable and not working.
It would be grateful if you can help me to find what would be the problem.
It's a problem of nested views. If you explicitly target the different views you can resolve it.
<ul>
<li><a ui-sref="home">Home</a></li>
<li><a ui-sref="sports">Sports</a></li>
<li><a ui-sref="country">Country</a></li>
</ul>
<div ui-view></div> <!-- your sports.html plugs in here -->
In sports.html :
<ul>
<li><a ui-sref="sports.football">Football</a></li>
<li><a ui-sref="sports.weightlifting">Weightlifting</a></li>
<li><a ui-sref="sports.swimming">Swimming</a></li>
</ul>
<div ui-view></div> <!-- your [sportName].html plugs in here -->
So in your app.js, you just have to add some params concerning the nested view in sport.html, like this :
$stateProvider
.state('home', {
url: '/',
templateUrl: 'index.html'
})
.state('sports', {
url: '/sports',
templateUrl: 'sports.html'
})
.state('country', {
url: '/country',
templateUrl: 'country.html'
})
.state('sports.football', {
url: '/football',
// Relatively targets the unnamed view in this state's parent state, 'sports'.
// <div ui-view/> within sports.html
views: {
"": {
templateUrl: 'sports/football.html'
}
}
})
.state('sports.weightlifting', {
url: '/weightlifting',
// Relatively targets the unnamed view in this state's parent state, 'sports'.
// <div ui-view/> within sports.html
views: {
"": {
templateUrl: 'sports/weightlifting.html'
}
}
})
.state('sports.swimming', {
url: '/swimming',
// Relatively targets the unnamed view in this state's parent state, 'sports'.
// <div ui-view/> within sports.html
views: {
"": {
templateUrl: 'sports/swimming.html'
}
}
});
If you want more details, you can read this doc : https://github.com/angular-ui/ui-router/wiki/Multiple-Named-views
There is no issue in your code. Probably you might be missing some dependencies.
Check out my plunker.click here to view code demo
app.js
(function(){
angular
.module('plunker', ['ui.router']);
})();
router.js
(function(){
'use strict';
angular
.module('plunker')
.config(['$stateProvider', function($stateProvider)
{
$stateProvider
.state('home', {
url: '/',
templateUrl: 'home.html'
})
.state('sports', {
url: '/sports',
templateUrl: 'sports.html'
})
.state('sports.football', {
url: '/football',
templateUrl: 'football.html'
})
.state('sports.weightlifting', {
url: '/weightlifting',
templateUrl: 'weightlifting.html'
})
.state('sports.swimming', {
url: '/swimming',
templateUrl: 'swimming.html'
})
.state('country', {
url: '/country',
templateUrl: 'country.html'
});
}])
})();
<li><a ng-click="goTo('sports.football')">Football</a></li>
In Your Controller
$scope.goTo = function(path){
$state.go(path)
}

angular ui.router and linking to dynamic pages

I am working on a backend rails app that produces and API for a front end ionic app to consume. I have the rails side (mostly) done, and am building the ionic app. I have experience working with angular, but not ionic.
Anyway, my question is I have a resource I'm receiving from the API. It's static, and the only actions available to it are index and show. I receieve the index action fine and can list them, but linking to the show pages results in a blank white screen. I put a couple console.log calls in the angular show controller to see if it's running at all, and it is, but it's not linking to the page. Here is the set up:
app.js
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('app.search', {
url: "/search",
views: {
'tab-search': {
templateUrl: "templates/search.html"
}
}
})
.state('app.gyms', {
url: '/gyms',
views: {
'tab-gyms': {
templateUrl: 'templates/gyms.html',
controller: 'GymsIndexCtrl'
}
}
})
.state('app.gymShow', {
url: "/gyms/:gymId",
views: {
'tab-gyms': {
templates: "templates/gym.html",
controller: 'GymsShowCtrl'
}
}
})
$urlRouterProvider.otherwise('/app/search');
})
controllers
angular.module('myApp.controllers', [])
.controller('GymsIndexCtrl', function($scope, $stateParams, Gyms) {
$scope.gyms = Gyms.index(function(response) {
console.log("from gyms");
console.log(response);
});
})
.controller('GymsShowCtrl', function($scope, $stateParams, Gyms) {
$scope.gym = Gyms.query({id: $stateParams.id}, function(response) {
// this is what's running, and it works
console.log("running");
console.log(response);
});
})
gyms.html
<ion-view view-title="Gyms">
<ion-content>
<h1 class="text-center">Gyms</h1>
<hr class="generic">
<div ng-repeat="gym in gyms">
<div class="list card">
<div class="item item-avatar">
<h2 class="pull-left"><b>{{gym.name}}</b></h2>
<p class="pull-lefft">{{gym.description}}</p>
</div>
<div class="item item-image">
<img src="{{gym.images}}" style="min-height: 200px;">
</div>
<!-- where the linking is. I have tried routes like this, and the
ui.router syntax of ui-sref="app.gymShow({gymId: gym.id})"
neither produce and error, but neither work -->
<a class="item item-icon-left assertive" href="#/app/gyms/{{gym.id}}">
<ion-icon name="person"></ion-icon>
{{gym.reviews_count}} reviews
</a>
</div>
</div>
</ion-content>
</ion-view>
In the ionic scaffold example, it uses this
.state('app.playlists', {
url: "/playlists",
views: {
'tab-playlists': {
templateUrl: "templates/playlists.html",
controller: 'PlaylistsCtrl'
}
}
})
.state('app.single', {
url: "/playlists/:playlistId",
views: {
'tab-playlists': {
templateUrl: "templates/playlist.html",
controller: 'PlaylistCtrl'
}
}
})
and I have tried to follow that exactly, but still not luck.
I have 2 questions. The first is how do I link to say show pages from an index action in ionic. And the second would be, can I use $routeProvider in Ionic? I know I should be able to, but I have not seen many resources showing that, so I do not want to cause too much overhead related to that just because I was having problems linking pages.
Any help pointing me in the right direction about either of those questions is very much appreciated.
So this was incredibly dumb on my part. In this section
.state('app.gymShow', {
url: "/gyms/:gymId",
views: {
'tab-gyms': {
templates: "templates/gym.html",
controller: 'GymsShowCtrl'
}
}
})
was supposed to be changed to this
.state('app.gyms', {
url: '/gyms',
views: {
'tab-gyms': {
templateUrl: 'templates/gyms.html',
controller: 'GymsIndexCtrl'
}
}
})
dumb syntax error on my end

Filling common views in abstract state in UI-Router

Would this result in presenting the page with header, footer and content block filled with content.list view?
$stateProvider
.state('contacts', {
abstract: true,
url: '/contacts',
views: {
header: { templateUrl: 'admin/header.html'},
content: {
templateUrl: 'contacts.html',
controller: function($scope){
$scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
}
},
footer: { templateUrl: 'admin/footer.html'}
}
})
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html'
})
.
<!-- index.html -->
...
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
...
.
<!-- contacts.html -->
<h1>Contacts Page</h1>
<div ui-view></div>
.
<!-- contacts.list.html -->
<ul>
<li ng-repeat="person in contacts">
<a ng-href="#/contacts/{{person.id}}">{{person.name}}</a>
</li>
</ul>
Yes, this will work. There is a working plunker.
The parent view's $scope (the view, defined in state 'contacts' views as a 'content') and its scope, will be a source for prototypical inheritance.
And that means, that its properties will be available in the child state 'contacts.list', because it is injected into that 'content' view
There is in detail more about it:
How do I share $scope data between states in angularjs ui-router?
To prove, it, we can extend the code snippet above with a list controller and inject some more contacts
...
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html',
controller: 'listCtrl', // new controller
})
}
])
// we get already initiated contacts... coming from parent view
.controller('listCtrl', ['$scope', function($scope) {
$scope.contacts
.push({ id: 2, name: "from a child" });
}])
Check it here

Angular UI-Router nested views

I am trying to get nested views to work in which after logging in it sends you to in.html from there all links link to a ui-view within in.html. Currently all links go to a "new" page
index.html
<!-- more HTML -->
<body ng-controller="MainController as main">
<!--<div id="loading"><span>Loading</span></div>-->
<div ui-view="default"></div>
</body>
<!-- more HTML -->
in.html
<a ui-sref="online">Online Users</a>
<div ui-view="main"></div>
app.js routes
var $td = $config.TPL_DIR;
$stateProvider
.state('auth', {
url: '/auth',
views: {
"default": {
controller: 'AuthController as auth',
templateUrl: $td + 'login.html'
}
}
})
.state('loggedin', {
url: '/in',
views: {
"default": {
templateUrl: $td + 'in.html'
}
}
})
.state('online', {
url: '/online',
views: {
"main": {
controller: 'OnlineController as online',
templateUrl: $td + 'online.html'
}
}
});
Try making some subviews and then changing the ui-view from in there. Something like
.state('loggedin.another_view', {
url: '',
views: {
"main": {
templateUrl: $td + 'partial.html'
}
}
})
Since loggedin is the parent view, when the router looks for main, it will look within the loggedin context for the view. It will load partial.html into your view.
Changed state to loggedin.online
.state('loggedin.online', {
url: '/online',
views: {
"main": {
controller: 'OnlineController as online',
templateUrl: $td + 'online.html'
}
}
});
Change link to
<a ui-sref=".online">Online Users</a>
<div ui-view="main"></div>

AngularJS, Nested UI-View

Could someone give a hint on ui-view?
I have one main ui-view and then two nested views, but for some reason these two are not getting loaded.
$stateProvider
.state('index',{
url:'',
templateUrl: './app/modules/main.html'
})
.state('index.feed',{
parent:'index',
templateUrl: './app/modules/feed.html'
})
.state('index.status',{
parent: 'index',
templateUrl: './app/modules/status.html'
});
The main page is loading but the two other views not
in my main.html I have:
<div class="voucher-display" ui-view="index.feed"></div>
<div class="feed" ui-view="index.feed"></div>
I just get an empty file.
The value you pass to ui-view in your html is not the name of the state, it should be the name of the view.
Is the html in feed.html and status.html supposed to replace or be displayed along with main.html? Either scenario will change the solution, but assuming you always want main.html, and want to display the feed.html and status.html where their respective nested views are selected, you'd do something like this:
.state('index', {
url: '/',
views: {
'main': {
templateUrl: './app/modules/main.html'
}
}
})
.state('index', {
views: {
'main': {
templateUrl: './app/modules/main.html',
},
'detail': {
templateUrl: './app/modules/feed.html'
}
}
})
.state('index.status', {
views: {
'main': {
templateUrl: './app/modules/main.html',
},
'detail': {
templateUrl: './app/modules/status.html'
}
}
});
And in your html you'd need two div tags that map the view.
<div ui-view="main"></div>
<div ui-view="detail"></div>
Now main.html should always show up in the first div, and feed.html or status.html, when their states are active, will show up in the second div.
Here is the docs for the ui-view directive, and here is a good blog post about ui-view.
After a couple of attempts, this worked to load the nested:
$stateProvider
.state('index', {
url: '',
views: {
'main': {
templateUrl: './app/modules/main.html'
},
'status#index': {
templateUrl: './app/modules/status.html'
},
'feed#index':{
templateUrl: './app/modules/feed.html'
}
}
})
index.html has:
<main ui-view="main"></main>
and inside the main.html I have:
<div class="voucher-display" ui-view="feed"></div>
<aside ui-view='status'></aside>
but now I got another problem, if I want to load another ui-view from my index.html, what should I do? because this does not work:
.state('clients',{
url:'/clients',
templateUrl: './app/modules/clients.html'
});

Categories

Resources