I'm learning Ember.js and now I'm stuck with a little contact form. I have two fields, one for email, another for the message, and I've set some validations for them. Unless the validations are satisfied, the 'Send' button must be disabled. Also, when everything is OK and user sends a message, the form must be substitued with a flash notice that message was sent. So, here's the code:
app/controllers/contact.js:
import Ember from 'ember';
export default Ember.Route.extend({
emailAddress: '',
message: '',
isValidEmail: Ember.computed.match('emailAddress', /^.+#.+\..+$/),
isValidMessage: Ember.computed.gte('message.length', 5),
isValid: Ember.computed.and('isValidEmail', 'isValidMessage'),
isInvalid: Ember.computed.not('isValid'),
actions: {
sendMessage() {
alert(`Sending message from: ${this.get('emailAddress')}`);
this.set('responseMessage', `Thank you! We've received message from: ${this.get('emailAddress')} . You will be responsed ASAP!`);
this.set('emailAddress', '');
}
}
});
and the template contact.hbs:
<h1>Contact us</h1>
<div class="well well-sm">
<p> If you have any questions, feel free to contact us!</p>
</div>
<div class="row">
<div class="col-md-6">
{{#if responseMessage}}
<br/>
<div class="alert alert-success">{{responseMessage}}</div>
{{else}}
<div class="form-group">
{{input type="email" value=emailAddress class="form-control" placeholder="Please type your e-mail address." autofocus="autofocus"}}
{{#if emailAddress.isValidEmail}}<span class="glyphicon glyphicon-ok form-control-feedback"></span>{{/if}}
</div>
<div class="form-group">
{{textarea class="form-control" placeholder="Your message. (At least 5 characters.)" rows="7" value=message}}
{{#if message.isValidMessage}}<span class="glyphicon glyphicon-ok form-control-feedback"></span>{{/if}}
</div>
{{/if}}
<button class="btn btn-primary btn-lg btn-block" disabled={{isInvalid}} {{action 'sendMessage'}}>Contact us!</button>
</div>
but, still:
The button remains active while the form is empty.
There's no glyphicons appearing while completing the form.
No notice appears when the message is sent.
What am I doing wrong?
You need to use the name of your form, as well as ng-disabled: DEMO
<form name="myForm">
<input name="myText" type="text" ng-model="mytext" required />
<button ng-disabled="myForm.$invalid">Save</button>
</form>
Please, feel free to vote me down, as I've shown a notorious lack of attention - the code which was pretended to be in app/controllers/contact.js was in app/routers/contact.js... So, naturally, when I removed it to the right place, everything worked. Special thanks to #Parag who made me enlighten. I am terribly sorry for disturbing and stealing time for this BS :(
Related
I have prepared a fiddle: https://jsfiddle.net/frvuw35k/
<div id="app">
<h2>Vue Event Test</h2>
<form #submit.prevent>
<div class="form-group row">
<div class="col-sm-12 input-group">
<input v-model="search" class="form-control" placeholder="Search users by Display Name"
#keyup.enter="doSearch">
<div class="input-group-append">
<button class="btn btn-primary" :disabled="!search" type="button"
#click.prevent="doSearch">
Search
</button>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-12">
<button v-if="showButton" class="btn btn-info"
#click.prevent="doSthElse">Do something else</button>
</div>
</div>
</form>
</div>
new Vue({
el: "#app",
data: {
search: null,
showButton: false
},
methods: {
doSearch: function(){
console.log('do search:', this.search)
this.showButton = true
},
doSthElse: function(){
console.log('do something else')
}
}
})
This simulates something I have faced in my Vue application.
There is a second button, that is shown when a condition is meet. This condition is a result of a search, that is triggered by hitting enter key on the input.
On a second enter after the condition was meet, also if one has clicked somewhere else and put the focus back on the input, the second button is triggered...
I don't see the reason and haven't found out how to avoid it so fare.
Explanation
This is caused by the browser behavior in a form.
The normal behavior is that when a user press down the Enter key on an input, it will submit the form.
Also, to submit a form, the browser will look for a button in the form, which is your "Do something else" button in your case.
So when the user presses Enter, it will trigger a button press on your last button, calling the doSthElse method.
How to fix
To fix this, simply add the a #keydown.enter.prevent listener on the form to prevent the default behavior to trigger.
I updated your fiddle with the fix : https://jsfiddle.net/vy57d63f/
Use this: You need to user #keydown and prevent modifier
<input v-model="search" class="form-control" placeholder="Search users by Display Name"
#keydown.enter.prevent="doSearch"/>
I have an HTML form that has its elements displayed in various Bootstrap modals. The first modal has a text box input that and a "Next" button to open the next modal. When the "next" button is pressed. I want to check if the text box is empty, and trigger a validation message. The form does not get submitted until the very end. Everything I've tried has not worked so far.
Javascript/jQuery code
$("#add_assistant_next").click(function () {
var textInput = document.getElementById('add_assistant_user');
var text = textInput.value;
if (text === "") {
textInput.setCustomValidity('Please fill out this field.');
textInput.checkValidity();
var form = $('#form_add_assistant');
form.find(':submit').click();
} else {
textInput.setCustomValidity('');
}
});
HTML
<form name="add_assistant" method="post" id="form_add_assistant">
<div class="modal-body">
<div class="step">
<span class="fas fa-arrow-right choose-arrow mr-1"></span>1. Choose a user to add
</div>
<div class="pl-3 pt-1">
<div>
<input type="text" id="add_assistant_user" name="add_assistant[user]" required="required" placeholder="UCInetID or UCI email address" class="mr-0 form-control" />
<button type="button" id="add_assistant_next" name="add_assistant[next]" data-toggle="modal" data-target="#add-user-modal" class="btn btn-outline-secondary btn">Look up user</button>
</div>
<input type="hidden" name="user_search_route" value="/courseSpace/20900/listAssistantEnrollment">
</div>
</div>
... form continues in other modals
Your JS code is probably fighting with Bootstrap for control of that button. To get around that, and have your validation, you could try modifying your code to have a middle step / temporary button to help with validation first before actually submitting. So something like this:
Javascript/jQuery code
$("#my_temp_button").click(function () {
var textInput = document.getElementById('add_assistant_user');
var text = textInput.value;
// Might also want to handle null and undefined cases?
if (text === "" || text === undefined || text === null) {
// I'm assuming if it's empty, it doesn't pass validation,
// so we just display this warning and wait for the user to fix it:
textInput.setCustomValidity('Please fill out this field.');
} else {
// it's not empty so validate:
if (textInput.checkValidity()) {
// it passed validation, so ok to submit.
// call the real button:
$('#add_assistant_next').click();
// do you need this?
var form = $('#form_add_assistant');
form.find(':submit').click();
} else {
// it failed validation, so display another error?
textInput.setCustomValidity('Try again.');
}
}
});
HTML:
<form name="add_assistant" method="post" id="form_add_assistant">
<div class="modal-body">
<div class="step">
<span class="fas fa-arrow-right choose-arrow mr-1"></span>1. Choose a user to add
</div>
<div class="pl-3 pt-1">
<div>
<input type="text" id="add_assistant_user" name="add_assistant[user]" required="required" placeholder="UCInetID or UCI email address" class="mr-0 form-control" />
<!-- Feel free to change the id name. This is the button the user sees. It's only purpose is to give your function above full control to it and prevent Bootstrap from touching it and jumping to the next modal without having the user fix the validation failure first: -->
<button type="button" id="my_temp_button" class="btn btn-outline-secondary btn">Look up user</button>
<!-- Hide the real button from the user: -->
<div style="display:none">
<button type="button" id="add_assistant_next" name="add_assistant[next]" data-toggle="modal" data-target="#add-user-modal" class="btn btn-outline-secondary btn">Look up user</button>
</div>
</div>
<input type="hidden" name="user_search_route" value="/courseSpace/20900/listAssistantEnrollment">
</div>
</div>
...
Have you tried adding a trap for the submit event itself?
$('#form_add_assistant').submit(function(evt){
//do your validation here
if (validation fails){
return false; // OR, alternatively, `evt.preventDefault()`
}
//form submission will continue if not returned false
});
References:
https://api.jquery.com/submit/
How to conduct manual form validation via jQuery .submit()
I try to achieve the following functionality. Have editable form inputs in an angular application. For example a user can see his first name being fetched by the server and then clicking an edit button the form text input appears, edit button disappears and in its place the buttons save and cancel appear. I use the angular-bootstrap-show-errors component to show errors.
However when a validation rule is not fulfilled during editing and I click on cancel button the form tries to show the error before going back to the starting state. For example, I press edit and delete all the first name characters, then press cancel, so before disappearing it tries to validate. Below is my view.
<!--First name edits-->
<div class="row">
<form name="firstNameEditForm" role="form" novalidate>
<div class="col-xs-3">
<p class="text-right">First Name:</p>
</div>
<div class="col-xs-6" ng-if="model.beforeFirstNameEdit">
<p class="text-success">
{{accountData.firstname || "Loading..."}}
</p>
</div>
<div class="col-xs-6" ng-if="!model.beforeFirstNameEdit">
<div class="form-group" show-errors>
<input name="firstName" ng-model="accountData.firstname" class="form-control" placeholder="First Name" type="text" required minlength=2 auto-focus />
<small class="help-block" ng-if="firstNameEditForm.firstName.$error.required">At least 2 characters required</small>
<small class="help-block" ng-if="firstNameEditForm.firstName.$error.minlength">At least 2 characters required</small>
</div>
</div>
<div class="col-xs-3" ng-if="model.beforeFirstNameEdit">
<button type="button" class="btn btn-warning btn-xs" ng-click="editFirstName()">Edit</button>
</div>
<div class="col-xs-3" ng-if="!model.beforeFirstNameEdit">
<button type="button" class="btn btn-success btn-xs" ng-click="update(accountData.firstname)">Save</button>
<button type="button" class="btn btn-danger btn-xs" ng-click="cancelFirstNameEdit()">Cancel</button>
</div>
</form>
</div><!--First name edits-->
And the controller
$scope.preFirstNameEditModel = {};
$scope.editFirstName = function() {
// Copy preedited data locally
$scope.model.beforeFirstNameEdit = false;
$scope.preFirstNameEditModel = angular.copy($scope.accountData.firstname);
}
$scope.cancelFirstNameEdit = function(){
$scope.model.beforeFirstNameEdit = true;
$scope.accountData.firstname = angular.copy($scope.preFirstNameEditModel);
};
How can I completely avoid validation when I click on cancel button? I read some answers on similar questions suggesting to change the type of button to type = "button" but still doesn't solve my issue.
The validation of the fields is triggered on focus lost, whichis causing the validation message. You can prevent this behaviour by using ng-show="submitted && firstNameEditForm.firstName.$error.required" and ng-show="submitted && firstNameEditForm.firstName.$error.minlength". This causes the message showing up only when the form is submitted.
Furthermore you have to change the type of the update button to submit.
I'm new to Angular and have trouble debugging my use of form validations. Namely I want to disable sumbit button if the form is invalid. My problem is that it stays invalid even if I've inserted apparently valid input. This goes so far as to be even invalid for empty form:
This is my template (original code is commented out):
<div>
<div class="row">
<div class="col-xs-6">
<a href ng-click="companiesCtrl.newEdit()" class="btn btn-primary">Uus asutus</a>
<table class="table">
<tbody>
<tr ng-repeat="company in companiesCtrl.list">
<td><a href ng-click="companiesCtrl.load(company)">{{company.name}}</a></td>
</tr>
</tbody>
</table>
</div>
<div class="col-xs-6">
<form id="editForm">
<!--<form ng-show="companiesCtrl.showEdit()" ng-submit="companiesCtrl.save()" name="editForm">-->
<!--<h4>Asutuse detailid</h4>-->
<!--<a ng-show="companiesCtrl.edit.id != 0" class="btn btn-primary" ng-href="../{{companiesCtrl.edit.id}}/product/list">Asutuse vaatele</a>-->
<!--<fieldset class="form-group">-->
<!--<input ng-model="companiesCtrl.edit.id"/>-->
<!--</fieldset>-->
<!--<fieldset class="form-group">-->
<!--<input placeholder="Asutuse nimi" title="Asutuse nimi" ng-model="companiesCtrl.edit.name" ng-required="true"/>-->
<!--</fieldset>-->
<!--<fieldset class="form-group">-->
<!--<input type="submit" class="btn btn-success" ng-disabled="editForm.$invalid" value="Salvesta" />-->
<!--</fieldset>-->
</form>
<span>{{editForm.$error}}</span>
<span>{{editForm.$invalid}}</span>
<span>{{editForm.$valid}}</span>
</div>
</div>
and this is the result from the "span"-s:
{"required":[{}]} true false
My question is, as I'm new to Angular, how to debug this, or what are the additional factors that can affect form "valid" status.
I managed to find the problem myself. I had multiple directives that were hidden by ng-show. All of them had a form named "editForm" this cause Angular to validate all the forms.
Just if someone ever needs this for debugging: I added following to my template:
<span ng-repeat="error in compForm.$error"><span ng-repeat="error2 in error">
{{companiesCtrl.logThis(error2)}}</span></span>
and the following to my controller:
this.logThis = function(toLog) {
console.log(toLog);
}
From log I saw that it was validating too many times and quessed my problem.
How do I modify Meteor's Accounts-ui to change the classes and the html tags rendered without re-writing all the accounts-ui logic?
For example I'd like to remove the "dropdown" behavior and just display the form directly in my page.
I read this answer but it doesn't go into detail - it just removes the default CSS. I'd like to go a bit deeper..
I figured this out for another answer, but thought I'd put it in here since it seems like a quicker way to get what you want.
Template.login.rendered = function()
{
Accounts._loginButtonsSession.set('dropdownVisible', true);
};
(Template.login should be Template.yourTemplateWithLoginButtons)
Styling
Remove accounts-ui
meteor remove accounts-ui
Add accounts-ui-unstyled & less
meteor add accounts-ui-unstyled
meteor add less
Finally, add the following file to your project directory & edit it to your viewing pleasure
https://github.com/meteor/meteor/blob/master/packages/accounts-ui/login_buttons.less
More customization
You can edit the accounts-ui package and edit html & js without starting from scratch:
Remove the accounts-ui-unstyled package and add the stuff in the dir below (except package.js & accounts_ui_tests.js) to your project's client dir, add accounts-urls and edit it to fine tune it to your spec.
https://github.com/meteor/meteor/tree/master/packages/accounts-ui-unstyled
Until meteor gives us a way to specify load order
Rename the following files so they load in the correct order
1accounts_ui.js
2login_buttons.html
3login_buttons_single.html
4login_buttons_dropdown.html
5login_buttons_dialogs.html
6login_buttons_session.js
7login_buttons.js
8login_buttons_single.js
9login_buttons_dropdown.js
login_buttons_dialogs.js
If all you want to do is remove the drop down behavior, then it would suffice to just add the accounts-ui-unstyled package and hide the components you don't want visible: e.g.:
.login-link-text { display: none; }
in your CSS to hide the Sign in link.
Here are the relevant id's and classes that you look into styling:
#forgot-password-link
#login-buttons
#login-buttons-password
#login-dropdown-list
#login-email
#login-email-label
#login-email-label-and-input
#login-password
#login-password-label
#login-password-label-and-input
#login-sign-in-link
#signup-link
.accounts-dialog
.additional-link
.additional-link-container
.login-button
.login-button-form-submit
.login-buttons-dropdown-align-left
.login-close-text-clear
.login-close-textClose
.login-form
.login-form-sign-in
.login-link-and-dropdown-list
.login-link-text
.login-password-form
Create your own html template similar to this below. Style it to your taste.
meteor add accounts-password accounts-ui
<template name="login">
<form class="login-form">
<div class="panel panel-default">
<div class="panel-heading">
<h3>Login</h3>
</div>
<div class="panel-body">
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" id="email" placeholder="Email address">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" id="password" placeholder="password">
</div>
</div>
<div class="panel-footer">
<button type="submit" class="btn btn-danger btn-lg">Login</button>
</div>
</div>
</form>
</template>
You can now call Meteor.loginWithPassword in the template event like so:
Template.login.events({
'submit .login-form': function(e) {
e.preventDefault();
var email = e.target.email.value;
var password = e.target.password.value;
Meteor.loginWithPassword(email, password,function(error){
if(error) {
//do something if error occurred or
}else{
FlowRouter.go('/');
}
});
}
});
You can create another form for registration as well.
Just call Accounts.createUser(object, callback);
This answer is a bit coming late but might help.
Create your own html template similar to this below. Style it to your taste.
meteor add accounts-password accounts-ui
<template name="login">
<form class="login-form">
<div class="panel panel-default">
<div class="panel-heading">
<h3>Login</h3>
</div>
<div class="panel-body">
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" id="email" placeholder="Email address">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" id="password" placeholder="password">
</div>
</div>
<div class="panel-footer">
<button type="submit" class="btn btn-danger btn-lg">Login</button>
</div>
</div>
</form>
</template>
You can now call Meteor.loginWithPassword in the template event like so:
Template.login.events({
'submit .login-form': function(e) {
e.preventDefault();
var email = e.target.email.value;
var password = e.target.password.value;
Meteor.loginWithPassword(email, password,function(error){
if(error) {
//do something if error occurred or
}else{
FlowRouter.go('/');
}
});
}
});
You can create another form for registration as well.
Just call Accounts.createUser(object, callback);