How can I avoid submitted state (and ng-submitted class & submitted scope property as true boolean) in a form when it is submitted?
Updated:
( function() {
angular
.module( 'app', [] )
.controller( 'SubmitController', SubmitController );
function SubmitController() {
var vm = this;
vm.submit = submit;
function submit( e ) {
console.log( 'Submit!' );
e.stopPropagation();
}
}
} )();
form,
[ng-form] {
padding: 1em;
border: 1px solid black;
}
.ng-submitted {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="SubmitController as vm">
<div ng-form>
<form novalidate ng-submit="vm.submit($event)">
<input type="text">
<button type="submit">Submit</button>
</form>
</div>
</div>
</div>
The main objective is that submit event was not fired and not arrived to parent element ([ng-form]).
Simple way to prevent the form to submit, add this type of logical condition with your Angular code:
<form name="formName" novalidate ng-submit="formName.$valid && ANY_LOGIC_CONDITION_LIKE_LOGIN.submit()">
This will help to prevent the form submission
See this.
Use formName.$valid to prevent submit form and add ng-model in your text box.
( function() {
angular.module( 'app', [] )
.controller( 'FormsController', FormsController );
function FormsController() {
var vm = this;
vm.submit = submit;
function submit( e ) {
console.log( 'Submit!' );
e.preventDefault();
e.stopPropagation();
}
}
} )();
[ng-form],
form {
padding: 10px;
border: 1px solid #000;
&.ng-submitted {
border: 1px solid #f00;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="FormsController as vm">
<div ng-form>
<form name="test" novalidate name="test" ng-submit="test.$valid && vm.submit($event)">
<input type="text" ng-model="vm.test" required>
<button type="submit">Submit</button>
</form>
</div>
</div>
</div>
See #Taylor Buchanan's answer here, once you added the directive. you can simply call it form your JavaScript function.
What you have to do:
Add that directive to your code.
Add a name to your parent-form: ng-form="myForm".
pass your form to your submit function: `vm.submit(myForm)'.
And then modify your function:
function submit( form ) {
console.log( 'Submit!' );
form.$setUnsubmitted();
}
P.S. Since I wasn't sure which one of the forms you wanted to unSubmit, So I assumed you want both. But Obviously, you are getting full controll, so you can modify the code accordingly.
UPDATED
Seeing you code/behavior (after the update)...
Why must you submit the child form? What is the benefit? I'm seeing that you are using novalidate, so browser validation won't work.
Why you don't simply use a ng-click="vm.click(myForm)". You won't have any troubles with parent form, and angularjs won't add/set any $submitted state or ng-selected class, so you can handle this manually as you want passing a simple form instance.
PLUNKER DEMO
HTML
<div ng-form>
<form id="myForm" name="myForm" novalidate >
<label>My Input:</label>
<input type="text" name="myInput" ng-model="vm.item.myInput" required>
<button type="button" ng-click="vm.click(myForm)">Submit</button>
</form>
</div>
CONTROLLER/JS
app.controller('MainCtrl', function($scope) {
vm.click = function (myForm) {
alert('submitted');
//myForm.$valid
//call to server
}
});
If this way fits for your scenario, here you are an awnser... If not, I hope some of others solutions works for you :)
You can trigger $setPristine() method of FormController. You just need to pass the form itself as an arguement to your submit function and trigger its built in method.
e.g.
View:
<form name="MyForm" ng-submit="submitForm(MyForm)">
<button type="submit">Submit</button>
</form>
Controller:
$scope.submitForm = (form) => {
form.$setPristine();
alert("Form was submitted")
}
Here's a working example of the above code snippet.
Update
It doesn't matter if you have nested forms. Propagation doesn't work like you think it does. You should also give your ng-form a name. Then pass it down to the submit function and call its built in $setPristine() method once more.
Check this code chunk:
View:
<div ng-form name="outterForm">
<form name="myForm" ng-submit="vm.submit($event,myForm, outterForm)">
<button type="submit">Submit</button>
</form>
</div>
Controller:
function submit( e, form1, form2 ) {
console.log( 'Submit!' );
e.stopPropagation();
form1.$setPristine();
form2.$setPristine();
}
Here's an the updated example based on your code chunk demonstrating the nested forms case.
Related
Hello I'm trying to get my contact form to display a thank you message after the message has been sent.
I looked around but the stuff I find looks more complex than what I think i need.
I think it's something i'm missing about the event listener and how this form works.
Here's my code:
.thanks {
display: none;
}
<div class="form-wrap">
<form class="contact-form" action="https://getsimpleform.com/messages?form_api_token=aa0a1c58e87ea8816ba9ff7d7a71d0ef" method="post">
<!-- all your input fields here.... -->
<div class="name-email">
<input class="contact-field contactform-name" type='text' name='name' placeholder="Name" required/>
<input class="contact-field contact-form-email" type="email:" name="email" placeholder="e-mail" value="" required>
</div>
<textarea class="contact-field" name="message" rows="20" cols="80" placeholder="Your Message" required></textarea>
<input class="contact-field submit" type='submit' value='Send' />
</form>
</div>
<div class="thanks">
<h1>Thanks for the message!</h1>
</div>
<script>
function displayThanks() {
document.querySelector(".thanks").style.display = "block";
document.querySelector(".contact-form").style.display = "none";
}
document.querySelector(".submit").addEventListener("submit", displayThanks)
</script>
I could make it work on click, but that would mean that even if they don't send a message and just click submit they will get thank you (FOR WHAT!?)
Thanks!
M
You are mixing client side logic with server side logic. It's hard to answer your question because we don't know, what the action "action="https://getsimpleform.com/messages?form_api_token=aa0a1c58e87ea8816ba9ff7d7a71d0ef" in your form tag is doing.
It might be usefull to validate the form, before you consider to submit it. You can do this by using the click event of the submit button in combination with preventDefault like this (Just an Example, you could even use RegEx for emails and stuff):
document.querySelector(".submit").addEventListener('click', function(e) {
var userInputName = document.getElementsByName("name")[0].value,
userInputEmail = document.getElementsByName("email")[0].value,
userInputMessage = document.getElementsByName("message")[0].value;
if (userInputName.length > 0 && userInputEmail.length > 0 && userInputMessage.length > 0)
{
document.querySelector(".thanks").style.display = "block";
document.querySelector(".contact-form").style.display = "none";
}
else
{
e.preventDefault();
}
}, false);
I would recommend to use IDs instead of names though, because IDs are unique and don't require a node list or ambiguous jQuery. Than you can use something like this: userInputName = document.getElementById("name").value;
And don't forget to use the right CSS for your logic. Like:
.thanks {display: none;}
I am using a template based form in angular. I also use bootstrap (v4) and I wish to show some validation messages when the form was submitted.
This is my form:
<form [ngClass]="{'was-validated': wasValidated}">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" class="form-control" [(ngModel)]="category.name" #name="ngModel" required maxlength="100"/>
<div *ngIf="name.invalid" class="invalid-feedback">
<div *ngIf="name.errors.required">
Name is required.
</div>
</div>
</div>
<button type="submit" class="btn btn-success" (click)="save()">Save</button>
</form>
My component looks as follows:
category: Category;
wasValidated: boolean = false;
ngOnInit() {
this.reset();
}
save() {
this.wasValidated = true;
this.categoriesService.createCategory(this.category).subscribe(
() => {
this.notificationService.add(notifications.category_saved, {name: this.category.name});
this.reset();
},
() => this.notificationService.add(notifications.save_category_failed)
);
}
reset() {
this.wasValidated = false;
this.category = {} as Category;
}
This works, but I have a feeling it's overly complex and more like a workaround rather than the right way. What is the best way to accomplish this?
Note: the class was-validated must be present on the form element in order to show the div with class invalid-feedback. I'm using this: https://getbootstrap.com/docs/4.0/components/forms/#validation
Note 2: I have currently no mechanism yet to prevent form submission on error. I'd like to know a good solution for that as well!
With the answer from #Chellappan V I was able to construct the solution I wanted.
I have applied to following changes:
First added #form="ngForm" to the form tag in the template. Secondly I changed the ngClass expression to reference the submitted state of the form, rather than referring to a boolean which was set to true manually when form was submitted. Last but not least I pass the form in the submit method on the save button.
<form novalidate #form="ngForm" [ngClass]="{'was-validated': form.submitted}">
<!-- form controls -->
<button type="submit" class="btn btn-success" (click)="submit(form)">Save</button>
</form>
In the component I injected the template variable in the component with #ViewChild.
#ViewChild("form")
private form: NgForm;
The submit method now takes a form parameter of type NgForm which is used to check if the form was valid before sending a request to the backend:
submit(form: NgForm) {
if (form.valid) {
this.categoriesService.createCategory(this.category).subscribe(
() => {
this.notificationService.add(notifications.category_saved, {name: this.category.name});
this.reset();
},
() => this.notificationService.add(notifications.save_category_failed)
);
} else {
this.notificationService.add(notifications.validation_errors);
}
}
Finally the reset method resets the form and the model so it can be re-entered to submit a next instance:
reset() {
this.form.resetForm();
this.category = {} as NewCategoryDto;
}
I am building a React app with Semantic UI in Meteor. I have had two places where event handlers don't seem to be functioning in any capacity, and I haven't found anything online with a problem to the same extent.
Below is my React class. I have tried various ways of calling the eventHandler methods, but nothing works. That also seems irrelevant since I can't even get an anonymous function to run.
SaveSearchPopout = React.createClass({
getInitialState: function() {
return {username: "", queryname: ""};
},
handleUsernameChange:function(e) {
console.log(e.target.value);
this.setState({username: e.target.value})
},
handleQuerynameChange:function(e) {
this.setState({queryname: e.target.value})
},
handleSave:function(e) {
console.log("handling save");console.log(e);
e.preventDefault();
alert("saving!");
return false;
},
render: function() {
console.log(this);
return (
<div className="ui modal saveSearchPopout">
<div className="header">Save Search</div>
<div className="content">
<form className="ui form" onSubmit={function() {console.log("test");}}>
<div className="field">
<input type="text" name="username"
placeholder="Username"
value={this.state.username}
onChange={function() {console.log("update")}} />
</div>
<div className="field">
<input type="text" name="queryname"
placeholder="Name this search"
value={this.state.queryname}
onChange={this.handleQuerynameChange}></input>
</div>
<div className="actions">
<div className="ui cancel button">Cancel</div>
</div>
<button type="submit">Click</button>
<button className="ui button" type="button"
onClick={function() {console.log("saving");}}>Save</button>
</form>
</div>
</div>
);
}
});
The class is rendered from another classes method which looks like:
saveSearch: function() {
var backingDiv = document.createElement('div');
backingDiv.id = 'shadowPopupBack';
document.getElementsByClassName('content-container')[0].appendChild(backingDiv);
ReactDOM.render(<SaveSearchPopout />, backingDiv);
//this.props.saveSearch;
$('.ui.modal.saveSearchPopout')
.modal({
closeable:false,
onDeny: function() {
var container = document.getElementsByClassName('content-container')[0];
var modalContainer = document.getElementById('shadowPopupBack');
container.removeChild(modalContainer);
}
})
.modal('show');
},
The only button that works is the Semantic UI cancel button.
Has anyone else run into this or have any idea what I am missing. Thanks for the help.
Don't know if this is the case, but in newer versions of React (or JSX), when you pass a function to an HTML component or a custom component, that function is not automatically bound to this instance.
You must bind them manually. For example:
onChange={this.handleQuerynameChange.bind(this)}
Or you could use arrow functions, because they will automatically bind to this:
onChange={e => this.handleQuerynameChange(e)}
I eventually found the answer here. Semantic UI modal component onClose with React
and I haven't worked through it yet, but it looks like this is more Reactive solution than using jQuery to bind eventHandlers: http://www.agilityfeat.com/blog/2015/09/using-react-js-and-semantic-ui-to-create-stylish-apps.
Hope this is helpful to someone else.
I am trying to access the Wikimedia API and display ten results. I have written a function that accesses the API and works on its own.
However, when I try to integrate a Search button to call that function and pass the search string to the function nothing happens in Chrome or Firefox.
(oddly though when I use the snippet function within stackoverflow it works fine!)
I'm wondering why my code doesn't work in the browser - could this have something to do with synchronous/asynchronous behavior and if so what is happening?
I'd really appreciate some insight into what is going on...
Thanks!
$(document).ready(function() {
// Initialize the Global Variables
var api = "http://en.wikipedia.org/w/api.php?callback=?&format=json&action=query&generator=search&gsrnamespace=0&gsrlimit=10&prop=pageimages|extracts&pilimit=max&exintro&explaintext&exsentences=1&exlimit=max&gsrsearch=";
var searchTitle = "";
var searchString = "";
$('#submit').click( function() {
searchTitle = $("input:text").val();
searchString = api + searchTitle;
getWiki(searchString);
});
// the following function works if I call it statically outside of the button click
// for example: getWiki(api + "giraffe");
// but it doesn't work if I call it dynamically from within the button. Why?
function getWiki(val) {
$.getJSON(val, function(json) {
$.each(json.query.pages, function(prop, item) {
var id = "#result" + item.index
$(id).html(item.title + "<br>" + item.extract);
});
});
}
// END Document Ready Function
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<body>
<form class="form-wrapper">
<input type="text" id="search" placeholder="Search for..." required>
<input type="submit" value="go" id="submit">
</form>
<div class="container">
<div class="row">
<div class="col-xs-12">
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>
<div id="result4"></div>
<div id="result5"></div>
<div id="result6"></div>
<div id="result7"></div>
<div id="result8"></div>
<div id="result9"></div>
<div id="result10"></div>
</div>
</div>
</div>
</body>
Using CSS, when a link is clicked it brings up a hidden DIV that contains a form. The user will then enter information and then submit the form. I'd like the hidden DIV to remain visible, and a 'success message' to be displayed after submission. Then the user will have the option of closing the DIV. I can't get it to work without reloading the page, which causes the DIV to become hidden again. Any ideas?
<body>
Click Me
<!--POPUP-->
<div id="hideshow" style="visibility:hidden;">
<div id="fade"></div>
<div class="popup_block">
<div class="popup">
<a href="javascript:hideDiv()">
<img src="images/icon_close.png" class="cntrl" title="Close" />
</a>
<h3>Remove Camper</h3>
<form method="post" onsubmit="email.php">
<p><input name="Name" type="text" /></p>
<p><input name="Submit" type="submit" value="submit" /></p>
</form>
<div id="status" style="display:none;">success</div>
</div>
</div>
</div>
<!--END POPUP-->
<script language=javascript type='text/javascript'>
function hideDiv() {
if (document.getElementById) { // DOM3 = IE5, NS6
document.getElementById('hideshow').style.visibility = 'hidden';
}
else {
if (document.layers) { // Netscape 4
document.hideshow.visibility = 'hidden';
}
else { // IE 4
document.all.hideshow.style.visibility = 'hidden';
}
}
}
function showDiv() {
if (document.getElementById) { // DOM3 = IE5, NS6
document.getElementById('hideshow').style.visibility = 'visible';
}
else {
if (document.layers) { // Netscape 4
document.hideshow.visibility = 'visible';
}
else { // IE 4
document.all.hideshow.style.visibility = 'visible';
}
}
}
</script>
</body>
Forms by default submit content by changing to the specified page in its 'action' attribute. You will need to build additional scripts to prevent it from doing that and submit the data using either AJAX or jQuery then process the result.
Or you could simply use whatever language you're programming in to set the default visibility for the division. If the form data exists, display it by default, otherwise hide it by default.
How about using an AJAX call to post the form instead of posting back the whole page?
Instead of using a "submit" type for your button, you can use a "button" type and use a script called by onclick which will use ajax to submit the form and do whatever is necessary.
This defeats slightly the meaning of a form, but works well. You might also want to think about using a javascript library like prototype or similar (jquery, etc) that gives you the functionality to create a get or post array of your form in order to make it easier.