So I have been trying to implement google single sign on into my angular application; however, sometimes when I reload the page the button disappear. My angular application is using angular routing. If I were to put my button outside of this it would work as expected. It just runs into problem when its loaded through a partial. Any idea how I can fix this?
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<div ng-view></div>
As #agektmr said, the problem is related to the way angular and platform.js interact with each other.
In order to use the auto rendered button you need to trigger the library when the DOM is loaded.
What I did is calling the following code in the onComplete method (I'm working with AngularMaterial dialogs, but you should be able to find a similar method quite easily):
$timeout(function() {
$window.gapi.signin2.render('g-signin2');
});
The only difference is in your html you should change your div and instead of adding it a g-signin2 class you should add an g-signin2 id:
<div id='g-signin2' data-onsuccess='yourMethod'></div>
If you're willing to learn more about Google's implementation you could take a look here.
I'd recommend using imperative approach for implementing the button for this.
<div id="signin">
<button>sign-in</button>
</div>
<script>
document.querySelector('#signin').addEventListener('click', function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signIn();
});
</script>
Find more concrete example here
https://github.com/GoogleChrome/google-sign-in
The code you indicated didn't work because of timing. platform.js library tries to take care of it but fails because it's before angular renders DOM.
Related
I have some problems using Material Design Lite (getmdl.io). I followed the steps showed in the getmdl.io web in order to install it (actually I use bower), but I always have the same problem, when I change the ng-route in my web, some resources don't render properly, I need to reload the page to get it properly rendered, for example.
First I have this:
then when I reload, I get what I want:
What I cant understand is why other resources like google icons or buttons work correctly but the menu button on the nav bar and other resources like this one need to reaload the page in order to render properly.
I try to include the library using the hosted method and bower method.
Any idea what is going on?
i past in my code this function
angular.module('app', ['ngRoute']).
run(function($rootScope, xxxx, xxx){
$rootScope.$on('$viewContentLoaded', function(event, next) {
componentHandler.upgradeAllRegistered();
});
});
It worked perfect! Good luck..
Libraries like MDL work by waiting for the page to load using the DOMContentLoaded event, scanning the page for things like input elements and manipulating them with JavaScript so that they can inject the bits and pieces needed to work with their components. This works fine on static websites, but the DOMContentLoaded event only fires once, so when Angular performs a page transition, the DOM changes without MDL knowing about it.
Material Design Lite has a section in its FAQ about using MDL on dynamic websites:
Material Design Lite will automatically register and render all elements marked with MDL classes upon page load. However in the case where you are creating DOM elements dynamically you need to register new elements using the upgradeElement function. Here is how you can dynamically create the same raised button with ripples shown in the section above:
<div id="container"/>
<script>
var button = document.createElement('button');
var textNode = document.createTextNode('Click Me!');
button.appendChild(textNode);
button.className = 'mdl-button mdl-js-button mdl-js-ripple-effect';
componentHandler.upgradeElement(button);
document.getElementById('container').appendChild(button);
</script>
Of course, this probably isn't terribly easy to do in your case, since you'd have to manually find each new element and call upgradeElement on it.
Usually, instead of doing this sort of event-based DOM manipulation, Angular uses directives to initiate DOM changes. Consider using a library built to interoperate with Angular, instead, such as Angular Material.
I need to implement a browser-like banner warning system so that the user can only access certain features after they've acknowledged certain warnings. The warning messages will depend on data sent from the server and I have to implement different styles and links.
Edit: There could be any number of warnings displayed at the same time. One for each feature. The user must individually acknowledge each warning before the corresponding feature is enabled. Some of the warning texts are static, some are dynamic. Some can have different acknowledge links instead of the standard "okay".
Traditionally, I would package each kind of warning into a class (in the OO sense) and push them to screen through a centralized method, e.g.
displayWarning(new InfoBanner("You must ... before you can modify " + data.name, function onclick() { ... }));
Here the InfoBanner class will have a method that creates the banner elements and attach the event handler.
The Angular way of doing this, on the other hand, seems to be you write the banner entirely in HTML with ng-if and ng-click, etc. E.g.:
<p style="info banner" ng-if="...">You must ... before you can modify {{...}}. <a href ng-click="...">Okay</a></p>
However, this seems quite unfocused and messy because there will now be a large blob of banner code dwarfing the functional part of the page. (There are hundreds of error types defined!)
Is there any way to resolve this without reverting to the fully imperative code?
(Note: a custom directive is probably not the answer as <p style="info banner" is almost like a directive and there's little sharable code among these warnings beyond this.)
(Edit: One can see this question in another way: in the imperative world, the warning-adding logic are scattered in the code but close to the feature they're protecting, so they're easy to understand and maintain. In the declarative world, they must be centralized to the place where they're displayed. I would like a solution where they're declared close to the component they're protecting but displayed centrally.)
What I understand from your question your problem is that since you are in an Angular application you need / should include your banners as markup in your HTML view, since however whether each banner is displayed or not depends on the data you are getting from the server you only know if a specific banner should be displayed in your controller (hence the ng-if you have included in your banner HTML example).
What I would propose in this case would be to create a BannerService which would hold a list of all the banners that should be displayed at any given time. In your controller you can use the functions exposed by the service to add banners to the list when the data you got from the server indicates that you should do so. Each banner in the list would be an object containing all the information that might be different between different banners (ex. banner text, type, etc.) meaning that your HTML view doesn't really need to "know" anything about specific banner details and can just display all the banners available in the BannerService using an ng-repeat.
You can see below a quick-and-dirty example to better understand how this would work.
var app = angular.module('TestApp', [])
app.service('BannerService', [function(){
var banners = [];
this.getBanners = function() {
return banners;
}
this.addBanner = function(banner) {
banners.push(banner);
}
}])
app.controller('TestCtrl', ['BannerService', function(BannerService) {
// Add banners to the banner service depending on your data etc.
BannerService.addBanner({text: "This is a banner", type: "info"});
}])
app.controller('BannerCtrl', ['$scope', 'BannerService', function($scope, BannerService) {
$scope.banners = []
$scope.$watch('BannerService.getBanners', function (newVal, oldVal, scope) {
$scope.banners = BannerService.getBanners();
});
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="TestApp">
<div ng-controller="BannerCtrl">
<div ng-repeat="banner in banners"><p class="banner {{banner.type}}">{{banner.text}}</p></div>
</div>
<div ng-controller="TestCtrl">Page content</div>
</div>
In my opinion what you are looking to implement fits perfectly with Aspect Oriented Programming. If you've never used AOP before be prepared for some light reading. The concept is simple and works very well with Angular's patterns. There is an AngularAOP project, but before you dive into it I suggest running through this article first:
http://www.bennadel.com/blog/2425-decorating-scope-methods-in-the-angularjs-prototype-chain.htm
All I am trying to do is include an anchor tag inside the html of a partial that links to an external site. Were this standard html, the code would simply be:
google
As simple as this is, I cannot seem to find a working solution for getting past angular intercepting the route (or perhaps replacing my anchor with the https://docs.angularjs.org/api/ng/directive/a directive unintentionally?).
I have scoured SO and the rest of the web and seen a myriad of solutions for dealing with: links within the same domain, routing within the SPA, routing within a page (ala $anchorScroll) but none of these are my issue exactly.
I suspect it may having something to do with using $sce but I am an Angular n00b and not really sure how to properly use that service. I tried the following in my view controller:
$scope.trustUrl = function(url) {
return $sce.trustAsResourceUrl(url);
}
with the corresponding:
<a ng-href="{{ trustUrl(item) }}">Click me!</a>
(as described here: Binding external URL in angularjs template)
but that did not seem to do the trick (I ended up with just href="{{" in the rendered page).
Using a plain vanilla anchor link like this:
google
also failed to do the trick (even though some online advised that standard href would cause a complete page reload in angular: AngularJS - How can I do a redirect with a full page load?).
I also tried adding the target=_self" attribute but that seemed to have no effect either.
Do I need to write a custom directive as described here?
Conditionally add target="_blank" to links with Angular JS
This all seems way too complicated for such a simple action and I feel like I am missing something obvious in my n00bishness, at least I hope so because this process is feeling very onerous just to link to another url.
Thanks in advance for any solutions, advice, refs or direction.
It turns out that I did in fact have all anchor links in the page bound to an event listener and being overridden. Since that code was fundamental to the way the page worked I did not want to mess with it. Instead I bypassed it by using ng-click to call the new url as follows:
HTML:
<a class="navLinkHcp" href="{{hcpurl}}" title="Habitat Conservation Plan" target="_blank" ng-click="linkModelFunc(hcpurl)">Habitat Conservation Plan</a>
Controller:
$scope.hcpurl = 'http://eahcp.org/index.php/about_eahcp/covered_species';
$scope.linkModelFunc = function (url){
console.log('link model function');
$window.open(url);
}
And voila! Good to go.
Thanks again to KevinB for cluing me in that this was probably the issue.
My problem
I am using Angular to create a Phonegap application. Most of my pages are fairly small and the transition/responsiveness is quick and smooth. However I have one page that is fairly large that I am having an issue with.
The method for changing to this page is straightforward:
<button ng-click="$location.url('/page2')"></button>
When you "tap" the button above it takes about 1-2s to respond and change pages. I have double checked all areas for improvement on this page and determined that the delay is caused by Angular compiling and parsing the DOM of this page prior to changing the page. Please note that I am testing this on a real device so it is not due to emulator speeds.
The question
Is there a way to automatically or manually intercept page changes and put them in a sort of "loading" page so the response to the button click is immediate and page change is visible but the page content loads in a second or 2 later onto this "loading" page.
Its only an issue cause it is very awkward to click something and have nothing happen. I am having a very hard time finding any resources on this matter so if someone can even point me in the right direction to look I would be grateful.
Edit:
A super hacky solution I found was to use an ng-include on wrapper page and delay the include for a little bit.
myBigPageWrapper.html:
<div ng-include="page"></div>
Controller:
$scope.page = '';
setTimeout(function() { $scope.page='/pages/myBigPage.html'; $scope.$apply(); }, 1000);
Then navigate to your wrapper page instead: $location.url('/myBigPageWrapper')
This is obviously not ideal... But I hope this helps clarify what I am attempting to do.
Page2.html
This is the section that causes the page to slow down, commenting this out makes the page load very quickly. There are 13 pages in the "auditPages" array each containing about 50 lines of html mostly containing form input elements. Quite a bit of logic however it runs great once it is loaded. I am not going to include all the pages as it would be overload.
<div class="page-contents">
<form name="auditPageForm">
<div ng-repeat="(pageKey, pageData) in auditPages " ng-show="currentAuditPage.name==pageData.name">
<audit-form page="pageData">
<ng-include src=" 'partials/audit/auditSections/'+pageData.name+'.html'" onload="isFormValid(pageKey)"></ng-include>
</audit-form>
</div>
</form>
</div>
To sum up my comments above:
Your question was:
Is there a way to automatically or manually intercept page changes and
put them in a sort of "loading" page?
A lot of people asks for this question since Angular doesn't seem to provide a nice handling of a loading transition.
Indeed, the possible nicest solution would have been to "play" with the resolve property of angular's module configuration.
As we know, resolve allows to run some logic before the targeted page is rendered, dealing with a promise. The ideal would be to be able to put a loading page on this targeted page, while the resolve code is running.
So some people have nice ideas like this one:
Nice way to handle loading icon while route is changing
He uses $routeChangeStart event, so the loading icon would happen on the SOURCE page.
I use it and it works well.
Also, there is another way: make use of $http interceptor (like #oori answer above), to have a common code allowing to put a loading icon but...I imagine you don't want the same icon on every kind of http request the page does, it's up to you.
Maybe in the future, a solution would come directly associated to the resolve property.
Angular has $httpProvider.responseInterceptors
// Original by zdam: http://jsfiddle.net/zdam/dBR2r/
angular.module('LoadingService', [])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('myHttpInterceptor');
var spinnerFunction = function (data, headersGetter) {
angular.element(document.getElementById('waiting')).css('display','block');
return data;
};
$httpProvider.defaults.transformRequest.push(spinnerFunction);
}])
// register the interceptor as a service, intercepts ALL angular ajax http calls
.factory('myHttpInterceptor', ['$q','$window', function ($q, $window) {
return function (promise) {
return promise.then(function (response) {
angular.element(document.getElementById('waiting')).css('display','none');
return response;
}, function (response) {
angular.element(document.getElementById('waiting')).css('display','none');
return $q.reject(response);
});
};
}])
I make a project in worklight used dojo mobile 1.8.1 and angularjs 1.0.1,but i got a strange problem.
Here is my html part:
<div data-dojo-type="dojox.mobile.ScrollableView" data-dojo-props="selected:true" id="id1" ></div>
<div class="full" data-dojo-type="dojox.mobile.View" id="id2"></div>
and my JavaScript part:
require([
"dojo", "dijit/_base/manager","dojo/parser", "dijit/registry",
], function(dojo) {
dojo.ready(function() {
// dijit.byId("id1").performTransition("id2"); //////////place I
});
});
var angularApp = angular.module('app', [])
.run(['$rootScope','$templateCache','$route',
function($rootScope,$templateCache,$route) {
dijit.byId("id1").performTransition("id2");////////place II
}]);
The problem is at place I, it works well, but when I put "dijit.byId("id1")" at place II, it shows:
dijit.byId("").is not defined
The ready function is executed after dojo parsed your document & constructed the widgets you try to get using dijit.byId.
The second part is not placed within the ready function, so dojo can't find your elements yet !
Solution: Access your elements in the ready function OR do not declare them declaratively (like you did, using html code...) !
Lucian
The dojo.ready() function registers an event-handler function (callback) which will be fired after the DOM got completely parsed.
This comes in very handy if you want to be sure that every html element got rerendered as dojo-widget before you perform operations on them.
So, in your example, Code II will be executed before the dijit.byId() function has been made available by loading the necessary modules (dijit/registry, ...). Code II would only work after the dom-ready event got fired and your "dojo.ready()" function did load the required modules.
You should definately invest 5 minutes in reading what dojo.ready() is about:
http://dojotoolkit.org/reference-guide/1.8/dojo/ready.html
Sidenote:
You shouldn't use dijit.byId() any more in Dojo 1.8.1. Try using dijit.registry.byId() (you have to require the dijit/registry module).
http://dojotoolkit.org/reference-guide/1.8/dijit/registry.html#dijit-registry-byid