multiple nested views with their own router - angularjs

I am building an angular app with nested views using ui-router. The app has a list of posts, each with its own Nav menu to change the post's view.
To simplify the context, lets just say that each nav menu can change the theme of the post it is attached to.
POST 1 CONTENT
[Light theme button] [Dark theme button]
--------------------------------------------
POST 2 CONTENT
[Light theme button] [Dark theme button]
--------------------------------------------
*
*
*
The problem I am facing is when I press a button on one post's nav menu, all the posts change.
I want the nav menu for each post only to effect its respective post.
I am a total beginner to angular and my questions are:
Is this a problem i would want to solve with ui-router? (do i want to
have a router for each post?)
If it is, how do i solve it?
If not, can you point me in the right direction?
any help is appreciated, whether it is a link, explanation, code or just a comment.
PS. in reality i am not just changing the theme, i am changing the content on the post completely. In the future i want to implement more than just 2 options menu options.
here is the code that i am using now (modified a bit to remove redundancy):
Main.js:
angular.module('app',['ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/postlist');
$stateProvider
.state('postlist', {
url: '/postlist',
templateUrl: 'ListView.html',
controller: ListCtrl
})
.state('postlist.light', {
templateUrl: 'pollViewLight.html',
controller: PollLightCtrl
})
.state('postlist.dark', {
templateUrl: 'pollViewDark.html',
controller: PollDarkCtrl
})
});
simplified part of index.html:
<nav>
<ul>
<li><a ui-sref="postlist">Post List</a></li>
</ul>
</nav>
<div class="container">
<div ui-view></div>
</div>
ListView.html:
<div class="row" ng-repeat="post in posts">
<div class="col">
<div ui-view></div>
<ul class="nav nav-pills">
<li><a ui-sref=".light">Light Theme</a></li>
<li><a ui-sref=".dark">Dark Theme</a></li>
</ul>
</div>
</div>

Related

AngularJs ui set the home page active

I am working of AngularJs v 1 app with ui routing.
My question simply how to set the home page active without clicking the ui-sref link.
I tried with ng-class="active" but it doesn't achieve the task.
<script>
angular.module("myApp",['ui.router'])
.config(function ($stateProvider,$urlRouterProvider,$locationProvider) {
$stateProvider
.state("home",{
url:"home",
views:{
'main':{templateUrl:"home.html"}
}
});
$locationProvider.hashPrefix('');
$urlRouterProvider.otherwise("/home");
</script>
<div class="container" style="margin-top: 60px">
<div ui-view="main"> </div>
</div>
Home page
<div class="row" style=" margin-top:100px; " ng-app="app" ng-class="active">
<h1>Home</h1>
</div>
What you are looking for is ui-sref-active
From the doc
A directive working alongside ui-sref to add classes to an element when the related ui-sref directive's state is active, and removing them when it is inactive. The primary use-case is to simplify the special appearance of navigation menus relying on ui-sref, by having the "active" state's menu button appear different, distinguishing it from the inactive menu items.
It will add the active for you if you're currently on the right state.
Markup should look something along the line of
<div class="some-navigation-class">
<a ui-sref="home" ui-sref-active="active">Home</a>
<!-- more nav goes here -->
</div>

Angular ui-router nested states without nested views

Let's say I have a films app. It has Home/About/Films pages. Films page has subcategories: Comedy/Drama. Currently, I'm struggling with navigation. The Goal is when I click Home - it should render home. About - about. Films - render all films (for example simple cards with title/genre/year). When I click Films->Comedy - it should use the same controller as Films, render exactly same template but only comedy films. Moreover, navigation should be active with Films and Comedy.
I've created plunker to demonstrate first approach https://plnkr.co/edit/d0Db18ZJYtQx6aJEZ3xa?p=preview
So my first approach was:
.state('layout.films', {
url: '/films',
controller: 'FilmsCtrl as films',
template: '<div>All films</div>'
})
.state('layout.films.genre', {
url: '/:genre',
controller: 'FilmsCtrl as films',
template: '<div>{{ films.genre }} films</div>'
})
and this layout:
<div class="layout">
<ul>
<li><a ui-sref-active="_active" ui-sref="layout.home">home</a></li>
<li><a ui-sref-active="_active" ui-sref="layout.about">about</a></li>
<li>
<a ui-sref-active="_active" ui-sref="layout.films">films</a>
<ul>
<li><a ui-sref-active="_active" ui-sref="layout.films.genre({genre: 'comedy'})">comedy</a></li>
<li><a ui-sref-active="_active" ui-sref="layout.films.genre({genre: 'drama'})">drama</a></li>
</ul>
</li>
</ul>
<hr>
<div ui-view></div>
</div>
But that doesn't work. Turns out to be that nested states requires nested views. I was sad. And tried second approach: https://plnkr.co/edit/3YWAoLcPU3fd2FdebWpD?p=preview
I created only one films state:
.state('layout.films-genre', {
url: '/films/:genre?',
controller: 'FilmsCtrl as films',
template: '<div>{{ films.genre }} films</div>'
})
And fixed layout to work with this:
<a ui-sref-active="_active" ui-sref="layout.films-genre">films</a>
<ul>
<li><a ui-sref-active="_active" ui-sref="layout.films-genre({genre: 'comedy'})">comedy</a></li>
<li><a ui-sref-active="_active" ui-sref="layout.films-genre({genre: 'drama'})">drama</a></li>
</ul>
Seems working. It even highlights menu items properly (wow). But actually... I can't change state from films->comedy to films. Next thought was to change the link to the all films:
<a ui-sref-active="_active" ui-sref="layout.films-genre({genre: ''})">films</a>
But now it doesn't highlight root menu item (films) when I click one of the genres. What should I do to implement desired behavior?
If I understand your question correctly, you want to have a page that can render a specific genre of films?
If so, I think you should look up stateParams. This way you could have a menu that all directs to the same child view, but sends different parameters. Then sort the list of movies accordingly to the params being sent.
It would look something like this:
.state('layout.films-genre', {
url: '/films',
controller: 'FilmsCtrl as films',
template: '<div> Template goes here </div>'
params: { genre: 'all' } // as default if I remember correctly
})
You might aswell find the answer to your question here:
How to pass parameters using ui-sref in ui-router to controller

AngularJS with Bootstrap: Suppress redirect when clicking a modal

I have a "Contact" link in my Navbar set to open a form as a modal when clicked. This is using Bootstrap. Recently I decided to add some AngularJS routing to my site and it works great for all links except this one. My route config looks like this:
asApp.config(function($routeProvider) {
$routeProvider
// route for the home page
.when('/', {
templateUrl : 'pages/home.html',
controller : 'mainController'
})
// route for the about page
.when('/about', {
templateUrl : 'pages/about.html',
controller : 'aboutController'
})
// route otherwise
.otherwise({
templateUrl : 'pages/home.html',
controller : 'mainController'
});
});
Since I don't have a dedicated page for the contact form (it's meant to open as a modal), I haven't added any specific routing for "/contact" in the above code which is why clicking that link triggers the "route otherwise" block and redirects opens up the home page. Is there any way to suppress this routing only for the "Contact" link? I want it to stay on the page that was open when the link was clicked and the modal opened. Not sure if I have been coherent enough so please feel free to ask me if you are lost.
In case it helps, here's the modal snippet along with some other relevant HTML parts:
/* Navbar snippet */
<div class="collapse navbar-collapse" ng-controller="HeaderController">
<ul class="nav navbar-nav navbar-right">
<li ng-class="{ active: isActive('/about')}">About</li>
<li ng-class="{ active: isActive('/blog')}">Blog</li>
<li>Contact</li>
</ul>
</div>
/* Main body snippet */
<div id="main"><div ng-view></div></div>
/* Modal snippet */
<div class="modal fade" id="contact" role="dialog">
<div class="modal-dialog">
/* Contact form code here... */
</div>
</div>
Thanks a ton for your attention and help.
You could try data-target to not mess with the angular routes (url after #)
<a href data-target="#contact" data-toggle="modal">Contact</a>
There is a great module ui.bootstrap by angularUI team for using angular with bootstrap:
https://angular-ui.github.io/bootstrap/
It has a $modal service to handle opening views as a modal.
However if you don't want to use that, you can try the following:
The most simple solution will be not to have the contact as a anchor tag.
Use a div with ng-click instead.
i.e., in place of <li>Contact</li>
use <li><div class="contact-modal" ng-click="functionThatOpensModal()">Contact</div></li>
And you can style this specific div to look same as the other links.
Hope this helps

AngularJs one page app ng-view trouble

I am brand new to AngularJs and after following some tutorials I decided to try to implement a one page app into one of my projects. I have it working but I had one question about this code. Could I change what is shown on the first page before they navigate to anything? I don't want it shown on every page just when they first land on it before clicking any links.
var app=angular.module('single-page-app',['ngRoute']);
app.config(function($routeProvider){
$routeProvider
.when('/',{
templateUrl: 'home.html'
})
.when('/about',{
templateUrl: 'about.html'
});
});
app.controller('cfgController',function($scope){
/*
Here you can handle controller for specific route as well.
*/
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="single-page-app">
<div ng-controller="cfgController">
<div>
<nav>
<ul>
<li>Home</li>
<li>About us</li>
</ul>
</nav>
</div>
<br/>
<div ng-view>
<!--
This DIV loads templates depending upon route.
-->
</div>
</div>
</body>
The pages load fine and if I try to add something to the index.html under ng-view it doesn't show up on the page.
You want to put the content to show up when a user first hits your site under in your "home.html" page which is linked to your "/" route for "ng-view". When a user clicks a link, they will be taken to a new route, and the code/ will be replaced by the route they selected.

AngularJS UI Bootstrap Tabs that support routing

I would like to use AngularJS UI Bootstrap Tabs in my project, but I need it to support routing.
For example:
Tab URL
--------------------
Jobs /jobs
Invoices /invoices
Payments /payments
As far as I can tell from the source code, the current tabs and pane directives doesn't support routing.
What would be the best way to add routing?
To add routing you typically use an ng-view directive. I'm not sure it's easy enough to modify angular UI to support what you're looking for, but here's a plunker showing roughly what i think you're looking for (it's not necessarily the best way of doing it - hopefully someone can give you a better solution or improve on this)
Use data-target="#tab1". Worked for me
This answer really helped me http://odetocode.com/blogs/scott/archive/2014/04/14/deep-linking-a-tabbed-ui-with-angularjs.aspx (very simple but powerful solution)
I also have this requirement and I'm doing something similar to the answer provided by Chris, but I'm also using AngularUI router, because ngRouter does not support nested views and it is possible you'll have the tabs content view inside another view (I did) and that won't work with ng-view.
you could pass your own custom key value pairs in the route definition and achieve this.
here's a good example:
http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm
Agree with the use of UI-Router which tracks states and works great with nested views. Speaking particularly of your Bootstrap tabs issue there is a great implementation that leverages UI Router: UI-Router Tabs
If you have a route called settings and you want to have tabs in that settings page something like this works.
<div class="right-side" align="center">
<div class="settings-body">
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#!/settings#private_email">Private email</a></li>
<li><a data-toggle="tab" href="#!/settings#signature">Signature</a></li>
<li><a data-toggle="tab" href="#menu2">Menu 2</a></li>
</ul>
<div class="tab-content">
<div id="private_email" class="tab-pane fade in active">
<div class="row" ng-controller="SettingsController">
<div>
<button class="btn btn-primary" ng-click="activatePrivateEmail()">Activate email</button>
<button class="btn btn-danger">Deactivate email</button>
</div>
</div>
</div>
<div id="signature" class="tab-pane fade">
<textarea ui-tinymce="tinymceOptions" ng-model="signature"></textarea>
<div class="send-btn">
<button name="send" ng-click="" class="btn btn-primary">Save signature</button>
</div>
</div>
<div id="menu2" class="tab-pane fade">
<h3>Menu 2</h3>
<p>Some content in menu 2.</p>
</div>
</div>
</div>
</div>
I got tabs with routing working the following way.
It's able to do everything you want from dynamic tabs, and it's actually very simple.
Tabs with a ui-view, so it can dynamically load templates,
Update routing in URL
Set history state
When directly navigating to a route with a tabbed view, the correct tab is marked as active.
Define the tabs with a parameter in your router
.state('app.tabs', {
url : '/tabs',
template : template/tabs.html,
})
.state('app.tabs.tab1', {
url : '/tab1',
templateUrl : 'template/tab1.html',
params : {
tab : 'tab1'
}
})
.state('app.visitors.browser.analytics', {
url : '/tab1',
templateUrl : 'template/tab2.html',
params : {
tab : 'tab2'
}
})
The tabs template (tabs.html) is
<div ng-controller="TabCtrl as $tabs">
<uib-tabset active="$tabs.activeTab">
<uib-tab heading="This is tab 1" index="'tab1'" ui-sref="app.tabs.tab1"></uib-tab>
<uib-tab heading="This is tab 2" index="'tab2'" ui-sref="app.tabs.tab2"></uib-tab>
</uib-tabset>
<div ui-view></div>
</div>
Which is paired with a very simple controller for getting the current active tab:
app.controller('TabCtrl', function($stateParams) {
this.activeTab = $stateParams.tab;
})
just a small add to accepted answer:
i needed to keep the current tab at page refresh so i used a switch like this:
$scope.tabs = [
{ link : '#/furnizori', label : 'Furnizori' },
{ link : '#/total', label : 'Total' },
{ link : '#/clienti', label : 'Clienti' },
{ link : '#/centralizator', label : 'Centralizator' },
{ link : '#/optiuni', label : 'Optiuni' }
];
switch ($location.path()) {
case '/furnizori':
$scope.selectedTab = $scope.tabs[0];
break;
case '/total':
$scope.selectedTab = $scope.tabs[1];
break;
case '/clienti':
$scope.selectedTab = $scope.tabs[2];
break;
case '/centralizator':
$scope.selectedTab = $scope.tabs[3];
break;
case '/optiuni':
$scope.selectedTab = $scope.tabs[4];
break;
default:
$scope.selectedTab = $scope.tabs[0];
break;
}
This aught to be able to do what you want:
https://github.com/angular-ui/ui-router

Resources