Controller loads 2 times if $location.path() changed - javascript

I'm facing a distrubing issue with my website. I'm using angular and typescript for development, the problem is : When a user logs in successfully, i'll redirect him to another page like " dashboard" , or "parentDashboard".
If i put a debugger on controller's constructor on parentDashboard or dashboard i see that the breakpoint is hit 2 times. I'm using $location.path('url') to redirect him.
This bug occurs only if i redirect him from login page. If i reload , for example dashboard page, that breakpoint is hit once.
This is the router:
.state('login', {
url: '/login',
templateUrl: '/app/login/login.partial.html',
controller: 'LoginController',
controllerAs: 'vm',
ncyBreadcrumb: {
label: 'Login'
},
.state('dashboard', {
url: '/dashboard',
templateUrl: '/app/dashboard/dashboard.partial.html',
controller: 'DashboardController',
controllerAs: 'vm',
}) //etc.....
NOTE: I don't have data-ng-controller tag in none of the pages. I found that will be a problem for multiple loads.
I don't know how to figure this.
If other sourcecode needed, i'll edit my question. Thanks
function initRouter($locationProvider: angular.ILocationProvider,
$urlRouterProvider: angular.ui.IUrlRouterProvider,
$stateProvider: angular.ui.IStateProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
$urlRouterProvider.otherwise('/');
$stateProvider

Please pay attention on trailing slash using $location.path('url').
$location.path('dashboard/')
may explain such behaviour.

Related

$urlRouterProvider.when keeps reloading state

I have just added nested states to my AngularJS application, but I also wanted to add a reroute if the parent state was hit, so I added this code to my UI-router
angular.module('app')
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('/PDGODSTD', '/PDGODSTD/weekinfo');
$stateProvider.state('PDGODSTD', {
//abstract: true, // uncommenting this resolves in an error telling me that I can't navigate to an abstract state
url: "/PDGODSTD",
templateUrl: '../Modules/PDGODSTD/web/index.html'
})
.state('PDGODSTD.weekinfo', {
url: "/weekinfo",
templateUrl: '../Modules/PDGODSTD/web/weekinfo/weekinfo.html',
controller: 'PDGODSTDweekinfoController',
resolve: {
}
})
});
index.html file only contains this piece of code <ui-view><ui-view />
What happens is that the router keep rerouting back and fourth between the parent state and the nested state, or so it seems in my debugger in Chrome.
This simple plunker seems to work: http://plnkr.co/edit/8O7YZY9vBsCW0Wbj6ygU?p=preview
Check if your templateUrl are fetching properly (check network tab).
In ui-router 1.0.0-rc.1 the preferred mechanism to redirect is to use redirectTo:.
The docs for redirectTo are here: https://ui-router.github.io/ng1/docs/latest/interfaces/ng1.ng1statedeclaration.html#redirectto
$stateProvider.state('PDGODSTD', {
redirectTo: 'PDGODSTD.weekinfo',
url: "/PDGODSTD",
templateUrl: '../Modules/PDGODSTD/web/index.html'
})
.state('PDGODSTD.weekinfo', {
url: "/weekinfo",
templateUrl: '../Modules/PDGODSTD/web/weekinfo/weekinfo.html',
controller: 'PDGODSTDweekinfoController',
resolve: {
}
})
The error was in my index.html file. The end tag was wrong it should have been <ui-view></ui-view> rather than <ui-view><ui-view />
Chris' answer is set as the correct one since he showed me that the error wasn't in the logic but somewhere else

Access variable from router in Angular

I am working on a web application with Angular. I have created a header that is included in the index.html, so it is fixed to every page and the content of the page is injected through a ui-view. My question is: if I want to only show this header after the user has passed the signup and login pages, what should I do?
I have added a variable to my router as shown below, but I am not sure how to access the router's variable from the index.html, as it is not attached to a controller (since it is fixed content across all pages). I intended to simply throw an ng-hide="hideHeader" in the index.html.
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'app/views/login/login.html',
controller: 'loginController',
controllerAs: 'vm',
hideHeader: true
})
.state('signup', {
url: '/signup',
templateUrl: 'app/views/signup/signup.html',
controller: 'signupController',
controllerAs: 'vm',
hideHeader: true
})
.state('landing', {
url: '/landing',
templateUrl: 'app/views/landing/landing.html',
controller: 'landingController',
controllerAs: 'vm',
hideHeader: false
})
.state('account-management', {
url: '/account-management',
templateUrl: 'app/views/account-management/account-management.html',
controller: 'accountManagementController',
controllerAs: 'vm'
hideHeader: false
});
});
How can I access this hideHeader value from the index.html? Is there a way to set a global scope variable and get the value from there?
Here's a different approach: Create a service specifically for authentication (login/logout) and authentication status. Then just call that service in your header. Given that the header is dependent on your authentication, then it makes sense to abstract that away. Your login/signup controllers will interact with this service.
Your routes can now also call this service to see if they should allow the user to view them based on login status.

How to change/set the main view using Yeoman with Angular Fullstack

I created a project using Yeoman (angular-fullstack). And now I would like to know how the change/set the main view to login.html. So normally when you start the application you first get the main view where you can chose to login or register. What I want is when the application start the page starts direct on the login.html
in your app.js file located at client\app\app.js, in the angular config add the following:
$stateProvider
.run(function ($state) {
$state.go('login');
});
So it should look like:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
$stateProvider
.run(function ($state) {
$state.go('login');
});
})
I realize this has been out here for a while and you likely already have a good solution, but I was recently looking at this myself and see a couple options.
One, inside app.js you could add the following code snippet under $urlRouterProvider:
.when('/', '/login')
Making your full method be something like:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.when('/', '/login')
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
})
This would force anyone going to your base page url directly to login, unless they provide the full route to another page. However, if you intend to still use the main.html, you will then need to go into client/app/main/main.js and change the default route to:
.state('main', {
url: '/main',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl'
});
So main is reachable by appending /main to the url.
Which brings me to option 2: Go into the main.js file and switch it's url to '/main' and go into login.js and switch it's url to '/'. Then anyone navigating to your base page automatically goes to the login screen but the url is viewed as just your domain without any sub page.
So client/app/main/main.js becomes:
.state('main', {
url: '/main',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl'
});
And client/app/account/account.js now contains:
.state('login', {
url: '/',
templateUrl: 'app/account/login/login.html',
controller: 'LoginCtrl'
})

How to globally implement Authentication with AngularJS, Devise and UI Router?

I'm quite new to Angular so this might be a newbie question.
I'm trying to implement a simple task manager (just an exercise) with Rails as backend and Angular as frontend. So far I followed a tutorial and everything worked fine.
Now I want to globally implement Authentication. That means: When a user is not registered and logged in, she should see a splash page or the login page.
I don't want to do that in every single Angular controller, because DRY. So I figured the UI Router might be a good place. And I have a slight suspicion that maybe $httpProvider.interceptors might be useful.
This is how far I got. I can check if a user is authenticated and prevent the page from loading. But nothing more. How would I go from here? Are there any good tutorials out there I missed?
This question on Stackoverflow goes in a similar direction but doesn't solve my problem since they are not using Devise.
Thanks!
// app.js
var app = angular.module("TaskManager", ['ui.router', 'templates', 'Devise'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home/_home.html',
controller: 'MainCtrl',
resolve: {
projectPromise: ['projects', function(projects) {
console.log($rootScope);
return projects.getAll();
}]
}
})
.state('projects', {
url: '/projects/{id}',
templateUrl: 'projects/_projects.html',
controller: 'ProjectsCtrl',
resolve: {
project: ['$stateParams', 'projects', function($stateParams, projects) {
return projects.get($stateParams.id);
}]
}
})
.state('login', {
url: '/login',
templateUrl: 'auth/_login.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function() {
$state.go('home');
})
}]
})
.state('register', {
url: '/register',
templateUrl: 'auth/_register.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function() {
$state.go('home');
})
}]
});
$urlRouterProvider.otherwise('home');
}
]);
// run blocks
app.run(function($rootScope, Auth) {
// you can inject any instance here
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
if(!Auth.isAuthenticated()) {
// So the magic should probably happen here. But how do I implement this?
// And how do I allow users to access the /login and /register page?
event.preventDefault();
}
})
});
I wrote an article that basically answers this question (at a high level at least) called How to Set Up Authentication with AngularJS and Ruby on Rails.
If you want to check whether the user is authenticated at any particular route, you can (in Angular's standard router, although I don't know about ui-router) use resolve. Here are a couple routes from an older project of mine:
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
isPublic: true
})
.when('/today', {
templateUrl: 'views/announcements.html',
controller: 'AnnouncementsCtrl',
resolve: {
auth: ['$auth', function($auth) {
return $auth.validateUser();
}]
}
})
The root route is public but /today requires authentication. Maybe that gives you enough to go on for ui-router as well. Side note: I should really update my article to account for ui-router (or Angular's new router) since I don't think many people use the regular old Angular router.
Edit: I remembered this thing from a different project of mine. This is CoffeeScript. Irrelevant code omitted.
"use strict"
angular.module("fooApp", [
"ngAnimate"
"ngCookies"
"ngResource"
"ngRoute"
"ngSanitize"
"ngTouch"
"ui.router"
"ng-token-auth"
])
.run ($rootScope, $auth, $location) ->
$rootScope.$on "auth:login-success", -> $location.path "/"
$rootScope.$on "auth:logout-success", -> $location.path "/sign-in"
$rootScope.$on "$stateChangeStart", (event, next) ->
$auth.validateUser().then (->
if $location.path() == "/"
$location.path "/file-uploads"
), ->
if !next.isPublic
$location.path "/sign-in"
Hopefully it's kind of self-evident what's going on there. This project DOES use ui-router, as you can tell from the $stateChangeStart.

Angular JS - load a templateUrl and controller on app initialization/beginning of app

so I want to load a templateUrl or, invoke $stateProvider on an action other than user input, like onEnter or onload, or just right away at the beginning of the app, but I can't seem to figure it out. Basically I have a simple nav, when clicked they load a partial which shows below the nav etc. I want to show an inital partial as soon as the app is invoked or loaded. below is a sample of my routing:
app.config(function($stateProvider, $urlRouterProvider){
$stateProvider
.state('page1', {
url: "/page1",
templateUrl: "partials/pag1.html"
})
.state('page1.stuff', {
url: "/page1stuff",
templateUrl: "partials/page1.stuff.html",
controller: someController
})
.state('page2', {
url: "/page2",
templateUrl: "partials/page2.html"
})
any help would be greatly appreciated.
If I understand you correctly you just need a default state
.state('home', {
url: '',
templateUrl: 'partials/home.html'
}

Categories

Resources