Angularjs menu login logout loading - angularjs

I am using Angularjs in a project.
For login logout I am setting a scope variable like below:
$scope.showButton = MyAuthService.isAuthenticated();
In markup its like
<li ng-show="showLogout">Logout</li>
When I logout it redirect to the login page but logout menu doesn't disappear.
Also tried like this:
$scope.showButton = MyAuthService.isAuthenticated();
In markup:
<li ng-class=" showLogout ? 'showLogout' : 'hideLogOut' ">Logout</li>
Seems scope change is not reflecting in my view, but when I reload page "logout menu" disappears as expected.
I also tried with directives like below:
MyApp.directive('logoutbutton', function(MyAuthService) {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
attrs.$observe('logoutbutton', function() {
updateCSS();
});
function updateCSS() {
if (MyAuthService.isAuthorized()) {
element.css('display', 'inline');
} else {
element.css('display', 'none');
}
}
}
}
});
No luck with that too.
How can I hide it when the logout is successful and also after successful login how can I show "logout button"?

Setup a watch on MyAuthService.isAuthenticated() and when that fires, set your scope variable to the result of that service call. In your first example, the scope variable is only getting set once when the controller is initialized (I am assuming that's where it is being run). You can set the watch up in the controller or, if you want to use a directive, in the directive link function.
Something like this:
$scope.$watch(MyAuthService.isAuthenticated, function(newVal, oldVal){
$scope.showButton = newVal;
});

Edit: After read the MarkRajcok comment I realized that this solution is coupling view from business logic layer, also it exposes the service variable to be changed outside the service logic, this is undesirable and error prone, so the $scope.$watch solution proposed by BoxerBucks it's probably better, sorry.
You can use $scope.$watch as in the BoxerBucks answer, but I think that using watchers isn't proper for services, because usually you want to access services variables in differents controllers expecting that when you change that services variables, all the controllers that inject that service will be automatically updated, so I believe that this is a good way to solve your problem:
In your MyAuthServices do this:
app.service('MyAuthService', function(...){
var MyAuthServiceObj = this;
this.authenticated=false; // this is a boolean that will be modified by the following methods:
// I supose that you have methods similar to these ones
this.authenticateUser(...){
...
// At some point you set the authenticated var to true
MyAuthServiceObj.authenticated = true;
}
this.logout(){
....
// At some point you set the authenticated var to false
MyAuthServiceObj.authenticated = false;
}
});
Then in your controller do this:
$scope.myAuthService = MyAuthService;
finally in your html:
ng-show="myAuthService.authenticated"
And this should work without using a watcher like in BoxerBucks answer.
Check this excellent video about AngularJS providers, to understand how to use services properly.

Related

Sharing data between a directive and a pagination controller in Angular during an API request

I'm sure similar questions have been asked many times on Stack Overflow, but I am still confused.
I have an Angular app that shows a list of articles. The list is long, so it needs to be paginated. For pagination, I'm using UI Bootstrap.
So the app is currently organized like this:
there is a directive ("home directive") that draws the list of articles
there is a UI Bootrstrap directive for pagination with its own controller
or, schematically:
Now, when I change the page in the paginator, I need to send the request to the the server, get a new batch of articles and show them in the home directive:
So the paginator's controller is sending the request, but the home directive needs to be aware of the new response and to use it for drawing a new list of articles.
How is this done in Angular? I've read about services/factories, but am not entirely clear how the home directive becomes aware of the new response from the server. Will I need watchers in the home directory, or is this done differently? Could you please explain?
UPDATE:
OK, here is my (failing) attempt to write a service:
home-directive.js
angular.module('myApp')
.directive("homeDirective", [ 'ArticleService', function(ArticleService){
return {
restrict: 'E',
templateUrl: "home.html",
scope: { },
link: function(scope, element, attrs){
ArticleService.getArticles();
scope.$watch('ArticleService.articles', function () {
console.log(ArticleService.articles);
scope.articles = ArticleService.articles;
});
}
}]);
article-service.js
angular.module('myApp')
.service('ArticleService', ['$http', function($http){
var that = this;
this.getArticles = function(){
$http.get('/path/to/api').success(function(data) {
console.log('working');
that.articles = data;
console.log(that.articles);
});
};
}]);
The call to ArticleService.getArticles() from the directive starts the getArticles() function, as I can see from the logger in the console. The server sends its response. that.articles changes. However, the directive fails to register the change of ArticleService.articles and doesn't get the data.
UPDATE 2
OK, if in the directive, before setting a watcher, I add this line in the directive:
scope.ArticleService = ArticleService;
then it will work.

Detecting window focus the angular way?

Is there a good angular way to detect window focus? I am using html5 notifications and I would like to only fire if the window is out of focus.
Thanks!
There's a built-in angular directive ngFocus here maybe it helps if you attach it to the body
<window, input, select, textarea, a
ng-focus="">
...
</window, input, select, textarea, a>
Edit: For window focus, there's the $window wrapper and you can do something like:
$window.onfocus = function(){
console.log("focused");
}
Edit #CristiBerceanu is right - you should use the built-in ng-focus directive. However, take this answer as a guideline for any missing event you want to bind.
You must create a directive:
angular
.module('MyModule', [])
.directive('onFocus', function(){
return {
restrict: 'A',
scope: {
'focus': '&onFocus'
},
link: function($scope, $element, $attributes) {
var focus = function(event) {
$scope.focus({'$event': event});
};
$element.on("focus", focus);
$scope.$on('$destroy', function(){
$element.off('focus', onClick);
});
}
}
});
Notice how the event is bound in the directive by jquery and NOT directly in the controller. Additionally, notice that a bound expression is tied using the & prefix (evaluable expression binding) instead of regular prefixes like # (text-binding) or = (scope property reference, bi-directional, binding).
In Cristi Berceanu's answer, he suggests assigning a function to $window.onfocus, which does work. However, there is a problem with that... only one function can be assigned to $window.focus at a time. Thus, by assigning a function to $window.onfocus, you could accidentally overwrite a previous function, and your function will be vulnerable to being overwritten later, too.
Here's a different solution that allows multiple functions to run with the window's focus or blur events:
var onFocus = function () {
// do something
};
var onBlur = function () {
// do something else
};
var win = angular.element($window);
win.on("focus", onFocus);
win.on("blur", onBlur);
This will allow you to assign multiple functions to the focus and blur events for the $window object.
If you added the functions inside a controller and want to remove those functions when the controller is destroyed, you can do something like this:
$scope.$on("$destroy", function handler() {
win.off("focus", onFocus);
win.off("blur", onBlur);
$interval.cancel(interval);
});
Solution inspired by this post: https://www.bennadel.com/blog/2934-handling-window-blur-and-focus-events-in-angularjs.htm
you can write a directive to attach to the body element and inside it you can use $window.onfocus event to notify your angular app using events or a service, the same thing you can do from inside a service, it all depends on your architecture

Using scope in directive and service

I am trying to create a developer console in my Angular app and I have the following problem.
On every page of my application I want my developer console to be available (like a floating thing in a corner of the screen). I have HTML template and I am switching content with ui-view. My controllers can include consoleService and then call something like myConsole.log(...). This function should add message to some array and then display it on web. For my console I have directive with HTML template, where I want to use ng-repeat to display all messages.
And now my problem. How should I design this? I was thinking like this:
First solution
Well, I can create global JS variable, store all messages there and then use <script> and write em out in my directive template.
WRONG! Not respecting Angular-way of doing things
Second solution
Ok, I`ll use $scope and put my messages there, and then I can use that magical Angular data bind to just let it work on its own. Ok, but here I have a problem, how can I inject $scope into my service?
WRONG! Injecting $scope into service is nonsense, because it would not make any sense, and again, it kinda oppose the Angular-way.
Third solution
Fine, lets store my messages on console service, since all services in Angular are singletons it should work pretty fine, but... there is no $scope inside a service, so I need to return this array of messages to a controller and then assign it to $scope in controller.
??? What do you think? My problem with this solution is that in every controller I need to assign message array to $scope and I just dont want to do that, since I need this console everywhere, on every page.
I hope that my explanation of what I want to do is clear, so I am just hoping for a hint, or advice on how to design it in Angular.
Use the third solution. Your service will have an array with all the messages and each controller can use one of it's functions (service.log(...)) which will basically just add one message to the service list.
Then on your directive, you just assign the getMessages to the directive scope:
var app = angular.module('plunker', []);
app.service('log', function(){
var messages = [];
return {
getMessages: function() { return messages; },
logMessage: function(msg) { messages.push(msg); }
}
})
app.controller('MainCtrl', function($scope, log) {
$scope.msg = '';
$scope.addMessage = function() {
log.logMessage($scope.msg);
};
});
app.directive('console', function(log) {
return {
restrict: "E",
scope: {},
replace: true,
template: "<div><div ng-repeat='msg in messages'>{{msg}}</div></div>",
link: function(scope, el, attr) {
scope.messages = log.getMessages();
}
};
});
plunker: http://plnkr.co/edit/Q2g3jBfrlgGQROsTz4Nn?p=preview

Apply loading spinner during ui-router resolve

resolve property of $routeProvider allows to execute some jobs BEFORE corresponding view is rendered.
What if I want to display a spinner while those jobs are executed in order to increase user experience?
Indeed, otherwise the user would feel the application has been blocked since no view elements were displayed for some milliseconds > 600 for instance.
Of course, there was the way to define a global div element out of the current view to display in order to display the spinner thanks to the $scope.$rootChangeStart function.
But I don't want to hide the whole page with just a poor spinner in the middle.
I want some pages of my webapp differ regarding the way the loading is displayed.
I came across this interesting post containing the exact issue I described above:
That approach results in a horrible UI experience. The user clicks on
a button to refresh a list or something, and the entire screen gets
blanketed in a generic spinner because the library has no way of
showing a spinner just for the view(s) that are actually affected by
the state change. No thanks.
In any case, after I filed this issue, I realised that the "resolve"
feature is an anti-pattern. It waits for all the promises to resolve
then animates the state change. This is completely wrong - you want
your transition animations between states to run parallel to your data
loads, so that the latter can be covered up by the former.
For example, imagine your have a list of items, and clicking on one of
them hides the list and shows the item's details in a different view.
If we have an async load for the item details that takes, on average,
400ms, then we can cover up the load almost entirely in most cases by
having a 300ms "leave" animation on the list view, and a 300ms "enter"
animation on the item details view. That way we provide a slicker feel
to the UI and can avoid showing a spinner at all in most cases.
However, this requires that we initiate the async load and the state
change animation at the same moment. If we use "resolve", then the
entire async animation happens before the animation starts. The user
clicks, sees a spinner, then sees the transition animation. The whole
state change will take ~1000ms, which is too slow.
"Resolve" could be a useful way to cache dependencies between
different views if it had the option not to wait on promises, but the
current behaviour, of always resolving them before the state change
starts makes it almost useless, IMO. It should be avoided for any
dependencies that involve async loads.
Should I really stop using resolve to load some data and rather start loading them in the corresponding controller directly? So that I can update the corresponding view as long as the job is executed and in the place I want in the view, not globally.
You can use a directive that listens on $routeChangeStart and for example shows the element when it fires:
app.directive('showDuringResolve', function($rootScope) {
return {
link: function(scope, element) {
element.addClass('ng-hide');
var unregister = $rootScope.$on('$routeChangeStart', function() {
element.removeClass('ng-hide');
});
scope.$on('$destroy', unregister);
}
};
});
Then you place it on the specific view's loader, for example:
View 1:
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
View 2:
<span show-during-resolve class="glyphicon glyphicon-refresh"></span>
The problem with this solution (and many other solutions for that matter) is that if you browse to one of the routes from an external site there will be no previous ng-view template loaded, so your page might just be blank during resolve.
This can be solved by creating a directive that will act as a fallback-loader. It will listen for $routeChangeStart and show a loader only if there is no previous route.
A basic example:
app.directive('resolveLoader', function($rootScope, $timeout) {
return {
restrict: 'E',
replace: true,
template: '<div class="alert alert-success ng-hide"><strong>Welcome!</strong> Content is loading, please hold.</div>',
link: function(scope, element) {
$rootScope.$on('$routeChangeStart', function(event, currentRoute, previousRoute) {
if (previousRoute) return;
$timeout(function() {
element.removeClass('ng-hide');
});
});
$rootScope.$on('$routeChangeSuccess', function() {
element.addClass('ng-hide');
});
}
};
});
The fallback loader would be placed outside the element with ng-view:
<body>
<resolve-loader></resolve-loader>
<div ng-view class="fadein"></div>
</body>
Demo of it all: http://plnkr.co/edit/7clxvUtuDBKfNmUJdbL3?p=preview
i think this is pretty neat
app.run(['$rootScope', '$state',function($rootScope, $state){
$rootScope.$on('$stateChangeStart',function(){
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$stateChangeSuccess',function(){
$rootScope.stateIsLoading = false;
});
}]);
and then on view
<div ng-show='stateIsLoading'>
<strong>Loading.</strong>
</div>
To further Pranay's answer this is how I did it.
JS:
app.run(['$rootScope',function($rootScope){
$rootScope.stateIsLoading = false;
$rootScope.$on('$routeChangeStart', function() {
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$routeChangeSuccess', function() {
$rootScope.stateIsLoading = false;
});
$rootScope.$on('$routeChangeError', function() {
//catch error
});
}]);
HTML
<section ng-if="!stateIsLoading" ng-view></section>
<section ng-if="stateIsLoading">Loading...</section>
I'm two years late to this, and yes these other solutions work but I find it easier to just handle all this in a just a run block like so
.run(['$rootScope','$ionicLoading', function ($rootScope,$ionicLoading){
$rootScope.$on('loading:show', function () {
$ionicLoading.show({
template:'Please wait..'
})
});
$rootScope.$on('loading:hide', function () {
$ionicLoading.hide();
});
$rootScope.$on('$stateChangeStart', function () {
console.log('please wait...');
$rootScope.$broadcast('loading:show');
});
$rootScope.$on('$stateChangeSuccess', function () {
console.log('done');
$rootScope.$broadcast('loading:hide');
});
}])
You don't need anything else. Pretty easy huh. Here's an example of it in action.
In ui-router 1.0 $stateChange* events are deprecated. Use transition hook instead. See migration guide below for more details.
https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events
The problem with '$stateChangeStart' and '$stateChangeSuccess' is "$rootScope.stateIsLoading" doesn't get refreshed when you go back to last state.
Is there any solution on that?
I also used:
$rootScope.$on('$viewContentLoading',
function(event){....});
and
$rootScope.$on('$viewContentLoaded',
function(event){....});
but there is the same issue.

Understanding the 'directive' paradigm in Angularjs

I have been thinking about directives in Angularjs like user controls in ASP.Net, and perhaps I have it wrong.
A user control lets you encapsulate a bunch of functionality into a widget that can be dropped into any page anywhere. The parent page doesn't have to provide anything to the widget. I am having trouble getting directives to do anything close to that. Suppose that I have an app where, once the user has logged in I hang onto the first/last name of the user in a global variable somewhere. Now, I want to create a directive called 'loggedinuser' and drop it into any page I want. It will render a simple div with the name of the logged in user pulled from that global variable. How do I do that without having to have the controller pass that information into the directive? I want the usage of the directive in my view to look as simple as
<loggedinuser/>
Is this possible?
I guess you can roughly sum up what a directive is as "something that encapsulates a bunch of functionality into a widget that can be dropped into any page anywhere", but there's more to it than that. A directive is a way to extend HTML by creating new tags, allowing you to write more expressive markup. For instance, instead of writing a <div> and a bunch of <li> tags in order to create a rating control, you could wrap it up with a new <rating> tag. Or, instead of lots of <div>s, and <span>s and whatnot to create a tabbed interface, you could implement a pair of directives, say, <tab> and <tab-page>, and use them like this:
<tab>
<tab-page title="Tab 1"> tab content goes here </tab-page>
<tab-page title="Tab 2"> tab content goes here </tab-page>
</tab>
That's the truly power of directives, to enhance HTML. And that doesn't mean that you should only create "generic" directives; you can and should make components specific to your application. So, back to your question, you could implement a <loggedinuser> tag to display the name of the logged user without requiring a controller to provide it with the information. But you definitely shouldn't rely on a global variable for that. The Angular way to do it would be make use of a service to store that information, and inject it into the directive:
app.controller('MainCtrl', function($scope, userInfo) {
$scope.logIn = function() {
userInfo.logIn('Walter White');
};
$scope.logOut = function() {
userInfo.logOut();
};
});
app.service('userInfo', function() {
this.username = ''
this.logIn = function(username) {
this.username = username;
};
this.logOut = function() {
this.username = '';
};
});
app.directive('loggedinUser', function(userInfo) {
return {
restrict: 'E',
scope: true,
template: '<h1>{{ userInfo.username }}</h1>',
controller: function($scope) {
$scope.userInfo = userInfo;
}
};
});
Plunker here.
The Angular dev guide on directives is a must-go place if you want to start creating powerful, reusable directives.

Resources