Google+ signin button with ReactJS - reactjs

I am trying to get Google+ login button to work in my React code. The react code is as following (the actual code has proper value for CLIENT_ID).
React.DOM.div({className: "signin-with-google"},
React.DOM.span({id: "signinButton"},
React.DOM.span({
className: "g-signin",
'data-callback': this.signinCallback,
'data-clientid': "CLIENT_ID",
'data-cookiepolicy': "single_host_origin",
'data-scope': "https://www.googleapis.com/auth/userinfo.email"}
)
)
)
The button shows up properly on the page, clicking on it brings up the OAuth dialog and hitting Accept makes it disappear and no errors/warnings are generated either in the dialog or on the javascript console. So to the best of my knowledge everything is working as expected.
However, the callback method this.signinCallback that I am specifying doesn't get invoked. Any, ideas on what I am doing wrong here?
Thanks

As stated in the Google+ Sign-in Button docs, data-callback is expected to be "A function in the global namespace". That is because Google's code likely calls your callback by name since all HTML attributes are just strings. It will do something like (warning, not real code):
window[element.dataset["callbackName"]]();
You are passing a reference to your callback, which is not globally accessible. You can expose it when the component is mounted and delete it from the global namespace when it is unmounted:
componentWillMount: function() {
// Somehow generate a unique ID for every G+ button. This uses Underscore's
// `uniqueId` function[1].
//
// [1] http://underscorejs.org/#uniqueId
this.callbackName = _.uniqueId("gPlusCallback-");
window[this.callbackName] = this.signinCallback;
},
componentWillUnmount: function() {
delete window[this.callbackName];
},
render: function() {
...
'data-callback': this.callbackName
...
}

Related

making a ui-sref functionality in a .js

Setting:
I have an angular app.
What I have:
I have this: ui-sref="providerDetail({id:provider.id})" functioning.
It is a functioning link to a page giving details on a given provider.
What I want:
I have a button that submits a form and makes a new provider. This works. But I also want this button to forward the user to that new provider's detail page.
I have a function running that prints the id of the new provider to the console when it is made, I just can't figure out how to forward the user to the appropirate location.
Note:
I can add this to the function:
window.location = "/dashboard.html#!/providerDetail/" +id;
And it will take me to the new page, but I get this impression that this is a bad route to take.
Don't manually change the URL in AngularJS application, do it in Angular way so that you don't need to worry about manually kick of digest cycle. From function you could call $state.go method to navigate between states.
function myFunction(id) {
$state.go('providerDetail', { id: id})
}
Note: Please make sure you inject $state inside you controller.

Angular Protractor end-2-end test fails after clicking a button

So today I have been working on setting up end-2-end testing for an Angular JS app with Protractor. For writing cleaner tests, I make use of the Page Object pattern as described on the Protractor website.
Test scenario: When a user enters the website, he/she needs to login first. By filling in their credentials and clicking a button, the user is redirected to a validation page. There they need to enter a code that has been sent to them by email. In this case, the validation code is always the same when running end-2-end tests (123456). When the user has entered his/her code, the validation button needs to be clicked in order to enter the landing page (organisations). There a list of organisations is presented to the user.
The Protractor test:
organisations.spec.js
'use strict';
describe('when selecting organisation', function () {
var page;
var util = require('../../util');
browser.get('/#/login');
// login before each test case
beforeEach(function () {
page = require('./organisations.po');
util.login('username', 'password');
});
// logout after each one
afterEach(function () {
page.logoutButton.click();
});
it('should have a list of organisations', function () {
expect(browser.getCurrentUrl()).toEqual('http://localhost:9111/#/organisations');
expect(page.organisationList.isPresent()).toBe(true);
});
});
organisations.po.js (the page object)
'use strict';
var MainPage = function () {
// organisations
this.organisationList = element(by.css('.select-list'));
this.logoutButton = element(by.css('.logout-button'));
};
module.exports = new MainPage();
util.js (code for login process)
'use strict';
var Util = function () {
this.login = function login(username, password) {
browser.get('/#/login');
var loginPage = require('./spec/login/login.po');
var validationPage = require('./spec/login/validate.po');
// fill in login form
loginPage.username.sendKeys(username);
loginPage.password.sendKeys(password);
// submit login form and navigate to validation page
loginPage.loginButton.click();
// fill in validation form
validationPage.validationCode.sendKeys('123456');
// submit validation form and navigate to landing page
validationPage.submitValidateBtn.click();
}
};
module.exports = new Util();
What happens when running the test: Protractor is able to fill in the user credentials and click the login button. However, the corresponding redirect to the validation page does not occur, causing the expectations to fail as Protractor is not able to locate the page objects since these are on another page. Here is a part of the error message that Protractor produces. Just in case, here can you see the Protractor configuration file that is being used.
I tried putting explicit waits (browser.wait(<condition>) around the button clicks, but that just seems to have the same effect. Could someone point out what I am doing wrong and why that redirect is not happening? Am I missing something obvious here? Thanks in advance!
If your login-pages are non-Angular, double check whether you need to set browser.ignoreSynchronization to false in Protractor.
If they are Angular, I would think you'd need to wait for the click promise to complete after login? Does the following work?
loginPage.loginButton.click().then(function () {
validationPage.validationCode.sendKeys('123456');
...
}
In this way, Protractor does the wait (suggested in other answers) itself, provided the login/validation page are indeed done in Angular.
As a related remark, in your beforeEach and afterEach, you may also want to use the optional done callback, so that the actual test only starts after login has been successful:
beforeEach(function (done) {
...
util.login(user, pw).then(done);
}
This does require, though, that your login method actually returns the promise of the validation click. (I typically let all my page object methods return the promises).
Also related to your page objects, instead of having a Login page object with just the element finders as fields, why not give that page object the more abstract login method that is now in a util class?
Furthermore, I tend to give every page object a 'self check' method, that permits checking that the browser is indeed on the expected page. After clicking login on the Login page, I expect to be on the validation page. Thus, the first thing I do is check (assert) that I'm indeed on the validation page. Only then do I actually press the validate button. This tends to give cleaner error messages in case your application's routing is broken.
I wrote a bit more about state navigation and page objects on my blog.
I've had some issues with code in a part further along interrupting at a spot earlier than you'd expect. For example, as soon as it loads up a page object that has the right path, but maybe an error with the declaration of the page itself, the whole test crashes.
First, I would comment everything out after clicking the login button. Then maybe add a 10s sleep. Run the test and watch it. Does it click the button? Does it get past the login screen?
If that is working, uncomment one line at a time and see where it crashes. Try keeping the sleep in there to see if it makes a difference. If the sleep does make a difference, that means whatever wait you were using wasn't one that works in that situation.
Of course, if you hit a line that crashes you every single time, then you can dig further!
what I have found to help work is to force protractor to wait till a page loads by waiting till the title is a specific title.
for example: the title could be login at your login page but then after you hit login the next page title is validation. So you can force protractor to wait till the validation page loads.
NOTE: I don't know the actual names of your titles so I'm just using place holders.
'use strict';
var Util = function () {
this.login = function login(username, password) {
browser.get('/#/login');
var loginPage = require('./spec/login/login.po');
var validationPage = require('./spec/login/validate.po');
// fill in login form
loginPage.username.sendKeys(username);
loginPage.password.sendKeys(password);
// submit login form and navigate to validation page
loginPage.loginButton.click();
// wait till our validation page loads then do our validation
browser.wait(protractor.until.titleIs("Validation"), 5000, "✗ Failed to wait for the validation page to load").then(function(){
// fill in validation form
validationPage.validationCode.sendKeys('123456');
// submit validation form and navigate to landing page
validationPage.submitValidateBtn.click();
});
}
};

Backbone.js change url without reloading the page

I have a site that has a user page. On that page, there are several links that let you explore the user's profile. I'd like to make it so that, when one of those links is clicked on, the url changes, but the top third of the page containing the user's banner doesn't reload.
I'm using Backbone.js
I have a feeling that I'm in one of those situation where I have such a poor understanding of the problem I'm dealing with that I'm asking the wrong question, so please let me know if that appears to be the case
My mistake was assuming that there was a special, built-in way of doing this in backbone. There isn't.
Simply running the following line of code
window.history.pushState('object or string', 'Title', '/new-url');
will cause your browser's URL to change without reloading the page. You can open up the javascript console in your browser right now and try it with this page. This article explains how it works in more detail (as noted in this SO post).
Now I've just bound the following event to the document object (I'm running a single page site):
bindEvents: () ->
$(document).on('click', 'a', #pushstateClick)
pushstateClick: (e) ->
href = e.target.href || $(e.target).parents('a')[0].href
if MyApp.isOutsideLink(href) == false
if e.metaKey
#don't do anything if the user is holding down ctrl or cmd;
#let the link open up in a new tab
else
e.preventDefault()
window.history.pushState('', '', href);
Backbone.history.checkUrl()
See this post for more info.
Note that you CAN pass the option pushstate: true to your call to Backbone.history.start(), but this merely makes it so that navigating directly to a certain page (e.g. example.com/exampleuser/followers) will trigger a backbone route rather than simply leading to nowhere.
Routers are your friend in this situation. Basically, create a router that has several different routes. Your routes will call different views. These views will just affect the portions of the page that you define. I'm not sure if this video will help, but it may give you some idea of how routers interact with the page: http://www.youtube.com/watch?v=T4iPnh-qago
Here's a rudimentary example:
myapp.Router = Backbone.Router.extend({
routes: {
'link1': 'dosomething1',
'link2': 'dosomething2',
'link3': 'dosomething3'
},
dosomething1: function() {
new myapp.MyView();
},
dosomething2: function() {
new myapp.MyView2();
},
dosomething3: function() {
new myapp.MyView3();
}
});
Then your url will look like this: www.mydomain.com/#link1.
Also, because <a href=''></a> tags will automatically call a page refresh, make sure you are calling .preventDefault(); on them if you don't want the page to refresh.

Whats the Advantage of Marionette AppRouter+Controller over Backbone.Router?

From my understanding, the differences is the callback functions to events on an AppRouter should exist in the Controller, instead of the same Router object. Also there is a one-to-one relationship between such AppRouter & Controllers, all my code from Router now moves to Controller, I don't see too much point of that? So why use them? I must be missing something?
The way I see it is to separate concerns:
the controller actually does the work (assembling the data, instanciating the view, displaying them in regions, etc.), and can update the URL to reflect the application's state (e.g. displayed content)
the router simply triggers the controller action based on the URL that has been entered in the address bar
So basically, if you're on your app's starting page, it should work fine without needing any routers: your actions (e.g. clicking on a menu entry) simply fire the various controller actions.
Then, you add on a router saying "if this URL is called, execute this controller action". And within your controller you update the displayed URL with navigate("my_url_goes_here"). Notice you do NOT pass trigger: true.
For more info, check out Derick's blog post http://lostechies.com/derickbailey/2011/08/28/dont-execute-a-backbone-js-route-handler-from-your-code/ (paragraph "The “AHA!” Moment Regarding Router.Navigate’s Second Argument")
I've also covered the topic in more length in the free preview of my book on Marionette. See pages 32-46 here: http://samples.leanpub.com/marionette-gentle-introduction-sample.pdf
I made some override for the router. And currently use it in this way (like Chaplin):
https://gist.github.com/vermilion1/5525972
appRoutes : {
// route : controller#method
'search' : 'search#search'
'*any' : 'common#notFound'
},
initialize : function () {
this.common = new Common();
this.search = new Search();
}

Backbone-route click doesn't not respond on 2nd attempt

I am doing form validation in a function when a user wants to preview an invoice which is called by a route:
routes: {
"new" : "newInvoice",
"new/:assignmentid" : "newInvoiceAssignment",
"edit/:invoiceid" : "editInvoice",
"preview/:invoiceid" : "previewInvoice",
"preview" : "preview",
"delete/:invoiceid" : "deleteInvoiceModal",
"whyCant" : "whyCant",
"whatsThis" : "whatsThis"
},
And here is my two buttons (actually, a button and an href) on the form:
<div class="span8 alignRight">
<button id="saveInvoiceDraft" type="submit" class="btn btn-warning">Save Draft</button>
<a id="previewInvoice" class="btn btn-primary">Preview & Send</a>
</div>
When this invoice is created, the URL for the tag is set with:
var url = '#preview';
$('#previewInvoice').attr('href',url);
And finally, when I click on the "Preview & Send" button, the previewInvoice(invoiceid) function below runs, properly catches the one form field missing and displays the error message. At that point even if I populate the form field, that button is dead and no longer responds. However, the "Save Draft" button works perfectly and mimic's the same code as in the previewInvoice() function.
I know there is probably a better way to do this, but I was following that way it was done in another section of the app I inherited. Actually, as I am typeing this I am wondering since the sendDraft() function works and its a button and the previewInvoice() function does not, the fact that it is a href might have something to do with it.
function previewInvoice(invoiceid) {
var invoice = new Invoice({"invoiceid": invoiceid});
invoice.set({"invoiceid": invoiceid,"invoicestatus": "draft"});
formGetter(invoice);
validateInvoiceForm(invoice);
if (window.errors.length == 0) {
//business logic here
if (window.panel == undefined) {
// business logic here
}
else {
//save business logic here
}
}
else {
showInvoiceErrors();
}
}
Any ideas why the button no longer responds? I am not seeing any error's in the console. I added a consol.log inside the function to display the value of a different form element, and it displays the first time in the console, but if I change the data and click the button again, that log does not update, which to me is another clue that it is just not firing.
Backbone.History listens to hashchange events to trigger navigation to routes. When you first click the previewInvoice button, the URL hash fragment is set to #preview, and the matching route is triggered.
When you click the same button the second time, the hash doesn't actually change, and therefore the router doesn't catch it.
I'm having a hard time recommending a good solution to this problem. Normally I would recommend catching the click event and calling router.navigate("preview", {trigger:true}); manually. However, based on your code sample it looks like your application is built around the Router, and there isn't a View layer for DOM event handling as you would expect in most Backbone applications.
On the Router level this is a bit trickier to solve. You could use router.navigate to set a dummy hash after the preview route has been executed. This would cause the link to trigger a hashchange on the second time as well. Unfortunately this would mean that the preview page would not be bookmarkable, and since you're not using pushState, would leave an extraneous history entry.
I'm afraid this issue will have to either be solved with a hacky fix (as outlined above) or a major refactoring.
I have this same problem, my solution is to put in a handler that does a fake "in-between" route that is hidden from history, so that Backbone.history will register the navigation as a change and trigger the action.
Put a class on links that you need the route action to trigger regardless if the URL is the same:
do it
In your Backbone view, put in an event handler:
events: {
"click .js-ensureNav": "_ensureNav",
},
Actual handler:
_ensureNav: function (event) {
var route_name = $(event.target).attr('href').slice(1);
Backbone.history.navigate("fake");
Backbone.history.navigate(route_name, {trigger: true, replace: true});
},
Instead of
var url = '#preview';
$('#previewInvoice').attr('href',url);
Try this
var date = new Date();
var url = '#preview?date:' + date ;
$('#previewInvoice').attr('href',url);
So Every time new request will be generated and this will solve your problem.

Resources