Environment
Ember Version: 2.0
Ember CLI Version: 2.13.0
Ember CP Validations Version: 3.4.0
Steps to Reproduce
hbs:
<div>
<label> Email: <label>
{{validated-input model=this placeholder="Enter new email" valuePath='new_email' id="new_email" didValidate=didValidate}}
<label> Password: <label>
{{validated-input model=this placeholder="Enter current password" valuePath='current_password' id="current_password" didValidate=didValidate}}
<button {{action "changeEmail"}}>Submit</button>
</div>
<div>
<label> Confirmation Token: <label>
{{validated-input model=this placeholder="Enter confirmation token" valuePath='confirmation_token' id="confirmation_token" didValidate=didValidate}}
<button {{action "verify"}}>Verify</button>
</div>
js:
import Ember from 'ember';
import { validator, buildValidations } from 'ember-cp-validations';
const Validations = buildValidations({
new_email: [
validator('presence', true),
validator('format', { type: 'email' })
],
current_password: [
validator('presence', true)
],
confirmation_token: [
validator('presence', true),
]
});
export default Ember.Component.extend(Validations, {
changeEmail: function() {
this.validate().then(() => {
if (this.get('validations.attrs.new_email.isValid') && this.get('validations.attrs.current_password.isValid')) {
...
....
} else {
this.set('didValidate', true);
}
});
});
Now when I click submit, changeEmail action is called and if validation fails it sets this.set('didValidate', true); which enables all the three validated-input field and shows validation error for even confirmation_token field. But i need to show validation error message only for current_password and new_email. vice versa when verify action is called
one way of doing is, unique property name for didValidate
For eg:
<div>
<label> Email: <label>
{{validated-input model=this placeholder="Enter new email" valuePath='new_email' id="new_email" didValidate=didValidateEmail}}
<label> Password: <label>
{{validated-input model=this placeholder="Enter current password" valuePath='current_password' id="current_password" didValidate=didValidatePassword}}
<button {{action "changeEmail"}}>Submit</button>
</div>
<div>
<label> Confirmation Token: <label>
{{validated-input model=this placeholder="Enter confirmation token" valuePath='confirmation_token' id="confirmation_token" didValidate=didValidateToken}}
<button {{action "verify"}}>Verify</button>
</div>
and in js set the property to true or false manually for each field:
changeEmail: function() {
this.validate().then(() => {
if (this.get('validations.attrs.new_email.isValid') && this.get('validations.attrs.current_password.isValid')) {
...
....
} else {
this.setProperties({didValidateEmail: true, didValidatePassword: true});
}
});
Is this the only way ?
One way to do this is to create a didValidate object on the component
didValidate: computed(() => ({})),
And when you click changeEmail in the else you can do
['newEmail', 'currentPassword'].forEach(key => {
this.set(`didValidate.${key}`, true);
});
Which creates a key for each property in the didValidate object and sets it to true.
Then in the template you can show the error for each field by passing
didValidate.newEmail or didValidate.currentPassword
Related
I'm creating and saving a form using Ember but when I reload the page the toggle keeping track of whether the form has been submitted or not resets to false.
I have a page where the default text is 'You have no account linked'. I then have a button that when pressed displays a form for the user to fill out information . When they click submit and save their information, the form disappears and renders some text about their account. When I reload the page however the text renders to the default 'You have no account linked', and when I click the submit form button, their information is populated in the form fields. How can I ensure that when the page is reloaded the text about the user account is displayed?
This is the controller for the page
export default Controller.extend({
isToggled: false,
emailConnected: false,
actions: {
submitImap(mailbox, toggle, email) {
this.get('ajax').request(`/api/accounts/${this.session.account.id}/mailboxes/imap`, {
method: 'POST',
data: mailbox
})
.then(() => Utils.notify("IMAP settings saved.", 'success'))
.catch(() => Utils.notify("Error saving IMAP account. Try again", 'error'));
this.send('contract', toggle);
this.send('expand', email);
},
disconnectIMAP(mailbox, property, email) {
this.get('ajax').request(`/api/accounts/${this.session.account.id}/mailboxes/imap`, {
method: 'DELETE',
data: {
user_id: mailbox.user_id
}
}).then(() => {
this.set(property, { smtp: {}});
})
.then(() => Utils.notify("IMAP removed. ", 'success'))
.catch(() => Utils.notify("Error removing IMAP account", 'error'));
this.send('contract',email );
},
expand: function(toggle) {
this.set(toggle, true)
},
contract: function(toggle) {
this.set(toggle, false)
}
}
});
This is the template handling the form submission
<h3>IMAP/SMTP</h3>
{{#if emailConnected}}
{{#if isToggled}}
<p> Edit your IMAP settings below </p>
{{else}}
<p>
You currently have IMAP account <strong>{{imapMailbox.username}}</strong>
connected for messaging.
</p>
<button {{action "disconnectIMAP" imapMailbox 'imapMailbox' 'emailConnected' }} class = 'btn btn-danger'>Disconnect</button>
{{/if}}
{{else}}
<p>
You currently do not have an account linked for messaging.
</p>
{{/if}}
{{#if isToggled}}
<form name='imap' class='modern-form full-width' {{action 'submitImap' imapMailbox 'isToggled' 'emailConnected' on="submit" }}>
<div class='row'>
<div class='col-sm-6'>
<h4>IMAP</h4>
<div class='form-group'>
<label>
Host
</label>
{{input type='text' required=true name='address' value=imapMailbox.address class='form-control'}}
</div>
<div class='form-group'>
<label>
Port
</label>
{{input type='text' required=true name='port' value=imapMailbox.port class='form-control'}}
</div>
<div class='form-check'>
{{input type='checkbox' name='ssl' checked=imapMailbox.ssl class='form-check-input'}}
<label for='ssl'>
SSL
</label>
</div>
<div class='form-check'>
{{input type='checkbox' name='starttls' checked=imapMailbox.starttls class='form-check-input'}}
<label>
TLS
</label>
</div>
<div class='form-group'>
<label>
Username
</label>
{{input type='text' required=true name='username' value=imapMailbox.username class='form-control'}}
</div>
<div class='form-group'>
<label>
Password
</label>
{{input type='password' required=true name='password' value=imapMailbox.password class='form-control'}}
</div>
</div>
<div class='col-sm-6'>
<h4>SMTP</h4>
<div class='form-group'>
<label>
Host
</label>
{{input type='text' required=true name='smtp_address' value=imapMailbox.smtp.address class='form-control'}}
</div>
<div class='form-group'>
<label>
Port
</label>
{{input type='text' required=true name='smtp_port' value=imapMailbox.smtp.port class='form-control'}}
</div>
<div class='form-check'>
{{input type='checkbox' name='smtp_ssl' checked=imapMailbox.smtp.ssl class='form-check-input'}}
<label for='ssl'>
SSL
</label>
</div>
<div class='form-check'>
{{input type='checkbox' name='smtp_starttls' checked=imapMailbox.smtp.enable_starttls_auto class='form-check-input'}}
<label>
TLS
</label>
</div>
<div class='form-group'>
<label>
Username
</label>
{{input type='text' required='true' name='smtp_username' value=imapMailbox.smtp.user_name class='form-control'}}
</div>
<div class='form-group'>
<label>
Password
</label>
{{input type='password' required='true' name='smtp_password' value=imapMailbox.smtp.password class='form-control'}}
</div>
</div>
</div>
<button type="submit" class='btn btn-success'>
Save
</button>
<button {{action 'contract' 'isToggled'}} class = 'btn btn-danger'>
Cancel
</button>
</form>
{{else}}
<button {{action 'expand' 'isToggled'}} class= 'btn btn-success'>
Connect email
</button>
{{/if}}
Right now, if I submit the form the behavior is as expected, displaying the current username of the account, but on reload the emailConnected variable resets to false and the default of 'you have no account connected' is present and when I click the form the values are populated.
If you reload the page (or) switch to a different route, the controller's property isToggled will reset to its initial state (i.e) to false in your case.
If you want to maintain the state and make use of the property isToggled at various parts of your application, you can use ember service
But in your case, you want to maintain the property state even after the page reloads. ember service doesn't maintain the state after the page reloads.
Here comes the use of browsers localStorage
So, in your case -
1) store the value of the property isToggled in browsers localStorage
import { computed } from '#ember/object';
export default Controller.extend({
isToggled: computed(function () {
// when the user visits the page for the very first time,
// isToggled value is set to false,
// from next time it gets the value from browsers localStorage.
if (localStorage.isToggled) {
return JSON.parse(localStorage.isToggled);
} else {
return false;
}
}),
...
actions: {
...
expand: function() {
localStorage.setItem('isToggled', JSON.stringify(true));
this.set('isToggled', true);
},
contract: function() {
localStorage.setItem('isToggled', JSON.stringify(false));
this.set('isToggled', false);
}
...
}
});
Now when the page is reloaded the isToggled property state doesn't change to the initial state.
You can find the isToggle browsers localStorage variable in your browsers developer tool: Application -> Local Storage tab
You could also use Ember Local Storage library to achieve this: https://github.com/funkensturm/ember-local-storage
I'm using the latest Ember (3.2).
I have made extension of text-area component:
app/components/enterable-textarea.js
export default TextArea.extend({
keyPress(event) {
if (event.keyCode === 13) {
console.info('e ', event);
}
}
});
I see the debug output in the console once I hit the 'Enter' key.
In my route template I have simple form like:
<form {{action "save" model.newNote on='submit'}}>
<div class="form-group">
<label for="tag">Tag</label>
{{input type="text" value=model.newNote.tag
placeholder="#anytag" class="form-control"}}
</div>
<div class="form-group">
<label for="note">Notepad</label>
{{enterable-textarea value=model.newNote.note
rows="6" class="form-control"}}
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Ho do I pass form action to the component or fire the 'submit' event of the form?
I need to pass whole form to the route action by pressing the 'Enter'
You pass an action as a property and then call it as a function:
export default TextArea.extend({
onEnter: () => {}, //or function() {},
keyPress(event) {
if (event.keyCode === 13) {
this.get('onEnter')(); //or even this.onEnter();
}
}
});
{{enterable-textarea value=model.newNote.note
rows="6" class="form-control" onEnter=(action "save" model.newNote)}}
Read more about actions
I have a simple contact form, with validation done using ember-cp-validations https://github.com/offirgolan/ember-cp-validations and I now need to integrate the new Google Recaptcha into that.
For the rendering of the recaptcha, I am using this code - https://gist.github.com/cravindra/5beeb0098dda657433ed - which works perfectly.
However, I don't know how to deal with the verification process to allow the form to be submitted/prevented if the challenge is correct/incorrect or not provided
Here is a truncated version of my contact-form component
import Ember from 'ember';
import Validations from './cp-validations/contact-form';
import config from '../config/environment';
export default Ember.Component.extend(Validations,{
data:{},
nameMessage:null,
init() {
this._super(...arguments);
this.set('data',{});
},
actions:{
submitForm() {
this.validate().then(({model,validations}) => {
if (validations.get('isValid')) {
// submit form
}
else {
if(model.get('validations.attrs.data.name.isInvalid')){
this.set('nameMessage',model.get('validations.attrs.data.name.messages'));
}
}
})
}
}
});
Here is the template for the component, which includes the rendering of the recpatcha using the gist above
<form {{action 'submitForm' on='submit'}}>
<div class="row">
<div class="medium-6 columns">
{{input type="text" value=data.name id="name" placeholder="Enter your name"}}
<div class="error-message">
{{nameMessage}}
</div>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
{{google-recaptcha}}
</div>
</div>
<button class="button primary" type="submit">Submit</button>
</form>
The Validations import looks like this
import { validator, buildValidations } from 'ember-cp-validations';
export default buildValidations({
'data.name': {
validators: [
validator('presence',{
presence:true,
message:'Please enter your name'
})
]
},
});
Many thanks for any help!
Register captchaComplete in your google-recaptcha component and mix the answer with your validations
UPDATE
contact-form.hbs
<form {{action 'submitForm' on='submit'}}>
<div class="row">
<div class="medium-6 columns">
{{input type="text" value=data.name id="name" placeholder="Enter your name"}}
<div class="error-message">
{{nameMessage}}
</div>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
{{google-recaptcha captchaComplete=validateRecatcha}}
</div>
</div>
<button class="button primary" type="submit">Submit</button>
</form>
contact-form.js
import Ember from 'ember';
import Validations from './cp-validations/contact-form';
import config from '../config/environment';
export default Ember.Component.extend(Validations,{
data:{},
nameMessage:null,
captchaValidated: false,
init() {
this._super(...arguments);
this.set('data',{});
},
actions:{
validateRecatcha(data){
//if data denotes captcha is verified set captchaValidated to true else false
},
submitForm() {
this.validate().then(({model,validations}) => {
if (validations.get('isValid') && this.get('captchaValidated')) {
// submit form
}
else {
if(model.get('validations.attrs.data.name.isInvalid')){
this.set('nameMessage',model.get('validations.attrs.data.name.messages'));
}
}
})
}
}
});
I am working on a simple CRUD app using ember js. I have a template with a input form which sends data to the server and server responds with success response.
input form data: "user": {"id": 1, "name": "John"};
server response: {"message":"Created successfully", "nextId": 2}.
I want to display the success message on the same template(one with the input form). Because template refers to the user model and user model does not have a message attribute. How can it be done? Please help.
Here is my code:
var Manager = Ember.Application.create();
Manager.Router.map(function() {
this.resource('users');
this.resource('user', {path: '/users/:user_id'});
this.resource('home', {path: '/'});
});
Manager.UserRoute = Ember.Route.extend({
model: function(params) {
return jQuery.getJSON("http://localhost:8085/users/"+ params.user_id);
},
actions :{
insert : function(){
var user = this.modelFor("User");
$.ajax({
url: "http://localhost:8085/Users",
data: JSON.stringify(user),
type: "POST",
contentType: "application/json",
})
.done(function(data){
//set data.message to display on UI
})
}
}
});
and my template looks like this:
<script type="text/x-handlebars" data-template-name="user">
<form class="form-horizontal" role="form" {{action "insert" on="submit"}}>
<div class="form-group">
<label for="userId" class="control-label col-xs-2">User Id</label>
<div class="col-xs-4 input-group">
{{input type="number" value=_id class="form-control" id="userId" placeholder="Identity"}}
</div>
</div>
<div class="form-group">
<label for="name" class="control-label col-xs-2">Name</label>
<div class="col-xs-4 input-group">
{{input type="text" value=name class="form-control" id="name" placeholder="Name"}}
</div>
</div>
{{#if message}}
<div class="alert alert-success input-group" role="alert">{{message}}</div>
{{/if}}
<div class="form-group button-center">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
</script>
Set the message property on your controller to the response that comes back.
actions :{
insert : function(){
var user = this.modelFor("User");
var self = this;
$.ajax({
url: "http://localhost:8085/Users",
data: JSON.stringify(user),
type: "POST",
contentType: "application/json",
})
.done(function(data){
//set data.message to display on UI
self.controllerFor('user').set('message', data.message);
})
}
}
When i first go the my login url, the inputs are correctly binded to the properties on my controller.
Then a press logout and what it does is transition back to the login page.
When i try to login again, it fails, because the input values arent binded to the controller, additionally i get this error:
Uncaught Error: Assertion Failed: Attempted to register a view with an id already in use: login
If i refresh the page it works.
Some of my code for references:
LoginRoute
export
default Ember.Route.extend({
setupController: function(controller) {
controller.reset();
},
beforeModel: function() {
if (!Ember.isEmpty(this.controllerFor('sessions.login').get('token'))) {
this.transitionTo('promotions');
}
}
});
LogoutRoute
export
default Ember.Route.extend({
beforeModel: function() {
this.controllerFor('sessions.login').reset();
this.transitionTo('sessions.login');
}
});
LoginAction on Controller
loginUser: function() {
var _this = this;
var login = this.get("login");
var pass = this.get("password");
Login Form
<form role="form" {{action 'loginUser' on='submit'}}>
<div class="form-group">
{{input type="text" class="form-control" id="login" placeholder="Login" value=login autofocus="autofocus"}}
{{input type="password" class="form-control" id="password" placeholder="Password" value=password}}
</div>
<div class="form-group">
<span class="input-group-btn">
<button class="btn btn-default" type="submit" ><span>Entrar</span></button>
</span>
</div>
</form>
After testing some more, i saw that the textFields are duplicating when i transition back to this page.
I really can't figure out, why