Yii2 - ajax validation on submit only, client validation on change - yii

Is it possible to enable ajax validation on form submit only, and have client / JS validation on change / blur? So for example:
User inputs data and presses tab key : client validation should apply
User then submits form : ajax validation should apply
If validation is successful, proceed with normal form submit
Here is my ActiveForm config:
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'enableAjaxValidation' => true,
'enableClientValidation' => true,
'validateOnBlur' => true,
'validateOnChange' => true,
'validateOnSubmit' => true,
]); ?>
Currently, when I focus out of a field, it will apply ajax validation as well - I do not want this.

To do it the way you want it to work you need to submit the form via AJAX and it is required to attach a handler on afterValidate event. The handler will replace default form submission and is responsible for sending data to the server and displaying error messages if server validation fails. Displaying validation messages requires support on the controller side.
You can update the form name/id respectively in the script. and add the alerts in the error and incorrect server response. your form will be automatically saved via ajax call without reloading you can reset the form inside the success part.
Attach a handler to form:
$this->registerJs('$(\'#my-form\').on(\'afterValidate\', function () {
var $yiiform = $(this);
$.ajax({
type: $yiiform.attr(\'method\'),
url: $yiiform.attr(\'action\'),
data: $yiiform.serializeArray(),
}
)
.done(function(data) {
if(data.success) {
$yiiform.submit();
// data is saved
} else if (data.validation) {
// server validation failed
$yiiform.yiiActiveForm(\'updateMessages\', data.validation, true); // renders validation messages at appropriate places
console.log("error on form"+data.validation);
} else {
console.log("incorrect server response");
// incorrect server response
}
})
.fail(function () {
console.log("request failed");
// request failed
})
return false; // prevent default form submission
})');
Just make sure you have your action coded like below
Controller action:
public function actionUpdate($id)
{
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
//call model validate to trigger validation messages
$model->validate();
$result = [];
// The code below comes from ActiveForm::validate(). We do not need to validate the model
// again, as it was already validated by save(). Just collect the messages.
foreach ($model->getErrors() as $attribute => $errors) {
$result[\yii\helpers\Html::getInputId($model, $attribute)] = $errors;
}
if (!empty($result)) {
return $this->asJson(['validation' => $result]);
}
return $this->asJson(['success' => true]);
} elseif ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', 'Form saved successfully');
return $this->redirect('index');
}
return $this->render('form', ['model' => $model]);
}
And most important just initialize your form with the option enableClientValidation as true like below.
$form = ActiveForm::begin([
'id' => 'login-form',
'enableClientValidation' => true,
]);

Related

React-native : how to check if it's the user's first connection?

I 'm new to react-native and I would like to set up my first page of the app with
1- The user never logged in the app, I 'll present the app with some slides + go to signup page
2 -The user is already in => directly go to the user page
3 -The user is already in but the id is not correct
Can you guide me through it ?
For the first point I really don't know how to check if the user exist in my db
For the second and third points I thought I'd try :
onPressLogin(){
fetch('linktomyAPI',{
method: 'POST',
headers:{
'Content-Type' : 'application/json',
'Accept':'application/json'
},
body: JSON.stringify({
username:this.state.username,
password: this.state.password,
})
})
.then(response => response.json())
.then((responseData) =>{
if(responseData.error !== 1){ // verify the success case, as you didn't provide the success case i am using the error code
this.setState({ // its recommended you verify the json before setting it to state.
userdetail: responseData,
})
setTimeout(() => {
Actions.Authentication();
}, 2300);
AsyncStorage.setItem('username', this.state.username); // its setItem not saveitem.
} else {
console.log(responseData);
Alert.alert(JSON.stringify(responseData)); // Alerts doesn't allow arrays or JSONs, so stringify them to view in Alerts
}
}).catch((error) => {
// handle catch
console.log("error:"+JSON.stringify(error));
});
}
Do you think I can do this way, is that relevant?
Thanks for taking the time to answer me. I'm really new so I need help and explanations. Thanks !!! :)
you can use the UserInfo to do. firstly, you have saved the user info in memory use Session js and in the device local file use AsyncStore. you already do it use the AsyncStore.
firstly save the user info into Session,the Session structure is:{id:"dd333",name:"john"},
...
setTimeout(() => {
Actions.Authentication();
}, 2300);
Session.id = res.id;
Session.name = res.name;
AsyncStorage.setItem('userinfo', Json.stringfy(res));
then you can show diffent page and condition.
// in the component which you want compoentDidMount
componentDidMount() {
// here you can try to get it from AsyncStore if the Session is null.
// then hanle it as the followng
if(Session.id == ""){
//default it is "", it means the user does not login
} else if(Session.id != "222"){
// the user id is not correct
} else {
// here is the user login and id is correct
}
}

VueJS check periodically global function

I have a VueJS project where I need to check periodically a function to see if a token has expired once the user login to the app successfully and if the token has expired have to show a modal message to user.
I have my Singin.vue file that contains the following code:
....
methods: {
...mapActions(['authorize']),
submit() {
this.$validator.validateAll().then(result => {
if (result) {
this.error = null;
this.processing = true;
this.authorize(this.credentials).then(() => {
// ***********
// HERE I have to check periodically if the token has expired
// ***********
this.$router.push({name: 'home'});
}).catch(error => {
console.warn('error message', error);
this.error = error.response.data.message;
this.processing = false;
});
}
});
}
When this.authorize happens I route to home, but before that happens I need to start calling a function periodically. Then If user Logoff then I have to clear the interval.
So first, I don't know where is the best place to have this TokenExpiration function code. Does it make sense to have it in a store file?
This is my api.js store file where I have my authorize function and my logout function, does it make sense to have the tokenExpirationCheck function here also?
There are several ways of doing it, but I would probably solve this using a plugin, because timers should not be in the store, and the behavior is global to the application, so I wouldn't put it into any single component.
The pugin would have a vuex.watch on the stoere's logged-in flag. When it goes from false => true, remove the timer (if active) and if it goes from false => true, add the timer. The timer function can then call the vuex dispatch to handle the functionality.

Express Validator using 5.3.0 middleware function

const app = require('express')();
const session = require('express-session');
const {
check,
validationResult
} = require('express-validator/check');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}))
app.get("/test", [
// username must be an email
check('username').not().isEmpty(),`//.withCustomMessage() based on the content of req.session`
// password must be at least 5 chars long
check('password').not().isEmpty()
],(req,res)=>{
console.log("req.session", req.session);
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
//console.log("req.session",req.session);
});
app.get("/",(req,res,next)=>{
req.session.message = "beoulo ";
// console.log(req.session);
res.status(200).json({
"status" :"session set"
});
});
app.listen(3000,()=>{
console.log("Listening on port 3000!!!");
});
Is passing Check directly as middleware the only way to use it .?
Can we still use req.checkbody(field,errormessage) format or something equivalent inside a seperate middleware function cause the error message has to be taken from the session
I want to access a variable from req.session and based on that generate a custom error message
previous implementation worked fine as it used req.checkBody()
with new changes what should I do to handle this scenario .
You can rewrite the default error messages inside of your own handler.
Assuming that your error messages are stored in req.session.errors, and that this is an object that maps a particular validation to a particular error message.
For instance:
// req.session.errors =
{
"USERNAME_EMPTY" : "The username cannot be empty",
"PASSWORD_EMPTY" : "The password cannot be empty",
}
Next, you would provide custom messages for each validation, that match the keys of the abovementioned object:
check('username').not().isEmpty().withMessage('USERNAME_EMPTY')
check('password').not().isEmpty().withMessage('PASSWORD_EMPTY')
Finally, inside your handler, you perform a lookup from the validation errors to the error message values:
if (! errors.isEmpty()) {
const list = errors.array();
list.forEach(error => {
error.msg = req.session.errors[error.msg] || error.msg;
});
return res.status(422).json({ errors: list });
}
Or just depend on an older version of express-validator so you can keep using the legacy API.

Vue resource, using the same form for inserts and updates

I'm using Vue resource to connect to my backend api. I have a form component which I use for both creating a new resource item and modifying existing resource items. The form works fine, but when I want to save the form, it needs to use the proper http method for the api call. If I am creating a new item, it should use the POST method, and if I'm updating an existing item it should use the PUT method. Right now, my form save method looks something like this:
if (this.itemId > 0) { // Update existing item
myresource.update({id: this.itemId}, this.item).then(response => {
//...
}, response => {
//...
});
}
else { // Post new item
myresource.save({}, this.item).then(response => {
//...
}, response => {
//...
});
}
Basically, I have to use an if statement to check whether to use the update or save resource functions, then the success/fail promises both use the same code. Is there some way to combine the two methods above with something like this:
var method = this.itemId ? 'PUT' : 'POST';
myresource.request(method, {id: this.itemId}, this.item).then(response => {
//...
}, response => {
//...
});
The above code obviously doesn't work, but is there a similar way to accomplish this without using an if statement and repeating my success/fail promises for each request type?
One simple option would be to create the request based on the conditions and then connect the remaining promises in a single chain:
const request = (this.itemId > 0) ? myresource.update ? myresource.save;
request({
id: this.itemId // Make the save call ignore the id
}, this.item).then(response => {
// Shared code
});

Custom Vue.js AJAX directives onSuccess processing

I have setup a custom Vue directive for ajax forms, however I would like it to process a custom onSuccess call with the received data...
The directive looks like this:
Vue.directive('ajax', {
bind: function (el, binding, vnode) {
el.addEventListener(
'submit', function(e){
e.preventDefault();
let formData = new FormData(el);
let method = el.method.toLowerCase();
Vue.http[method](el.action, formData).then(response => { // success callback
data = response.data;
// Do a custom callback for binding.expression
}, response => {
// error callback
});
}
);
},
});
And im using it in various components, in this form:
<form method="POST" action="api/groups" v-ajax="customFunction"></form>
I would like the addGroup method called for the component in which the group is with data passed as a parameter...
Vue.component('x',{
methods: {
customFunction: function(data) : { }
}
});
In this way I would be able to turn any form into AJAX submit, with the possibility to process the data differently for each component. Is that possible?
Your directive needs multiple values a callback function and data and may add up in future. Assign your directive to object literal.
<form method="POST" action="api/groups" v-ajax="{successCb: customSuccessFunction, errorCb: customErrFunction, data: data}">
</form>
In your directive you can access them as below
Vue.directive('ajax', function (el, binding) {
console.log(binding.value.successCb.color) // => customSuccessFunction
console.log(binding.value.errorCb.text) // => customErrFunction
console.log(binding.value.data) // => data object
})