how to capture the recaptcha response in ui - ember.js

I have installed the ember-g-recaptcha addon in my ember app using ember install ember-g-recaptcha. I'm able to see the recaptcha in the UI. But how do I check whether that box is checked or not. If it's not checked my form should not submit and should display an error message.
I want to display this error message when the response is empty. I have written this in my controllers:
onCaptchaExpired() {
$("#message").show();
$("#message").css("color", "red");
$("#message").html("Recaptcha response cannot be empty.");
},

First you really should rely on ember to manage the DOM, and don't do it manually with jQuery.
A simple solution could be to just toggle a boolean when the g-recaptcha component fires the actions:
template:
{{g-recaptcha onSuccess=(action "onCaptchaResolved") onExpired=(action "onCaptchaExpired")}}
{{captchaError}}
<button disabled={{isInvalid}} />
component:
Ember.Component.extend({
isInvalid: true,
captchaError: null,
actions: {
onCaptchaResolved(captchaResult) {
this.set('isInvalid', false);
},
onExpired() {
this.set('isInvalid', true);
this.set('captchaError', 'Recaptcha response cannot be empty.')
}
}
})
This should give you an idea how you could do this.

Related

Handling enter key event in ember

I have a component in my application.It have a form with text fields.It will have a submit button.When submit is pressed it will send a post request to the server.I also handled a keyboard event in components js file.When enter is pressed it will send a post request to the server.When the enter key is pressed two times continuously it is making two post request to the server with first request success and second request failed.
I want to make my app in such away even if the user presses the enter key two times continuously it should send only one post request to the server.Can any one help me solve this issue.Thanks in advance.
components js file:
export default Component.extend({
keyDown:function(event){
let self = this;
if(event.keyCode === 13){
self.send('submitform');
return false;
}
actions: {
submitform(){
//logic to handle the post request to the server
}
}
Try usig Ember.run.debounce,
export default Ember.Component.extend({
keyDown: function(event) {
let self = this;
if (event.keyCode === 13) {
// self.send('submitform');
Ember.run.debounce(self,self.get('submitform'),400);
return false;
}
},
submitform(){
//handle submit form logic
}
});
You can play with twiddle here
You will want to disable submitForm() until your POST request is complete. You can do this by setting a property submitting on the component and turning it on before the POST and off once the promise is resolved. Perhaps try something like:
export default Ember.Component.extend({
submitting: false,
keyDown: function(event) {
if (event.keyCode === 13) {
this.submitform();
}
},
submitform() {
// Run only if not currently submitting
if (!this.get('submitting')) {
// What to do when submit succeeds
const success = () => {
this.set('submitting', false);
}
// What to do when submit fails
const fail = () => {
this.set('submitting', false);
}
// Do the POST request
this.set('submitting', true);
this.get('someModel').save().then(success).catch(fail);
};
}
});
And, unrelated, this allows you to do fun things with your template such as disabling and styling the submit button for as long as the POST promise is not resolved yet:
<button {{action 'submitForm'}} disabled={{submitting}} class="{{if submitting 'loading'}}">
{{#if submitting}}
Submitting ...
{{else}}
Submit
{{/if}}
</button>
Oh and lastly, no need to use let self = this; anymore. Use ES6 arrow functions () => { ... } instead so you can keep using this inside.

EmberJS test fails first time running in phantomjs

Problem
I have a /login route that uses ember-simple-auth to implement authentication. During testing ember-cli-mirage is used to mock the backend. The user logs in by providing their email address and password.
In total I have 4 acceptance tests for this route, similar to the test below:
test('should show error message for invalid email', function(assert) {
visit('/login');
fillIn('input#email', 'invalid-email');
fillIn('input#password', 'invalid-password');
click('button.button');
andThen(function() {
assert.equal(find('div.notification').text(), "Invalid email/password");
});
});
When I run the tests using ember t only the first test in the file fails. If I comment this test out, the next one fails, and so on. If I run the tests in server mode with ember t -s the same test fails; however, when I press enter to re-run the tests, all the tests pass.
The failure message is always the same, shown below:
not ok 7 PhantomJS 2.1 - Acceptance | login: should show error message for invalid email
---
actual: >
expected: >
Invalid email/password
stack: >
http://localhost:7357/assets/tests.js:22:19
andThen#http://localhost:7357/assets/vendor.js:48231:41
http://localhost:7357/assets/vendor.js:48174:24
isolate#http://localhost:7357/assets/vendor.js:49302:30
http://localhost:7357/assets/vendor.js:49258:23
tryCatch#http://localhost:7357/assets/vendor.js:68726:20
invokeCallback#http://localhost:7357/assets/vendor.js:68738:21
publish#http://localhost:7357/assets/vendor.js:68709:21
http://localhost:7357/assets/vendor.js:48192:24
invoke#http://localhost:7357/assets/vendor.js:10892:18
flush#http://localhost:7357/assets/vendor.js:10960:15
flush#http://localhost:7357/assets/vendor.js:11084:20
end#http://localhost:7357/assets/vendor.js:11154:28
run#http://localhost:7357/assets/vendor.js:11277:19
run#http://localhost:7357/assets/vendor.js:32073:32
http://localhost:7357/assets/vendor.js:48783:24
Log: |
After all the tests have run, test emits an exception:
# tests 60
# pass 59
# skip 0
# fail 1
Not all tests passed.
Error: Not all tests passed.
at EventEmitter.getExitCode (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/lib/app.js:434:15)
at EventEmitter.exit (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/lib/app.js:189:23)
at /home/jon/projects/jonblack/wishlist-web/node_modules/testem/lib/app.js:103:14
at tryCatcher (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:510:31)
at Promise._settlePromise (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:567:18)
at Promise._settlePromise0 (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:612:10)
at Promise._settlePromises (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/promise.js:691:18)
at Async._drainQueue (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/async.js:138:16)
at Async._drainQueues (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/async.js:148:10)
at Immediate.Async.drainQueues (/home/jon/projects/jonblack/wishlist-web/node_modules/testem/node_modules/bluebird/js/release/async.js:17:14)
at runCallback (timers.js:637:20)
at tryOnImmediate (timers.js:610:5)
at processImmediate [as _immediateCallback] (timers.js:582:5)
It seems odd that this is emitted for tests failing rather than just reporting the test failure, so perhaps it's related.
Running the tests in Firefox and Chromium work, as does running the application in development mode and logging in manually. The problem is limited to phantomjs.
I have other acceptance tests for another route and these all pass. It seems limited to the /login route, suggesting that it is possibly related to authentication.
Debugging
I've tried debugging by adding pauseTest() to the test and "phantomjs_debug_port": 9000 to testem.js but both Firefox and Chromium do nothing when I use the debug console. This might be my lack of experience debugging phantomjs, but I would at least expect it to give me an error - it literally does nothing.
It feels as though there is a timing issue between phantomjs and something, possible ember-simple-auth, in my Ember app.
I'm not that experienced debugging phantomjs problems nor Ember acceptance test failures, so any help is appreciated.
Versions
ember-cli 2.10.0
ember-simple-auth 1.1.0
ember-cli-mirage 0.2.4
Update 1
The button is inside a login-form component:
<form {{action 'login' on='submit'}}>
<p class="control has-icon">
{{input value=email id='email' placeholder='email' class='input'}}
<i class="fa fa-envelope"></i>
</p>
<p class="control has-icon">
{{input value=password id='password' placeholder='password'
type='password' class='input'}}
<i class="fa fa-lock"></i>
</p>
<p class="control">
<button class="button is-success" disabled={{isDisabled}}>Log In</button>
</p>
</form>
The component's login action just calls the passed in login handler:
import Ember from 'ember';
export default Ember.Component.extend({
email: "",
password: "",
isDisabled: Ember.computed('email', 'password', function() {
return this.get('email') === "" || this.get('password') === "";
}),
actions: {
login() {
var email = this.get('email');
var password = this.get('password');
this.attrs.login(email, password);
}
}
});
Which is the authenticate method in the login controller:
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service(),
actions: {
authenticate(email, password) {
this.get('session').authenticate('authenticator:oauth2', email, password).catch((data) => {
this.set('errors', data['errors']);
});
}
}
});
Update 2
As suggested by Daniel I added a delay to the test:
test('should show error message for invalid email', function(assert) {
visit('/login');
fillIn('input#email', 'invalid-email');
fillIn('input#password', 'invalid-password');
click('button.button');
andThen(function() {
Ember.run.later(this, function() {
assert.equal(find('div.notification').text(), "Invalid email/password");
}, 0);
});
});
Using only Ember.run.later the test still failed, but putting that inside the andThen causes it to pass. Have you noticed the bizarre part? The delay is 0 milliseconds.
I still want to find an explanation for this because I don't trust that this will run the same on whatever machine the tests run on.
Update 3
Today I had a surprise: suddenly the tests were working again!
I added a new route with acceptance tests. The route itself is an authenticated route, so the tests use the authenticateSession test helper from ember-simple-auth to authenticate.
when I remove the tests that use this helper, the error returns!.
I'm not sure what this means. It feels like the issue is with ember-simple-auth, but it might also be a giant coincidence that the helper resolves another timing issue.
Down the rabbit hole we go...
Update 4
Below is the configuration for the auth endpoints in ember-cli-mirage:
this.post('/token', function({db}, request) {
var data = parsePostData(request.requestBody);
if (data.grant_type === 'password') {
// Lookup user in the mirage db
var users = db.users.where({ email: data.username });
if (users.length !== 1) {
return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
errors: [{
id: 'invalid_login',
status: '400',
title: 'Invalid email/password',
}]
});
}
var user = users[0];
// Check password
if (data.password === user.password) {
if (!user.active) {
return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
errors: [{
id: 'inactive_user',
status: '400',
title: 'Inactive user',
}]
});
} else {
return new Mirage.Response(200, {
'Content-Type': 'application/json'
}, {
access_token: 'secret token!',
user_id: user.id
});
}
} else {
return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
errors: [{
id: 'invalid_login',
status: '400',
title: 'Invalid email/password',
}]
});
}
} else {
return new Mirage.Response(400, {'Content-Type': 'application/json'}, {
errors: [{
id: 'invalid_grant_type',
status: '400',
title: 'Invalid grant type',
}]
});
}
});
this.post('/revoke', function(db, request) {
var data = parsePostData(request.requestBody);
if (data.token_type_hint === 'access_token' ||
data.token_type_hint === 'refresh_token') {
return new Mirage.Response(200, {'Content-Type': 'application/json'});
} else {
return new Mirage.Response(400, {'Content-Type': 'application/json'},
{error: 'unsupported_token_type'});
}
});
Update 5
Here's my config/environment.js file:
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'wishlist-web',
environment: environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false
}
},
APP: {
}
};
if (environment === 'development') {
}
if (environment === 'test') {
// Testem prefers this...
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
ENV.ServerTokenEndpoint = 'http://localhost:9292/token';
ENV.ServerTokenRevocationEndpoint = 'http://localhost:9292/revoke';
ENV.ApiHost = 'http://localhost:9292';
}
return ENV;
};
You have few things to try here to debug this issue.
You could remove {{isDisabled}} from button to make sure it's not disabled when you try to click it.
Use setTimeout instead of andThen and see if it's timing issue.
Replace authenticate action code with nothing, to make sure it isn't causing your test to fail.
You could also rewrite test to put your assert.ok after some event in JavaScript. For example you could mock authenticate action or observer errors property. You can do this by using lookups or registers in acceptance environment - tests from one of my Ember CLI addons could help you - ember-link-action/tests/acceptance/link-action-test.js.
Edit
Having seen what worked for you experience tells me that you should try 2 things.
For this code:
andThen(function() {
Ember.run.later(this, function() {
assert.equal(find('div.notification').text(), "Invalid email/password");
}, 0);
});
You could try Ember.run.scheduleOnce('afterRender', this, () => { ... assert here } instead of using Ember.run.later. Or you could try using just Ember.run instead of Ember.run.later.
Conclusion: The key to fixing this issue could be putting your assertion in Ember Run Loop.
I would assume that the error you're seeing (Invalid email/password) is the (mock) server response and indicates something is wrong with either the mock or the credentials you're using in the test.
I'd also not use mirage for mocking the authentication request. mirage (just like Jason API) is resource based and not something that's well suited for authentication.

How to display error message in ember js 2.0

I would like to display an error message when the server responses with record not found.
The model in the route handler:
model: function(userLoginToken) {
var userLoginToken= this.store.createRecord('userLoginToken');
return userLoginToken;
},
The action:
actions: {
sendOTP: function(userLoginToken) {
var thisObject = this;
var model=this.currentModel;
this.store.findRecord('user-login-token', userLoginToken.get('mobileNumber')).then(function(response) {
//thisObject.get('controller').set('model', response);
},
function(error) {
//thisObject.get('controller').set('model', error);
//alert("model======== "+model.get('errors'));
});
},
The template is not displaying any error message.
The template:
{{#each model.errors.messages as |message|}}
<div class="errors">
{{message}}
</div>
{{/each}}
Unfortunately, the error message doesn't appear.
Ember depends on an DS.error object, in order to get errors from your models the response has to fulfill the requirements. In order to get Ember to recognize an valid error, in Ember 2.x the error code MUST be 422 and has to follow jsonapi http://jsonapi.org/format/#errors-processing
If you want to catch the errors from the backend response you have to use the catch method:
this.store.findRecord('user-login-token', userLoginToken.get('mobileNumber'))
.then(success => {
// Do whatever you need when the response success
})
.catch(failure => {
// Do whatever you need when the response fails
})
},
For catching the errors automatically as you are doing in your template, your backend needs to response in the right way. I would suggest you to read the answer for this SO question.

Ember: how to retrieve validation errors after becameInvalid state?

I use the RESTadpater to persist data. When a validation error occurs, I want to return a 422 response and then log the errors and show an indication next to each incorrect field.
My REST response status code is as follows:
Status Code:422 Unprocessable Entity
My REST response body is as follows:
{
"message": "Validation failed",
"errors": [
{
"name": "duplicate"
}
]
}
In my controller, the becameInvalid fires correctly.
App.AuthorsNewController = Ember.ObjectController.extend({
startEditing: function () {
//Create a new record on a local transaction
this.transaction = this.get('store').transaction();
this.set('model', this.transaction.createRecord(App.Author, {}));
},
save: function (author) {
//Local commit - author record goes in Flight state
author.get('transaction').commit();
//If response is success: didCreate fires
//Transition to edit of the new record
author.one('didCreate', this, function () {
this.transitionToRoute('author.edit', author);
});
//If response is 422 (validation problem at server side): becameError fires
author.one('becameInvalid', this, function () {
console.log "Validation problem"
});
}
...
2 QUESTIONS:
I want to log below the 'console.log "Validation problem"', the complete list of errors returned by the server. How can I do that ?
In my hbs template, I want to indicate an error next to the relevant field. How can I do this ?
I am not sure that the data returned via REST adapter is correct. So problem might be at the REST side or at the Ember side ...
Solution:
In controller save function:
author.one('becameInvalid', this, function () {
console.log "Validation problem"
this.set('errors', this.get('content.errors'));
});
In hbs template:
{{view Ember.TextField valueBinding='name'}}
{{#if errors.name}}{{errors.name}}{{/if}}
Here is how I do it, may not be the best practice but it works for me:
instead of using commit(), I use save(), and if you wonder what's the difference, here is the link. I haven't tried your approach of using transaction, but basically I create the record using record = App.Model.createRecord(...), and here is the code of my apiAddShop function inside the AddShopController:
apiAddShop: function() {
//console.log("add shop");
newShop = App.Shop.createRecord({name:this.get('name'), currentUserRole:"owner"});
//this.get('store').commit(); // Use record.save() instead, then() is not defined for commit()
var self = this;
newShop.save().then(function(response){
self.transitionToRoute('shops');
}, function(response){
// if there is error:
// server should respond with JSON that has a root "errors"
// and with status code: 422
// otherwise the response could not be parsed.
var errors = response.errors;
for(var attr in errors){
if (self.hasOwnProperty(attr)) {
self.set(attr+"Error", true);
self.set(attr+"Message", Ember.String.classify(attr)+" "+errors[attr]);
}
console.log(attr + ': ' + errors[attr]);
}
console.log(response.errors.name[0]);
});
},
the above code assume there is a attrError(boolean) and attrMessage(string) for each of the attributes in your form. Then in your template, you could bind the class of your field to these error attributes, such as <div {{bindAttr class=":control-group nameError:error:"}}>, and the error message could be easily display next to the form field such as: <span {{bindAttr class=":help-inline nameError::hidden"}} id="new_shop_error">{{nameMessage}}</span> Here is my example handlebar gist (have to use gist here, since SO is escaping my html inputs).

FB.login() called before calling FB.init()

I have seen this question in SO several times. Most of cases user called facebook js (all.js) twice or did not provide the application id. But my case is different. I have included all.js once and I also have set application id. But still I am getting this error "FB.login() called before calling FB.init()" when I click the login button. The markup for login button is as follows
<fb:login-button autologoutlink="true" perms="email,user_birthday,user_about_me,publish_stream"></fb:login-button>
And the javascript codes are
<script type="text/javascript">
FB.init({appId: '${section.parameters['facebook.app.id']}', status: true, cookie: true, xfbml: true});
/* All the events registered */
FB.Event.subscribe('auth.login', function(response) {
window.location.reload();
});
FB.Event.subscribe('auth.logout', function(response) {
// do something with response
//logout();
});
function setFacebookStatus(form) {
var status = $('#comment-form-body').val();
var params = {};
params['message']= status;
params['link'] = '${article.url}';
FB.api('/me/feed', 'post',params, function(response) {
if (!response || response.error) {
alert('Error occured');
}
else {
alert('<fmt:message key="comment.form.facebook.success" />');
$("#facebookComment").overlay().close();
form.submit();
}
});
}
</script>
I have added all.js in the header of my html page. I am really pissed off this issue. I would be very grateful if you help me.
Edit
One more thing to mention. Sometimes it works and sometimes it shows me the warning. Say I was logged in to facebook from another tab and then I logged out from facebook. Then I refresh my website page that has the facebook integration. Then I get the warning. Main problem is that sometimes it is working sometimes not.
I have modified my javascript as follows and now it is working.
$(function() {
window.fbAsyncInit = function() {
FB.init({appId: '${section.parameters['facebook.app.id']}', status: true, cookie: true, xfbml: true});
};
$('body').append('<div id="fb-root"></div>');
$.getScript(document.location.protocol + '//connect.facebook.net/en_US/all.js');
})
setFacebookStatus function remain same. Adding those scripts on document ready removes the problem of facebook javascript warning and non deterministic login behavior. My guess is that on document ready initialization of FB scripts ensures execution of all calls in required order.