Restrict access to route with routeprovider unless variable as been set - angularjs

I'm in the process of learning AngularJS, working on a more in-depth ToDo app. I'm having an issue with trying to limit access to a url or "route" using angular.
When you hit my dev url on my machine (todo.ang) it brings you to todo.ang/#/home, on this view you see the categories which have todos associated to each. EG (category = cat, cat has a todo of "feed", and "play"), when you click a category I'm calling the $scope.goToCategory function (seen in my JS fiddle) which sets a variable for my firebase ref then redirects you too /#/todo. This is working correctly.
My problem is, I don't want the user to be able to access /#/todo if the todoRef variable is still undefined. But it seems like even after $scope.goToCategory is called and todoRef is set to a firebase URL, the routerprovider never gets recalled to know that todoRef has been set to a different value so it always forces you back to /#/home.
code:
var todoRef = undefined;
if (todoRef !== undefined) {
$routeProvider.when('/todo', {
templateUrl: 'views/todo.html',
controller: 'TodoCtrl'
});
}
$scope.goToCategory = function(catId) {
test = catId;
todoRef = new Firebase("URL HERE");
$location.path('/todo');
}
I didn't include the entire file of code but if thats necessary, I can do that as well.
JSFiddle

All routes are only being set during the config phase.
what happens in your code is that 'todo' route is ignored during the initiation of ngRoute.
What you should do is to setup the route but have a resolve like so:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/todo', {
templateUrl: 'views/todo.html',
controller: 'TodoCtrl',
resolve: {
todoRef: ['$q', function($q) {
return todoRef ? todoRef : $q.reject('no ref');
}]
}
});
}]);
If 'todoRef' is undefined the route is rejected.
Also you should consider moving 'todoRef' into a service and not on global scope.
You can also listen for route errors and for example redirect to home route:
app.run(['$rootScope', '$location', function($rootScope, $location) {
$rootScope.$on('$routeChangeError', function() {
$location.path('/home');
});
}]);

Related

Set AngularJS Global before App Initialization

I am currently working in an AngularJS code base where different routes are triggered depending on some toggle element attached to every customer. In my app, I need to route to different places twice, once, in my app.js file, where I manage my state and another time, in a controller when I use the same variable to branch.
I have tried to use a combination of manually bootstrapping and using the run method but haven't had any success.
var app = angular
.module('app', ['customer'])
.run(function (customer) {
angular.element(document).ready(function () {
customer.myMethod().then(function (enabled) {
enabled = enabled;
angular.bootstrap(document, ['app']);
});
});
});
$stateProvider
.state('home', {
url: enabled ? '/' : '/customer',
templateUrl: 'views/home.html',
controller: 'HomeCtrl',
data: {
title: 'Home'
}
})
And then in my controller, use the same variable in my init method for example:
$scope.init = function () {
if (enabled) { ... }
else { ... }
}
$scope.init();
I notice that the enabled variable eventually does get set, but it happens after the state is decided. Is there a way to set a global variable before my AngularJS app starts in order to use it anywhere?
Thanks

How to get rid of content flash using $firebaseObject

Currently I'm loading my firebase data right in my controller like this:
app.controller("MyCtrl", function ($scope, $firebaseObject) {
$scope.users = {};
var myDB = new Firebase('https://mydb.firebaseio.com/');
$firebaseObject(myDB.child("users")).$bindTo($scope, "user");
});
This way after the page is loaded, there is a delay during which the data is retrieved from firebase, therefore at first the page appears to be empty until the data arrives. This is unconvinient.
In order to prevent this flashy behavior I wanted to resolve the required data in my $routeProvider but wasn't able to get a promise.
How to properly get rid of the content flashing?
Is there a way to get a promise so one can resolve the $firebaseObject before the view is shown?
Please do not offer quick-and-dirty-solutions containing ng-cloak, ng-hide, ng-show etc.
You can use the $loaded() promise in your resolve function.
app.config(["$routeProvider", function($routeProvider) {
$routeProvider.when("/home", {
// the rest is the same for ui-router and ngRoute...
controller: "HomeCtrl",
templateUrl: "views/home.html",
resolve: {
"myObject": ["$firebaseObject", function($firebaseObject) {
var ref = new Firebase("...");
return $firebaseObject(ref).$loaded();
}]
}
})
https://www.firebase.com/docs/web/libraries/angular/guide/synchronized-objects.html
Check out the AngularFire docs on routing in authentication for better guidance on handling routes with promises in general.
https://www.firebase.com/docs/web/libraries/angular/guide/user-auth.html

AngularJS watch value within service

I'm currently developing an AngularJS web application.
I have a primary view (Index), child view (Dashboard) and grandchild view (Raiding The Rails).
http://localhost:4000/#/dashboard/raiding-the-rails/1
Within the grandchild view (Raiding The Rails) I am displaying dress information relevant to the state ID /1, each dress has a specified state ID e.g /1,/2,/3.
I have a controller/service sending the state ID to a console.log (within the parent) and when viewing raiding-the-rails/1 the console.log displays {stateID: "1"}, If I change the URL to raiding-the-rails/4 the console.log doesn't update unless I refresh the page.
Also, When I completely refresh the browser the console.log spits out three objects instead of one?
I've reviewed many sites and have tried and tried again trying to figure this out, I even tried setting up a Watch service but this failed massively.
If anyone could help me out I would be highly grateful!Thank you.
App:
(function(angular, undefined){
"use strict";
var am = angular.module('virtual-fitting', ['ui.router']);
am.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('dashboard', {
url: '/dashboard',
templateUrl: '../views/dashboard.html'
})
.state('dashboard.raidingtherails', {
url: '/raiding-the-rails',
templateUrl: '../views/dashboard.raiding-the-rails.html'
})
.state('dashboard.raidingtherails.dress', {
url: '/:id',
templateUrl: '../views/dashboard.raiding-the-rails.dress.html'
});
$urlRouterProvider.otherwise('/');
});
})(angular);
Service:
(function(angular, undefined) {
"use strict";
var am = angular.module('virtual-fitting');
am.factory('raidingService', function () {
var raidingService = {
stateID: null
};
return raidingService;
});
})(angular);
Parent Controller:
(function(angular, undefined) {
"use strict";
var am = angular.module('virtual-fitting');
am.controller('dashboardCtrl', function(raidingService) {
console.log(raidingService);
});
})(angular);
Child Controller:
(function(angular, undefined) {
"use strict";
var am = angular.module('virtual-fitting');
am.controller('raidingtherailsCtrl', function($state, $stateParams, raidingService) {
var self = this;
raidingService.stateID = $stateParams.id;
});
})(angular);
I assume it's in the parent controller that you want to watch your service?
If so, you could watch a function, like so:
$scope.$watch(function() {
return raidingService.stateId;
}, function(value) {
console.log(value);
}, true);
That should work.
MVC pattern used in any framework:
You creating Service with .get() and .set() methods. Set method is common to be used in any place you desire to operate model value and allows you to create one point that will handle changes to your model. The final move in .set() method is .$boradcast() notifying whole application about changes to your model.
Pros:
one access point to value
application is know about any changes
no need to write $watch with watching collections (holly-molly)
having access point to changes with '$on' in your controllers $scope
Cons:
easy to forget to use .set() method instead of simple assignment

Angular - Logout redirect route

I am trying to create a route that clears the users session and redirects them back to the root homepage.
.config(function config( $routeProvider, $stateProvider ) {
$routeProvider.
when('/logout', {resolve: {redirect: function(Session){
Session.clear();
return "/home";
}}});
I'm obviously doing something wrong here, as calling
$location.path("/logout");
... ignores the function and redirects back to the default route. I've tried adding console.log statements to the function to see if it is being called.
Am I using the redirect function incorrectly?
Have you considered putting your logout logic in a separate controller? Would be a little cleaner, more robust, & make redirection more straightforward. Like so:
function LogoutController($location) {
Session.clear();
$location.path('/home');
}
The your route is:
when('/logout', {
template: '', //A template or templateUrl is required by AngularJS, even if your controller always redirects.
controller: 'LogoutController'
}).
I had the same issue and what I did instead was create a logout function in my navigationController that gets hit when the URL is clicked
<li>Log Out</li>
And in my navigationController:
$scope.logout = function () {
localStorage.clearAll();
window.location = '/logout';
};
I'm running ASP.NET behind Angular so I needed the browser (not angular) to route to /logout which is mapped in ASP.NET config (does a few other session clean ups and redirects to authentication app)
Hope this helps
just store the $sessionStorage (username) then delete the the $sessionStorage (username) ..
$scope.logout = function(){
delete $sessionStorage.sessname; //sessname is get sessionStorage username
$location.path('/login');
};
help me for this link:https://stackoverflow.com/questions/36056745/angularjs-click-logout-button-to-clear-sessionstorage-again-and-again-go-back-to
I use this approach
$routeProvider
.when('/logout', {
resolve: {
logout: ['authService', function (authService) {
authService.clear(true);
}]
}
})

AngularJS UI-Router scoping issues

I've got what I think is a scoping issue with angular ui-router, but I'm not quite sure.
angular.module('Meeting').controller('MeetingController', ['$scope', 'signalRService', '$stateParams', function ($scope, signalRService, $stateParams) {
$scope.setMeetings = function(meetings) {
$scope.meetings = meetings.map(function(meeting) {
return {
id: meeting.CategoryId,
name: meeting.MeetingName
};
});
$scope.$apply();
};
$scope.connectToSignalR = function () {
signalRService.connect();
signalRService.registerAddMeetingsClientMethod($scope.addMeetings);
};
$scope.requestMeetings = function() {
signalRService.requestMeetings($stateParams.departmentId);
};
$scope.connectToSignalR();
$scope.eventId = $stateParams.eventId;
}]);
Basically, my module is injected with a signalR service, and I register a callback on it to set meetings. I have a button on my view to tell the signalR service to fetch the meetings, which then calls the callback I just registered.
Now, all this works fine with ui-router, but only the first time the page is loaded. Here's my routing config:
angular.module('Meeting')
.config(
['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state("meeting",
{
url: "/meeting/:departmentId/",
templateUrl: '/home/meetingPage',
controller: "MeetingController"
})
.state("meeting.members",
{
url: "/members/",
templateUrl: "/home/memberspage",
controller: "MeetingMemberController"
})
.state("meeting.edit", {
url: "/meetingedit",
views: {
'meetingtime': {
templateUrl: '/home/timepage',
controller: 'MeetingTimeController'
},
'location': {
templateUrl: '/home/locationpage',
controller: 'MeetingLocationController'
}
}
});
}]);
When I load up a meeting state (i.e. mysite/meeting/3), all the signalR methods are called, the meeting model in the MeetingController is populated, and the data appears in the view.
When I navigate to another state (i.e. mysite/meeting/4), the signalR methods are still called, and the meeting model is populated, but then just disappears. If I manually refresh the page with F5, it starts to work again, but navigating to a different page stops everything working.
I'm thinking it's a scoping issue, because when I navigate to a different page, the meetings object is still populated from the previous page.
The way I was registering callbacks with a singleton signalR service was getting really cumbersome, and doesn't play well with ui-router, as I found out.
I switched to using promises, and everything works so much more elegantly. Basically, I have a method on my signalR hub that returns the object I want:
public List<Meeting> GetMeetingsForMember(int memberId)
{
return _meetingRepository.GetAllUpcomingMeetingsForMember(int memberId);
}
Then, in my controller, I create a promise, and pass it to my signalR service for resolution:
var deferred = $q.defer();
deferred.promise.then(
function (meetings) {
setMeetings(meetings);
}
);
signalRService.getMeetingsForMember(memberId, deferred);
The getMeetingsForMember method on my signalR service accepts the promise and resolves it:
getMeetingsForMember = function (memberId, deferred) {
deferred.resolve(signalRService.hub.server.getMeetingsForMember(memberId));
}

Resources