form segmentation value 'each' loop ajax request - html

I have a html form to get information from users. I segment this information to launch from each segment an ajax request (the segmentation is done following ">" characters). However, when I did not use 'each' jquery function (so without segmentation) ajax works, but with each jquery function ajax does not work at all and the loop does not work.
This is my javascript:
$(document).ready(function() {
$('#formu').on('submit', function() {
var sequence = $('#sequence').val().split(">"); // I segment here
$.each(sequence, function(k){ // for each segment I launch a request with the segment as argument
if (sequence[k].length != 0){
alert(sequence[k]);
$.ajax({
url:'run.py',
type:$(this).attr('method'),
data: {'sequence':">"+sequence[k]}, // the segment is given to python script
success: function(data){
$('#result').html(data);
}
});
} // enf if
//return false;
}); // end of each
}); // end of submit
}); // end of jquery
This is my html:
MyPage
<form method="post" id="formu" >
<textarea id="sequence" name="sequence" cols="80" rows="10"></textarea>
<br/>
<input type="submit" value="Submit">
</form>
<div id="result">
</div>
example of input:
>mysequence 1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>mysequence 2
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
>mysequence 3
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

When you loop through an array using jQuery.each. You get passed key, value pairs into the function. What you were doing was getting the value back from the key, while it is already passed. There is no need to do that. Also, instead of returning false you can also prevent the event from doing its default behaviour
$('#formu').on('submit', function(e) {
e.preventDefault(); // Prevent default behavior
var sequence = $('#sequence').val();
alert(sequence); // Check this value
sequence = sequence.split(">");
$.each(sequence, function(key, value){
alert(value); // Alert is better placed here so you know what 'value' is
if (value.length != 0){
$.ajax({
url : 'run.py',
type : $("#formo").attr('method'), // Replaced 'this' with your form as 'this' might mean something you are not expecting here
data : { 'sequence' : ">"+value },
success : function(data){
$('#result').html(data);
}
});
}
});
});
As we don't know about you getting errors it is hard to do more than 'general improvements' to your code.
I think the reason it does not work might be because you use this in a $.each loop which might refer to the string instead of the form.

Related

Reactive statement throws an error once it hits undefined variables. How to prevent it?

just playing around with Svelte. And I have noticed that when the reactive statement ($) is being calculated, it will throw an error when one of the depending variables is undefined.
While this behaviour is correct, how can I prevent it from calculating if the variable it depends on is undefined?
To see the error, type something in either of the fields, and then delete it, so it becomes empty.
<script>
let num = "";
let secondNum = "";
$: output = "prefix" + num.toString() + secondNum.toString() + "suffix";
function handleChange() {
console.log(num, secondNum);
}
function handleSubmit(e) {
e.preventDefault();
}
</script>
<form on:submit={handleSubmit}>
<input type="number" bind:value={num} on:change={handleChange}>
<input type="number" bind:value={secondNum} on:change={handleChange}>
<button type="submit">
Submit
</button>
</form>
Clearing a numeric input causes its bound value to be undefined. num.toString() is an error if num is undefined (that's a JavaScript thing, not a Svelte thing).
Easiest fix would be this:
$: output = `prefix${num || ''}${secondNum || ''}suffix`;

how to detect div value (Django rendered) change with MutationObserver

I am rendering a value from django backend to frontend, and I am trying to detect the div value change with MutationObserver. Below is my current code:
MutationObserver part:
window.addEventListener('load', function () {
var element = document.getElementById('myTaskList');
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var observer = new MutationObserver(myFunction);
observer.observe(element, {
childList: true
});
function myFunction() {
console.log("this is a trial")
console.log(element);
console.log(element.innerHTML);
}
// setTimeout(function(){
// element.innerHTML = 'Hello World!';
// }, 1000);
//
// setTimeout(function(){
// element.innerHTML = 'Hello Space!';
// }, 2000);
});
html part:
<div hidden id="myTaskList">{{resultList | safe}}</div>
I am rendering a string "dummyValue" to the div, but just don't see the value from the console.log() statements inside function.
this works well when I uncomment the setTimeout functions though.
Thanks for any help on why MutationObserver won't detect the rendered div value
I finally figured out the reason. Hope this might be helpful for people having similar issues in the future.
So, basically I was using my Django form submit button to do two actions at one time:
1. submit data to the view and process the data in the view;
2. trigger another function with the click action through
Ajax.
The second action was blocked by the first action, and I was only able to get result from action 1.
My solution: I modified action 1 to use Ajax as well. As I mentioned above, I originally used the Django form to submit data. I trigger action 2 inside the success function of action 1. Everything is working well now.

trigger active form validation manually before submit

is it possible to call the active form validation programmatically via javascript? I need to call the validation procedure before doing some ajax operations.
Guess I'm a bit late with a reply here but I just had the same question and the solution by soju did not work for me either.
So I looked a bit deeper into the JS-code of ActiveForm and found that it appears to monitor the status of each field in a variable and if the field is "untouched" the validation isn't triggered, unless submitting the actual form. So I changed my call to this:
var $form = $("#my-form"),
data = $form.data("yiiActiveForm");
$.each(data.attributes, function() {
this.status = 3;
});
$form.yiiActiveForm("validate");
This now appears to be working as I expect.
We can achieve that by merging #BlueZed and #S Rana's answer.
You can write below script, so we can check that if form has any error in it then form will not submit (Even It will work for tabular (wizards) like form ).
var $form = $("#form"),
data = $form.data("yiiActiveForm");
$.each(data.attributes, function() {
this.status = 3;
});
$form.yiiActiveForm("validate");
if ($("#form").find(".has-error").length) {
return false;
}
Thanks blue zed,
but before this -
to append form field, u need to do this stuff...
// your input
$inputData = $form->field($model,"prductName");
// this remove next line & convert double quotes to single quotes
$newInputData= trim(preg_replace('/\s+/', ' ',str_replace('"',"'", $inputData)));
// then append like this
$("#table").append("'.$newInputData.'");
// this worked for me along with blue zend solution like this below
$this->registerJs('
$(document).on("click","input[type=text]",function(){
var $form = $("#w0"),
data = $form.data("yiiActiveForm");
$.each(data.attributes, function() {
this.status = 3;
});
$form.yiiActiveForm("validate");
});
');
Yes it is possible, you should try this :
$('#form-id').yiiActiveForm('validate');
this is worked for me
$this->registerJs( "
$('body').on('beforeSubmit', 'form#product-form', function () {
var form = $(this);
// return false if form still have some validation errors
if (form.find('.has-error').length) {
return false;
}
// submit form
$.ajax({
url: form.attr('action'),
type: 'post',
data: form.serialize(),
success: function (response) {
}
});
return false;
}); ");
The validation is activated when submitting the form. Thus, this will work:
$form.data('yiiActiveForm').submitting = true;
$form.yiiActiveForm('validate', false);
The second argument to validate is called forceValidate.
To validate manually from javascript, you should place following code to end of form submit event.
return $('#your-form-id').yiiActiveForm('validate');
For novice users,
$('#your-form-id').submit(function () {
return $('#your-form-id').yiiActiveForm('validate');
});
Also note that you should specify form id on ActiveForm like below
<?php $form = ActiveForm::begin(['id' => 'd4d-hub-form']); ?>
I had the same issue. And the are no clearly solution in official documentation and I don't know why any solution on Stackoverflow does not work for me. May be in the different versions of yii2 there is different ways to do that. I have spent a lot of time to find solution. In my case I have triggered validation for individual input on the form:
$('#form-id').data('yiiActiveForm').submitting = false;
$('#form-id').yiiActiveForm('validateAttribute', 'input-id'); //this will triger validation for input with id 'input-id'
$('#form-id').yiiActiveForm('validateAttribute', 'other-input-id'); //this will triger validation for input with id 'other-input-id'
please notice second parameter of function yiiActiveForm() it is not selector and it is not name of attribute it is id of input of attribute!!!
Try this
$("#form-id").data('yiiActiveForm').submitting = true;
$("#form-id").yiiActiveForm('validate');
it will show validation error if any field is not valid.
Also if all fields are validate then it will submit the request

AngularJS - Server side validation and client side forms

I am trying to understand how to do the following things:
What is the accepted way of declaring a form. My understanding is you just declare the form in HTML, and add ng-model directives like so:
ng-model="item.name"
What to send to the server. I can just send the item object to the server as JSON, and interpret it. Then I can perform validation on object. If it fails, I throw a JSON error, and send back what exactly? Is there an accepted way of doing this? How do I push validation errors from the server to the client in a nice way?
I really need an example, but Angulars docs are pretty difficult to understand.
Edit: It seems I've phrased my question poorly.
I know how to validate client side, and how to handle error/success as promise callbacks. What I want to know, is the accepted way of bundling SERVER side error messages to the client. Say I have a username and password signup form. I don't want to poll the server for usernames and then use Angular to determine a duplicate exists. I want to send the username to the server, validate no other account exists with the same name, and then submit form. If an error occurs, how do I send it back?
What about pushing the data to the server as is (keys and values) with an error field appended like so:
{
...data...
"errors": [
{
"context": null,
"message": "A detailed error message.",
"exceptionName": null
}
]
}
Then binding to the DOM.
I've also been playing around with this kind of thing recently and I've knocked up this demo. I think it does what you need.
Setup your form as per normal with any particular client side validations you want to use:
<div ng-controller="MyCtrl">
<form name="myForm" onsubmit="return false;">
<div>
<input type="text" placeholder="First name" name="firstName" ng-model="firstName" required="true" />
<span ng-show="myForm.firstName.$dirty && myForm.firstName.$error.required">You must enter a value here</span>
<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
</div>
<div>
<input type="text" placeholder="Last name" name="lastName" ng-model="lastName"/>
<span ng-show="myForm.lastName.$error.serverMessage">{{myForm.lastName.$error.serverMessage}}</span>
</div>
<button ng-click="submit()">Submit</button>
</form>
</div>
Note also I have added a serverMessage for each field:
<span ng-show="myForm.firstName.$error.serverMessage">{{myForm.firstName.$error.serverMessage}}</span>
This is a customisable message that comes back from the server and it works the same way as any other error message (as far as I can tell).
Here is the controller:
function MyCtrl($scope, $parse) {
var pretendThisIsOnTheServerAndCalledViaAjax = function(){
var fieldState = {firstName: 'VALID', lastName: 'VALID'};
var allowedNames = ['Bob', 'Jill', 'Murray', 'Sally'];
if (allowedNames.indexOf($scope.firstName) == -1) fieldState.firstName = 'Allowed values are: ' + allowedNames.join(',');
if ($scope.lastName == $scope.firstName) fieldState.lastName = 'Your last name must be different from your first name';
return fieldState;
};
$scope.submit = function(){
var serverResponse = pretendThisIsOnTheServerAndCalledViaAjax();
for (var fieldName in serverResponse) {
var message = serverResponse[fieldName];
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
if (message == 'VALID') {
$scope.myForm.$setValidity(fieldName, true, $scope.myForm);
serverMessage.assign($scope, undefined);
}
else {
$scope.myForm.$setValidity(fieldName, false, $scope.myForm);
serverMessage.assign($scope, serverResponse[fieldName]);
}
}
};
}
I am pretending to call the server in pretendThisIsOnTheServerAndCalledViaAjax you can replace it with an ajax call, the point is it just returns the validation state for each field. In this simple case I am using the value VALID to indicate that the field is valid, any other value is treated as an error message. You may want something more sophisticated!
Once you have the validation state from the server you just need to update the state in your form.
You can access the form from scope, in this case the form is called myForm so $scope.myForm gets you the form. (Source for the form controller is here if you want to read up on how it works).
You then want to tell the form whether the field is valid/invalid:
$scope.myForm.$setValidity(fieldName, true, $scope.myForm);
or
$scope.myForm.$setValidity(fieldName, false, $scope.myForm);
We also need to set the error message. First of all get the accessor for the field using $parse. Then assign the value from the server.
var serverMessage = $parse('myForm.'+fieldName+'.$error.serverMessage');
serverMessage.assign($scope, serverResponse[fieldName]);
I've got similar solution as Derek, described on codetunes blog. TL;DR:
display an error in similar way as in Derek's solution:
<span ng-show="myForm.fieldName.$error.server">{{errors.fieldName}}</span>
add directive which would clean up an error when user change the input:
<input type="text" name="fieldName" ng-model="model.fieldName" server-error />
angular.module('app').directive 'serverError', ->
{
restrict: 'A'
require: '?ngModel'
link: (scope, element, attrs, ctrl) ->
element.on 'change', ->
scope.$apply ->
ctrl.$setValidity('server', true)
}
Handle an error by passing the error message to the scope and telling that form has an error:
errorCallback = (result) ->
# server will return something like:
# { errors: { name: ["Must be unique"] } }
angular.forEach result.data.errors, (errors, field) ->
# tell the form that field is invalid
$scope.form[field].$setValidity('server', false)
# keep the error messages from the server
$scope.errors[field] = errors.join(', ')
Hope it would be useful :)
Well, the Answer Derek Ekins gave is very nice to work on. But: If you disable the submit button with ng-disabled="myForm.$invalid" - the button will not automatically go back to enabled as the server-based error state doesn't seem to be changed. Not even if you edit ALL fields in a form again to comply with valid inputs (based on client side validation).
By default, the form is submitted normally. If you don't provide a name property for each field in the form then it won't submit the correct data. What you can do is capture the form before it submitted and submit that data yourself via ajax.
<form ng-submit="onSubmit(); return false">
And then in your $scope.onSubmit() function:
$scope.onSubmit = function() {
var data = {
'name' : $scope.item.name
};
$http.post(url, data)
.success(function() {
})
.failure(function() {
});
};
You can also validate the data by setting up required attributes.
If you choose ngResource, it would look like this
var Item = $resource('/items/');
$scope.item = new Item();
$scope.submit = function(){
$scope.item.$save(
function(data) {
//Yahooooo :)
}, function(response) {
//oh noooo :(
//I'm not sure, but your custom json Response should be stick in response.data, just inspect the response object
}
);
};
The most important thing is, that your HTTP-Response code have to be a 4xx to enter the failure callback.
As of July 2014, AngularJS 1.3 has added new form validation features. This includes ngMessages and asyncValidators so you can now fire server side validation per field prior to submitting the form.
Angular 1.3 Form validation tutorial :
Taming forms in Angular 1.3
Video | Repo | Demo
References:
ngMessages directive
ngModel.NgModelController
I needed this in a few projects so I created a directive. Finally took a moment to put it up on GitHub for anyone who wants a drop-in solution.
https://github.com/webadvanced/ng-remote-validate
Features:
Drop in solution for Ajax validation of any text or password input
Works with Angulars build in validation and cab be accessed at formName.inputName.$error.ngRemoteValidate
Throttles server requests (default 400ms) and can be set with ng-remote-throttle="550"
Allows HTTP method definition (default POST) with ng-remote-method="GET"
Example usage for a change password form that requires the user to enter their current password as well as the new password.:
Change password
Current
Required
Incorrect current password. Please enter your current account password.
<label for="newPassword">New</label>
<input type="password"
name="newPassword"
placeholder="New password"
ng-model="password.new"
required>
<label for="confirmPassword">Confirm</label>
<input ng-disabled=""
type="password"
name="confirmPassword"
placeholder="Confirm password"
ng-model="password.confirm"
ng-match="password.new"
required>
<span ng-show="changePasswordForm.confirmPassword.$error.match">
New and confirm do not match
</span>
<div>
<button type="submit"
ng-disabled="changePasswordForm.$invalid"
ng-click="changePassword(password.new, changePasswordForm);reset();">
Change password
</button>
</div>
As variant
// ES6 form controller class
class FormCtrl {
constructor($scope, SomeApiService) {
this.$scope = $scope;
this.someApiService = SomeApiService;
this.formData = {};
}
submit(form) {
if (form.$valid) {
this.someApiService
.save(this.formData)
.then(() => {
// handle success
// reset form
form.$setPristine();
form.$setUntouched();
// clear data
this.formData = {};
})
.catch((result) => {
// handle error
if (result.status === 400) {
this.handleServerValidationErrors(form, result.data && result.data.errors)
} else {// TODO: handle other errors}
})
}
}
handleServerValidationErrors(form, errors) {
// form field to model map
// add fields with input name different from name in model
// example: <input type="text" name="bCategory" ng-model="user.categoryId"/>
var map = {
categoryId: 'bCategory',
// other
};
if (errors && errors.length) {
// handle form fields errors separately
angular.forEach(errors, (error) => {
let formFieldName = map[error.field] || error.field;
let formField = form[formFieldName];
let formFieldWatcher;
if (formField) {
// tell the form that field is invalid
formField.$setValidity('server', false);
// waits for any changes on the input
// and when they happen it invalidates the server error.
formFieldWatcher = this.$scope.$watch(() => formField.$viewValue, (newValue, oldValue) => {
if (newValue === oldValue) {
return;
}
// clean up the server error
formField.$setValidity('server', true);
// clean up form field watcher
if (formFieldWatcher) {
formFieldWatcher();
formFieldWatcher = null;
}
});
}
});
} else {
// TODO: handle form validation
alert('Invalid form data');
}
}
As I understand the question is about passing errors from the server to the client. I'm not sure if there are well-established practices. So I'm going to describe a possible approach:
<form name="someForm" ng-submit="submit()" ng-controller="c1" novalidate>
<input name="someField" type="text" ng-model="data.someField" required>
<div ng-show="someForm.$submitted || someForm.someField.$touched">
<div ng-show="someForm.someField.$error.required" class="error">required</div>
<div ng-show="someForm.someField.$error.someError" class="error">some error</div>
</div>
<input type="submit">
</form>
Let's say a server returns an object of the following kind:
{errors: {
someField: ['someError'],
}}
Then you can pass the errors to the UI this way:
Object.keys(resp.errors).forEach(i => {
resp.errors[i].forEach(c => {
$scope.someForm[i].$setValidity(c, false);
$scope.someForm[i].$validators.someErrorResetter
= () => $scope.someForm[i].$setValidity(c, true);
});
});
I make each field invalid and add a validator (which is not really a validator). Since validators are called after every change, this let's us reset the error status.
You can experiment with it here. You might also want to check out ngMessages. And a couple of related articles.

HTML5 history popstate with ajax, direct link not work

This Demo I want to click forward to content(index.php?id=id) click back to subject(index.php).
Q1. (index.php?id=id) direct link content page not available, it will show (index.php) subject , why?
Q2. Click back after the second times (forward > back > forward > back) then url will stop change, why? (Safari 5)
update: Use window.onpopstate url works fine. Not get this error.
Any suggestion will be appreciated.
$(window).bind('popstate', function(){
$.ajax({
url:location.pathname,
success: function(data){
$('.container').html(data);
}
});
});
Demo
$('.subject').click(function(){
$.ajax({
url: 'index.php?id=' + $(this).attr('rel'),
success: function(data){
$('.container').html(data);
}
});
var pageurl;
pageurl = 'index.php?id=' + $(this).attr('rel');
if(pageurl != window.location){
window.history.pushState({path: pageurl}, "", pageurl);
}
return false;
});
index.php
<div class="container">
<?php
if($_GET['id']){
...
print"<div class="content"></div>";
}
else{
...
print"<div class="subject" rel="id"></div>"
}
?>
</div>
The popstate event handler will receive an event object which contains the state you passed in to pushState. It's accessible via event.state Here's the event interface definition. Here's some demo code to illustrate the concept.
window.addEventListener("popstate", function(event) {
if( event.state !== null )
alert('Browser back button was clicked, state returned ' + JSON.stringify( event.state ) );
};
You just have to fetch the page content(i.e. subject for your scenario) using ajax and show it in your page container. Consider if you are in 'index.php' and clicks on a subject.. so your url changes to 'index.php?id=7'. Now you are clicking back.. here in 'popstate' event, 'location.pathname' gives 'http://host/index.php'. Thats it, fetch the data from 'location.pathname' using ajax and show it.
$(window).bind('popstate', function(){
$.ajax({
url:location.pathname,
success: function(data){
$('.content').html(data);
}
});
});