How to prevent services from firing before user is authorized in AngularJS app - angularjs

I have a service that is called to get the authorized user's location list. This information is used to populate a select list and they can switch locations to view different information.
The code for the select list is in the nav bar in the index.html but I don't want the service to fire on the page until after they user has been successfully authenticated and the app switches to the main view.
I'm using ng-show to hide the navigation bar with an isAuthorized() function for the AuthCtrl. However, the service call from the LocationCtrl to populate the ng-repeat always fires whether the user is logged in or not. I could move the nav component into the main view but then I'd need to have it in all the views that are protected. What is the best way to handle populating service-based menu items in a single page app?
index.html
<!doctype html>
<html lang="en" ng-app="betterbooks">
<head>
scripts...
</head>
<body>
<nav class="navbar-fixed-top" ng-controller="AuthCtrl as auth">
<div class="container" ng-show="auth.isAuthorized()">
<ul class="navbar-right">
<li>Home</li>
<li class="dropdown">
Accounts <span class="caret"></span>
<ul class="dropdown-menu" ng-controller="LocationCtrl">
<li ng-repeat="location in locations">
<a href="#/location/{{ location.id }}/">
{{ location.name }}
</a>
</li>
</ul>
</li>
<li><button ng-click="auth.logout()">Logout</button></li>
</ul>
</div>
</nav>
<div class="view-container" style="height:100%">
<div ui-view class="view-frame"></div>
</div>
</body>
</html>

Use an ng-if instead. The navbar won't be loaded into the dom until auth.isAuthorized() is true, so the LocationCtrl won't render or run until that point.

Related

use ngStorage values in the template of site

I've got a site that uses ngStorage to save cart data until checkout, when it is then stored in the database. When using vm.$storage = $localStorage; in my controller, I can access the cart information easily for the part of the page in the section of index.html but I WANT to be able to access and display information from ngStorage in the header of my site, which is nested inside .
I've tried adding ng-controller="SiteController" to my li tag but still don't get anything to display that way.
How can I get the localStorage using ngStorage to display in the template (index.html) of my site?
My index.html file is setup something like this:
<html ng-app="myApp">
<body>
// a bunch of code to show my other navigation
// code to show my shopping cart at a glance
<li class="quick-cart">
<a href="#">
<span class="badge badge-aqua btn-xs badge-corner">
{{vm.$storage.cart.items.length}}
</span>
<i class="fa fa-shopping-cart"></i>
</a>
<div class="quick-cart-box">
<h4>Shop Cart</h4>
<div class="quick-cart-wrapper">
<a href="#"><!-- cart item -->
<h6>Item Name</h6>
<small>$12.34</small>
</a><!-- /cart item -->
<!-- cart no items example -->
<!--<a class="text-center" href="#"><h6>0 ITEMS ON YOUR CART</h6></a>-->
</div>
<!-- quick cart footer -->
<div class="quick-cart-footer clearfix">
VIEW CART
<span class="pull-left"><strong>TOTAL:</strong> $54.39</span>
</div>
<!-- /quick cart footer -->
</div>
</li>
//other code between the header and controller content
<div ng-view></div> //the CONTENT section controlled by each controller
//other code to finish the page
My site-controller.js code is:
/* global angular */ angular.module('myApp')
.controller('SiteController', SiteController);
function SiteController($route, $routeParams, AuthFactory, jwtHelper, $window, $localStorage, $sessionStorage) {
var vm = this;
vm.$storage = $localStorage;
vm.$storage.cart = vm.$storage.cart;
console.log(vm.$storage.cart);
}
Since you are using controllerAs syntax in controller you need to use the as alias in ng-controller
ng-controller="SiteController as vm"
This defines the vm you are already using in the view for:
{{vm.$storage.cart.items.length}}

Angular - Page does not bind variable outside view when specific view url is requested

I tried to solve the problem but could not find the solution for this one. I have 2 views home and cart. Home displays all the products and cart displays all the items added to the cart. Backend for this is NodeJS which just servers index file and processes views.
In the following index file, I have {{fullCart.totalItems}} . When I try localhost:3000 then it binds perfectly by showing 0 and routes to localhost:3000/home but when I try localhost:3000/home url directly in the browser then it does not bind {{fullCart.totalItems}} so nothing is displayed
index.html
<body ng-app="bagitApp" ng-init="quanArr=[1,2,3,4,5,6,7,8,9,10]">
<div class="container-fluid nav-color navbar-fixed-top" ng-controller="headerController">
<div class="container">
<a class="navbar-brand" href="#"><img class="img-responsive" src="images/logo.png"></a>
<nav>
<div class="navbar-header">
<button type="button" class="navbar-toggle nav-btn" data-toggle="collapse" data-target="#navBar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="navBar">
<ul class="nav navbar-nav">
<li>Home</li>
<li>Cart</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><i class="glyphicon glyphicon-shopping-cart"></i><i class=" teal new badge">{{fullCart.totalItems}}</i></li>
</ul>
</div>
</nav>
</div>
</div>
<div class="container pan-mar" ng-view>
</div>
</body>
main.controller.js
var app=angular.module('bagitApp',['ngRoute']);
app.config(['$routeProvider','$locationProvider',function($routeProvider,$locationProvider){
$routeProvider
.when('/home',{
templateUrl:'partials/Products Panel.html',
controller:'productsController'
})
.when('/cart',{
templateUrl:'partials/cart.html',
controller:'cartController'
})
.otherwise({
redirectTo:'/home'
});
$locationProvider.html5Mode({enabled:true,requireBase:false});
}]);
app.factory('cartService',function(){
return {
cart:[],
totalItems:0
};
});
function headerController($rootScope,$scope,cartService){
$scope.fullCart = cartService;
console.log("headerController")
console.log($scope.fullCart)
}
function productsController($rootScope,$scope,cartService){
$scope.fullCart = cartService;
$scope.products = products;
console.log("productsController")
console.log($scope.fullCart)
$scope.addToCart=function(item,quantity){
$scope.fullCart.cart.push({item,quantity:parseInt(quantity)});
$scope.fullCart.totalItems += parseInt(quantity);
console.log($scope.fullCart);
}
$scope.removeFromCart=function(itemId){
}
}
app.controller('productsController',['$rootScope','$scope','cartService',productsController])
app.controller('headerController',['$rootScope','$scope','cartService',headerController])
Server.js
var express = require('express');
var server = express();
var engines = require('consolidate');
server.set('views',__dirname);
server.engine('html',engines.mustache);
server.set('view engine','html')
server.use(express.static(__dirname));
server.get('/:name',function(req,res){
res.render('index');
});
server.listen(3000,function(){
console.log("Listening on localhost:3000");
})
Your problem is that when you go to
localhost:3000
then on the server your express.static serves index.html as a static asset so it's not render using mustache.
Since mustache uses same syntax {{ }} as angular then when you go to /home route handler for /:name renders index.html using mustache and since you dont pass in any variable then mustache simply deletes this
{{fullCart.totalItems}}
what you should do is
1.create public folder and place all public assets in it and change
server.use(express.static(__dirname));
to
server.use('/public', express.static(__dirname));
-html templates into templates subfolder
-js files like angular.js into js subfolder, etc.
2.create views folder and place your index.html in it.
This way you avoid index.html is servered once as static and once render with mustache. from now on it's always rendered. Unfortunately this will lead to another problem which is mustache will overwrite or delete any {{}}
So one option is to change angular {{}} to something like [[ ]] by adding
appModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
Then you could use it in your template:
<div>[[message]]</div>
Or you can most probably change {{}} to something else in mustache too but i don't know myself but you can have a look at mustache docs
On the node service you will want to add this.
app.get('*', function(req, res, next){
res.sendFile(PATH_TO_INDEX);
});
This will respond with the index.html on any other route, and then once the angular app bootstraps it will take over on routing and render the correct page.
In your angular app you will want to add this as well for each script/css file.
<base href="/">
Make sure it is above any of your scripts or stylesheets though.

AngularJS view without using main ui-view template?

I'm creating an application that provides a logged in user with the ability to create a poll (with questions and choices) in their dashboard.
Once the poll has been created I would like to redirect to the poll page, which will have a unique url (ex: http://example.com/p/3eRr4g6).
I would like this page to NOT use the dashboard template.
How does one accomplish this?
Here's my dashboard view:
Example Poll Page/Template:
UPDATE: To show my current index file and how I have it structured. (In reply to koox00's response)
<body class="hold-transition skin-purple sidebar-mini">
<div class="wrapper">
<div ng-include="'components/navbar/navbar.html'"></div>
<div ng-include="'components/sidebar/sidebar.html'"></div>
<div class="content-wrapper">
<div ui-view></div>
</div>
<div ng-include="'components/footer/footer.html'"></div>
</div>
</body>
You can use named views.
Create a state that loads the default template in that view e.g main and make every other state a child of this one if you want to share data.
In the desired state you can load over the main view the html you want.
update
default.html
<div class="wrapper">
<div ng-include="'components/navbar/navbar.html'"></div>
<div ng-include="'components/sidebar/sidebar.html'"></div>
<div class="content-wrapper">
<div ui-view></div>
</div>
<div ng-include="'components/footer/footer.html'"></div>
</div>
index.html
<body>
<div ui-view="main"></div>
</body>
take a look at nested states also if you want to share data between states parent/child.

AngularJS - Change navigation menu based on user logged

I am using SpringBoot and Angular JS for my web application and I would like to know a good way to change the navigation menu based on the user logged.
I was placing the navigation bar inside all the views but im sure there is a better way to do it.
User.class
#Entity
public class Usuario implements Serializable {
#Enumerated(EnumType.STRING)
private Rol rol; -> This can be "UserA","UserB","UserC"
}
And my different navigation menus are
Navigation for UserA
<nav id="nav">
<ul>
<li>A1</li>
<li>A2</li>
<li>A3</li>
</ul>
</nav>
Navigation for UserB
<nav id="nav">
<ul>
<li>B1</li>
<li>B2</li>
<li>B3</li>
</ul>
</nav>
Navigation for UserC
<nav id="nav">
<ul>
<li>C1</li>
<li>C2</li>
<li>C3</li>
</ul>
</nav>
Thanks!
You can use ng-show, ng-if or ng-switch. Beter use ng-if or ng-switch as it doesn't create html for other roles and other users wont be able to see links for other roles if they do inspect element. ng-show will just hide only(adds the display:none style)
In controller get the role
JS
$scope.role = $http.get("some url to get role");
HTML
<nav id="nav" ng-switch='role'>
<ul ng-switch-when='userA'>
<li>A1</li>
<li>A2</li>
<li>A3</li>
</ul>
<ul ng-switch-when='userB'>
<li>B1</li>
<li>B2</li>
<li>B3</li>
</ul>
<ul ng-switch-when='userC'>
<li>C1</li>
<li>C2</li>
<li>C3</li>
</ul>
<ul ng-switch-default='userDefault'>
<li>Default1</li>
<li>Default2</li>
<li>Default3</li>
</ul>
</nav>
Default is optional
For all nav add ng-show and control your rol. And you have set your rol to $scope.role
<nav id="nav" ng-show="role=='userA'">
<ul>
<li>A1</li>
<li>A2</li>
<li>A3</li>
</ul>
</nav>
If you don't want to use ajax call;You can set $scope.role value on your jsp and when user changed result will be changed. You can use "JSTL" or "Jsp shortTag" shortTag example;
<nav id="nav" ng-show="<%=rol%>=='userC'">
<ul>
<li>C1</li>
<li>C2</li>
<li>C3</li>
</ul>
</nav>

angular 1.2, how would a router load views without making get calls?

I've been going over the current (angular 1.2.16) routing and multiple views method for angular. Its detailed here. In this we see that for every route there is a get request to load the partial html.
How would I change this so all get requests for views happen when the app instantiates and then the routes switch the views without making further calls to the server?
Suppose that you want to change the content of a div depending on what is stored in data.mode. You need to have first a mechanism to change the value of data.mode and that's entirely up to you.
<div ng-switch on="data.mode">
<div ng-switch-when="first_value">
<!--Your first partial page content-->
</div>
<div ng-switch-when="second_value">
<!--Your second partial page-->
</div>
<div ng-switch-when="second_value">
<!--Your third partial page-->
</div>
<div ng-switch-default>
<!--Default content when no match is found.-->
</div>
</div>
You can do what they suggest here and use ui-router
i.e.
<!-- index.html -->
<body>
<div ui-view="viewA"></div>
<div ui-view="viewB"></div>
<!-- Also a way to navigate -->
<a ui-sref="route1">Route 1</a>
<a ui-sref="route2">Route 2</a>
</body>

Resources