In my angular.js app I have a profile view with various tabs, each one a nested view with it's own controller. What I want to do is to be able to click back and forth different tabs but always remaining in the profile view.
But every time I click on one of the tabs, e.g "nested_view_1" with url "/nested_view_1" and templateUrl: "nested_view_1.html", it changes the path to "/nested_view_1" and renders a blank page (because I didn't specified a "/nested_view_1" in my $routeProvider). Only when I click back, it shows my selected tab content inside my view
May be a problem that I'm using ui.router and ng-route at the same time.
here is my app.js file:
var app = angular.module('app', ['ngRoute', 'ui.router','ui.bootstrap','ui.bootstrap.tpls'])
app.config(['$routeProvider', '$stateProvider' ,function ($routeProvider, $stateProvider) {
$routeProvider
.when('/', {
templateUrl: '/app/views/home.html',
controller: 'HomepageCtrl'
})
.when('/articles', {
templateUrl: 'app/views/articles/index.html',
controller: 'ArticlesCtrl'
})
.when('/articles/:id', {
templateUrl: 'app/views/articles/show.html',
controller: 'ShowArticleCtrl'
})
... other routes ...
$stateProvider
.state('profile', {
abstract: true,
url: '/profile',
templateUrl: "app/views/users/profile.html",
controller: 'UserShowCtrl'
})
.state('nested_view_1', {
url: '/nested_view_1',
views: {
"tabContent": {
templateUrl: 'app/views/users/sales.html',
controller: 'UserSalesAreaCtrl'
}
}
})
.state('nested_view_2', {
url: "/nested_view_2",
views: {
"tabContent": {
templateUrl: 'app/views/users/buys.html',
controller: 'UserAreaCtrl'
}
}
})
.... all the way to nested view 4, in my case
}])
my view:
<ul class="nav nav-tabs mt-50" >
<li ng-repeat="t in tabs" class="nav-item" heading="{{t.heading}}" active="t.active">
<a ui-sref="{{t.route}}" style="font-weight: 200;" class="nav-link" ng-class="{'active'}" >
{{ t.heading }}
</a>
</li>
<div ui-view="tabContent"></div>
</ul>
my UserShowCtrl
angular.module('app').controller('UserShowCtrl', ['$scope', 'user', '$routeParams', '$location', '$state',
function ($scope, user, $routeParams, $location, $state) {
$scope.tabs = [
{ heading: 'nested_view_1', route:'nested_view_1', active:true },
{ heading: 'nested_view_2', route:'nested_view_2', active:false },
{ heading: 'nested_view_3', route:'nested_view_3', active:false },
{ heading: 'nested_view_4', route:'nested_view_4', active:false }
];
}]);
Prefix your nested routes with the parent route parent.
$stateProvider
.state('profile', {
abstract: true,
url: '/profile',
templateUrl: "app/views/users/profile.html",
controller: 'UserShowCtrl'
})
.state('profile.nested_view_1', {
url: '/nested_view_1',
views: {
"tabContent": {
templateUrl: 'app/views/users/sales.html',
controller: 'UserSalesAreaCtrl'
}
}
})
.state('profile.nested_view_2', {
url: "/nested_view_2",
views: {
"tabContent": {
templateUrl: 'app/views/users/buys.html',
controller: 'UserAreaCtrl'
}
}
})
Related
What I'm trying to do is create a ui-view for each request in requests.
When the app state = request.detail I'd like to render the "detail" into a UI-view with the id set as the request.id. I cannot figure out how to pass the request.id through angular and name the ui-view which the state renders in dynamically.
For Example if the request.id = 1 I'd like the ui-view to be ui-view="1";
if the request.id=23 , i'd like it's ui-view to be ui-view="23".
Request.js
app.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('home', { url: '', views: { 'main': { templateUrl: 'static_pages/home.html'}}})
.state('requests', { url: '/requests', views: {'main': { templateUrl: 'requests.html', controller: 'RequestsCtrl'}}})
.state('requests.detail', { url: '/:id', views: {':id': { templateUrl: function($stateParams) {return `/requests/${$stateParams.id}`;}, controller: 'RequestController'}}})
.state('requests.detail.pdf', { url: '.pdf', views: { 'requestpdf': { templateUrl: function($stateParams) {return `/requests/${$stateParams.id}.pdf`;}, controller: 'RequestController'}}})
.state('requests.detail.edit', { url: '/edit', views: {'main2': { templateUrl: function($stateParams) {return `/requests/${$stateParams.id}/edit`;}, controller: 'RequestController'}}})
.state('users', { url: '/users', templateUrl: 'users.html', controller: 'UsersCtrl'})
.state('/users/:id', { url: 'users_show.html', controller: 'UsersCtrl' });
$locationProvider.html5Mode({ enabled: true, requireBase: false });
});
app.controller("RequestsCtrl", ['$scope', '$state', '$stateParams', 'Request', '$location', function($scope, $stateParams, $state, Request, $location ) {
$scope.requests = Request.query();
$scope.request = Request.query();
$scope.deleteRequest = function (request) {
Request.delete({id: request.id})
console.log("deleted" + request.id);
}
}]);
app.controller("RequestController", ['Request', '$scope', '$stateParams', RequestController]);
function RequestController( $scope, $stateParams, Request ) {
$scope.currentRequestId = $stateParams.request.id;
};
Index.html
<div id='requests' class="requests col-xs-5 col-md-5" ng-controller="RequestsCtrl" >
<div ng-repeat="request in requests" class="request">
<div id="full" ui-view="{{request.id}}"></div>
<a ng-click="showRequest(request);">
<span ng-if="request.read == false"> *NEW*</span>
<span class="col-xs-12">Request #{{request.id}}</span><br>
<span class="col-xs-1">{{request.name}}</span>
<span class="col-xs-1">{{request.email}}</span>
<span class="col-xs-4 col-xs-offset-4">{{request.city}}, {{request.state}}, {{request.region}} </span>
</a>
<div>
<div ng-controller="RequestController">
<a ui-sref="requests.detail({id: request.id})" ng-click="showfullreqest()" ng-init="fullrequest = false">View</a>
<a ng-click="deleteRequest(request)" style="float:left;"> Delete </a>
</div>
</div>
</div>
</div>
Am new to the angular js.
Am implementing a nested ui view but the problem is when checking the current state of a page it returns many objects such that i cant use $state.current to set the ng-show
I would like the navbar shown and hidden in some states.
I have tried
The main index.html page
<html>......
<body ng-app="myapp">
<nav ng-controller="headerCtrl" ng-show="shownav()" >
//here is the navbar code..The shownav is defined on the headerCtrl
</nav>
<div ui-view> </div>
//Angular js, controllers and services links
</body>
</html>
The app.js code
var app = angular.module('myApp', ['ui.router']);
app.controller('headerCtrl',function($scope, $state) {
$scope.shownavbar = function(){
var state = $state.current;
if(state ==='login' ){
return false;
}else{
return true;
}
}
app.config(function($stateProvider, $urlRouterProvider, $httpProvider){
$stateProvider
.state('login', {
url:'/login',
templateUrl: 'templates/index/login/login.html',
controller: 'loginCtrl'
})
.state('dash', {
url: '/dash',
templateUrl:'templates/layout/dashboard.html',
abstract:true
})
.state('dash.call',{
url: '/call',
templateUrl: 'templates/index/calls/calls.html',
controller: 'callCtrl'
})
.state('dash.profile', {
url: '/profile',
templateUrl: 'templates/index/account/profile/profile.html',
controller: 'profileCtrl'
})
});
I would like the navbar hidden for some states like when a user is on the login state
At the headerctrl i have also tried
$scope.shownavbar = function(){
var state = $state.current;
$log.info(state);
}
This returns in the console:
angular.js:13708 Object {name: "", url: "^", views: null, abstract: true}
angular.js:13708 Object {name: "", url: "^", views: null, abstract: true}
angular.js:13708 Object {url: "/login", templateUrl: "templates/index/login/login.html", controller: "loginCtrl", name: "login"}
angular.js:13708 Object {url: "/login", templateUrl: "templates/index/login/login.html", controller: "loginCtrl", name: "login"}
angular.js:13708 Object {url: "/login", templateUrl: "templates/index/login/login.html", controller: "loginCtrl", name: "login"}
WHAT COULD BE THE PROBLEM
I can think in two ways to solve this.
First solution
EDITED
inside your run() put a function to see when the state is changing and hide or show the navbar.
myApp.run(function ($rootScope, $state) {
$rootScope.navbar = false;
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.name === 'login') {//toState variable see the state you're going
$rootScope.navbar = false;
} else {
$rootScope.navbar = true;
}
});
});
and change in your ng-show="navbar"
Second solution
the second solution i can think is you use multiple views.
$stateProvider
.state('login', {
url: '/',
views: {
'navbar': {
templateUrl: null,
controller: null
},
'body': {
templateUrl: "views/login.html",
controller: 'LoginController'
}
}
})
.state('home', {
url: '/home',
views: {
'navbar': {
templateUrl: "views/navbar.html",
controller: null
},
'body': {
templateUrl: "views/inicio.html",
controller: null
}
}
});
and in your html views put something similar to this:
<div ui-view="navbar"></div>
<div ui-view="body"></div>
This is my index.html:
<body>
<div ui-view></div>
</body>
This is my app.js:
angular.module('sample', [
'auth0',
'ngRoute',
'sample.home',
'sample.header',
'sample.login',
'ui.router',
'angular-storage',
'angular-jwt'
])
.config(function myAppConfig($stateProvider, $urlRouterProvider, $routeProvider, authProvider, $httpProvider, $locationProvider,
jwtInterceptorProvider) {
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'login/login.html',
controller: 'LoginCtrl'
}).state('root', {
url: '/',
abstract: true,
views: {
'header': {
templateUrl: 'home/header.html',
controller: 'HeaderCtrl'
},
'footer': {
templateUrl: 'home/footer.html'
}
},
data: {
requiresLogin: true
}
}).state('root.home', {
url: '/',
views: {
'#': {
templateUrl: 'home/home.html'
}
},
data: {
requiresLogin: true
}
})
$urlRouterProvider.otherwise('/');
authProvider.init({
domain: AUTH0_DOMAIN,
clientID: AUTH0_CLIENT_ID,
loginUrl: '/login'
});
jwtInterceptorProvider.tokenGetter = function(store) {
return store.get('token');
}
$httpProvider.interceptors.push('jwtInterceptor');
})
.run(function($rootScope, auth, store, jwtHelper, $location, $state, $stateParams) {
$rootScope.$on('$locationChangeStart', function() {
if (!auth.isAuthenticated) {
var token = store.get('token');
if (token) {
if (!jwtHelper.isTokenExpired(token)) {
auth.authenticate(store.get('profile'), token);
} else {
$location.path('/login');
}
}
}
});
})
.controller('AppCtrl', function AppCtrl($scope, $location) {
$scope.$on('$routeChangeSuccess', function(e, nextRoute) {
if (nextRoute.$$route && angular.isDefined(nextRoute.$$route.pageTitle)) {
$scope.pageTitle = nextRoute.$$route.pageTitle + ' | Auth0 Sample';
}
});
})
If I do login & the root that's commented out, everything works fine. But I need to put in a header and footer (the files are correct) and when I try the root + root.home, I get a blank screen with no errors on the browser's console either.
I'm trying to go off of a few examples from online (such as this one) but none are working out so I don't know what I'm doing wrong.
Right now my header/footer.html just say header/footer.html while home has a button on it.
Added the full app.js in case that helps. Each html (footer/header/home) just has
<h1>Home</h1>
<div ui-view></div>
Edit: index.html
<body>
<div ui-view="header"></div>
<div ui-view></div>
<div ui-view="footer"></div>
</body>
app.js
.state('root.home', {
url: '/',
views: {
'#': {
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
}
},
data: {
requiresLogin: true
}
})
I believe your issue here is your route is looking for a view named container not a class.
<div ui-view="container"></div>
Since it can't find a view named that it does not insert anything in the view.
Or you can just change your view route to be:
views: {
'#': {
templateUrl: 'home/home.html'
}
Which will tell it to insert that HTML in the first unnamed view it finds.
You can find a break down of how nested views work with UI-Router here
Assuming your home.html looks like this:
<div ui-view="header"></div>
<div ui-view="footer"></div>
your route should be something like
views: {
'header#home': {
templateUrl: 'home/header.html',
controller: 'HeaderCtrl'
},
'footer#home': {
templateUrl: 'home/footer.html'
}
},
data: {
requiresLogin: true
}
My setup
app/learning/learning.js
angular.module('meanApp')
.config(function($stateProvider) {
$stateProvider
.state('learning',{
templateUrl: 'app/learning/learning.html',
url: '/learning',
controller: 'LearnCtrl',
views: {
'list': {
templateUrl: 'app/learning/learning.list.html',
controller: function($scope){ }
},
'magic': {
templateUrl: 'app/learning/learning.magic.html',
controller: function($scope){ }
}
}
});
});
app/learning/learning.html
<h1>Learning</h1>
<div ui-view="list"></div>
<div ui-view="magic"></div>
app/learning/learning.list.html
<p>A list</p>
app/learning/learning.magic.html
<h2>Magic</h2>
when I navigate to /learning I get a blank page, I'm not sure what I'm doing wrong so any help would be greatly appreciated.
You shouldn't load the base template inside state when there are nested ui-view,
Define the base template inside views.
Route Code
angular.module('meanApp')
.config(function($stateProvider) {
$stateProvider
.state('learning',{
url: '/learning',
views: {
'': {
templateUrl: 'app/learning/learning.html',
controller: 'LearnCtrl'
},
'list#learning': { //you missed state name here
templateUrl: 'app/learning/learning.list.html',
controller: function($scope){ }
},
'magic#learning': { //you missed state name here
templateUrl: 'app/learning/learning.magic.html',
controller: function($scope){ }
}
}
});
});
This could Help you, Thanks.
The problem was that I was trying to load the base template inside state and I missed out the state name as Pankaj said, so the solution is...
angular.module('meanApp')
.config(function($stateProvider) {
$stateProvider
.state('learning',{
url: '/learning',
views: {
'': {
templateUrl: 'app/learning/learning.html',
controller: 'LearnCtrl'
},
'list#learning': { //you missed state name here
templateUrl: 'app/learning/learning.list.html',
controller: function($scope){ }
},
'magic#learning': { //you missed state name here
templateUrl: 'app/learning/learning.magic.html',
controller: function($scope){ }
}
}
});
});
I have an app with 3 views (A,B,C) and 2 states(1,2)
html
<div ui-view="A"></div>
<div ui-view="B"></div>
<div ui-view="C"></div>
The two states are called list and create. In both states the template and controller of view A + B stay the same but view c should change templates and controllers. I can get view c's content to change but it refreshes view A and view B as it does ie things that are in their controllers run again.
What is the correct way to organise the router to prevent this?
js so far
$urlRouterProvider.otherwise("/basestate/list");
$stateProvider
.state('baseState', function() {
url:"/basestate",
templateUrl: "basestate.html",
controller: 'BaseStateCtrl'
})
.state('baseState.list', function() {
url: "/list",
views: {
"viewA#baseState": {
templateUrl: "viewA.html"
controller: "ViewACtrl"
},
"viewB#baseState": {
templateUrl: "viewB.html"
controller: "ViewBCtrl"
},
"viewC#baseState": {
templateUrl: "list.html"
controller: "listCtrl"
}
}
})
.state('baseState.create', function() {
url: "/create",
views: {
"viewA#baseState": {
templateUrl: "viewA.html"
controller: "ViewACtrl"
},
"viewB#baseState": {
templateUrl: "viewB.html"
controller: "ViewBCtrl"
},
"viewC#baseState": {
templateUrl: "create.html"
controller: "createCtrl"
}
}
})
To achieve that you basically need to freeze your viewA and viewC at the level of baseState and make that state abstract:
.state('basestate', {
url: '/basestate',
abstract: true,
views: {
"viewA": {
templateUrl: "viewA.html",
controller: "ViewACtrl"
},
"viewB": {
templateUrl: "viewB.html",
controller: "ViewBCtrl"
},
"viewC": {
template: '<div ui-view="viewC_child"></div>'
}
}
})
Note that for viewC we are making a placeholder that will contain our nested view (either list or create):
.state('basestate.list',{
url: "/list",
views: {
"viewC_child": {
templateUrl: "list.html",
controller: "ListCtrl"
}
}
})
.state('basestate.create', {
url: "/create",
views: {
"viewC_child": {
templateUrl: "create.html",
controller: "CreateCtrl"
}
}
})
Check this plunkr and be careful with commas in your code :)