How to validate array inputs using proengsoft/laravel-jsvalidation and Laravel 5 - javascript

Hi I'm trying to validate an array input and select like this:
<td width="23%">
{!!Form::select('mmscod_id[]',['' => '- Seleccione un material -'] +$mat,null,['class' => 'form-control', 'id'=>'mmscod_id'])!!}
</td>
<td width="17%">
<input type="text" class="form-control" id="cantidad" name="vtcanp[]"/>
</td>
<td width="17%">
<input type="text" class="form-control" id="precio" name="vtprep[]"/>
</td>
I'm using the proengsoft/laravel-jsvalidation for client-side validation. For the back-end I use Laravel's Form request.
I also use the method of this site: How To: Validate an array of form fields with Laravel but it doesn't work and throws errors:
error1
error2
Edit:
I forgot to mention that these elements are created dynamically
Please help me

Laravel supports validating array inputs. You need to use this convention to validate array item.
$rules = [
'vtcanp.*' => 'required',
];
For example:
This is my custom request class
class InvoiceRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules =[
'client' => 'required',
'date' => 'required',
'total' => 'required',
'discount' => 'required',
'item.*' => 'required',
'rate.*' => 'required',
'quantity.*' => 'required',
];
return $rules;
}
}
And in the view added
{!! JsValidator::formRequest('App\Http\Requests\InvoiceRequest') !!}
These validate and show the error message with position of input array that I dynamically added to the view.

Related

Prevent InvalidArgumentException on dynamic form field creation

I'm following Symfony cookbook on dynamic form fields creation.
Basically, in my case, I have a Product, a ProductVersion and a Quantity field in my form.
On new forms, ProductVersion is hidden (only with a class attribute, It's still an EntityType).
On Product change (via select menu), I make an AJAX request to see if some ProductVersion exists for this product. If so, I populate the ProductVersion with available versions and show it to the user.
It's working fine with new forms. But when editing the same form, I have an InvalidArgumentException response on my AJAX request that tells me that the Quantity field is null :
Expected argument of type "int", "null" given at property path
"quantity".
I understand that indeed, I don't provide the quantity on my form submission through the AJAX request but that's the purpose of this method isn't it ? To only submit the field that makes a dynamic field change.
How can I do to avoid this Exception ?
Here is the ItemType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('product', EntityType::class, [
'label' => 'item.product',
'class' => Product::class,
'placeholder' => 'item.product',
]);
$formModifier = function (FormInterface $form, Product $product = null) {
if (null !== $product) {
$productVersions = $product->getVersions();
if (count($productVersions) > 0) {
$form->add('productVersion', EntityType::class, [
'class' => 'App\Entity\ProductVersion',
'placeholder' => 'item.product_version',
'choices' => $productVersions,
'label' => 'item.product_version'
]);
} else {
$form->add('productVersion', EntityType::class, [
'class' => 'App\Entity\ProductVersion',
'placeholder' => 'item.product_version',
'choices' => [],
'label' => 'item.product_version',
'disabled' => true,
'row_attr' => [
//'class' => 'd-none'
]
]);
}
} else {
$form->add('productVersion', EntityType::class, [
'class' => 'App\Entity\ProductVersion',
'placeholder' => 'item.product_version',
'choices' => [],
'label' => 'item.product_version',
'disabled' => true,
'row_attr' => [
//'class' => 'd-none'
]
]);
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$form = $event->getForm();
$data = $event->getData();
$options = $event->getForm()->getConfig()->getOptions();
//add custom product version according product selected
$formModifier($event->getForm(), $data->getProduct());
$form
->add('quantity', IntegerType::class, [
'label' => 'item.quantity',
]);
if ($data->getId()) {
$form
->add('save', SubmitType::class, [
'label' => $options['submit_btn_label'],
]);
} else {
$form
->add('save', SubmitType::class, [
'label' => $options['submit_btn_label'],
]);
}
}
);
$builder->get('product')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$product = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $product);
}
);
}
And here is the Javascript part :
$(document).ready(function () {
var $product = $('#item_product');
// When product gets selected ...
$product.on('change', function () {
console.log("product has changed")
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected product value.
var data = {};
data[$product.attr('name')] = $product.val();
// Submit data via AJAX to the form's action path.
console.log(data);
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: data,
success: function (html) {
// Replace current position field ...
$('#item_productVersion').closest('.form-group').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#item_productVersion').closest('.form-group')
);
// Position field now displays the appropriate positions.
}
});
});
})
As #Jakumi suggested, adding an empty_data option to the quantity field solved the problem. Nevertheless, It gives an error on the quantity on the form received via the AJAX request and that's normal.
I found that this is not a "clean" method (you have to tweak form fields, you load the entire page on each AJAX request, etc..) so I decided to create a special route dedicated to form submissions that are meant to add/remove/edit fields.
This route creates a new form with preset fields (in my case the product selected by the user).
Then I can populate the other field (in my case the 'ProductVersion') with a regular PRE_SET_DATA Form Event.
This way :
- I don't have to worry about any other required field
- I can serialize the entire form in my AJAX request (no need to find the field, everything is done in the form builder)
- The AJAX request only output the form (I guess this improves performance a bit)
The form builder doesn't change (you still need to listen to PRE_SET_DATA and POST_SUBMIT. If you don't listen to the POST_SUBMIT, the form will not know about the choices you added dynamically and It will give you an error).
Here is the JS part :
$product.on('change', function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// serialize the entire form
var data = $form.serializeArray();
// Submit data via AJAX to the form's action path.
console.log(data);
$.ajax({
url : '/project/item/partial-edit', //custom route
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current position field ...
$('#item_productVersion').closest('.form-group').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#item_productVersion').closest('.form-group')
);
}
});
});
Here is the special route :
/**
* #Route("/item/partial-edit", name="edit_item_partial", requirements={"project"="\d+","item"="\d+"})
*/
public function edit_item_partial(Request $request)
{
$item = new Item();
$formOption = $request->get('option') ?? array(
'submit_btn_label' => 'update'
);
$form = $this->createForm(ItemType::class, $item, $formOption);
$form->handleRequest($request);
if ($form->isSubmitted()){
//if form was submitted, create a new form with the $item that has now a Product given
$newForm = $this->createForm(ItemType::class, $item, $formOption);
//here is the custom view only rendering the form
return $this->render('item/new_form-only.html.twig', [
'form' => $newForm->createView(),
]);
}
else {
return new Response('No form was submitted');
}
}

Changing exists rule based on choosen option

So i have this simple code to input value based on database value in registration form and it's work great,
The code below located at register.blade.php
This for selecting branch
<div class="form-group form-material floating">
<select class="form-control" name="branch" id="branch">
<option value="1">Option A</option>
<option value="2">Option B</option>
</select>
<label class="floating-label" for="inputStatus">Branch</label>
</div>
This one for inputing reference
<div class="form-group form-material floating {{ $errors->has('reference') ? ' has-error' : '' }}">
<input type="text" class="form-control empty" id="reference" name="reference">
<label class="floating-label" for="inputStatus">Reference</label>
#if ($errors->has('reference'))
<span class="help-block">
<strong>{{ $errors->first('reference') }}</strong>
</span>
#endif
</div>
the code below located atRegisterController.php
And this one for validation
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'phone' => 'required|string|min:5|max:20',
'rate' => 'required',
'product' => 'required',
'branch' => 'required',
'reference' => [
'required',
Rule::exists('references')->where(function ($query) {
if( 'branch' == 1 ){
$query->where('references.type', '=', "TOP");
}
else{
$query->where('references.type', '=', "BOTTOM");
}
}),
],
]
);
}
In registration form when user choose the first option on branch, the user can only write (Example : 'ABC', 'DEF', 'GHI') if the user write another value it will return Error Message : 'Wrong reference code', but when the user choose second option on branch, the user can only write (Example : '123', '456', '789'), , does anyone have and idea how to do it? Thanks
Edit 1 :
The code above works fine, but the exists rule for reference is not working, user still able to input 'ABC', 'DEF', or 'GHI' when choosing second option.
Edit 2 :
I also have this custom validation inside register.blade.php
fields: {
reference: {
validators: {
notEmpty: {
message: 'Reference must be filled'
},
stringLength: {
max: 8,
message: 'Max 8 character'
}
}
}
}
For this you need Custom Validation .Custom Validation . and about query, you can do that inside
public function passes($attribute, $value){
//value will have your reference value
//Logic here and return true/false
}
public function message()
{
//error message
//return "The :attribute value doesnt exist.";
}
and if you want to pass parameters in custom rule then you need to use constructor and use it . i.e
protected $customParams;
public function __construct($customParams)
{
$this->customParams = $customParams;
}
and rules would look like this
'reference' => ['required',new CustomRule('customvalue')]
Hope this helps.
You can make custom validation. So you can move this extra validation inside your custom validation. May be it can solve your problem.
Refer this for more on custom validation: Laravel custom validation
Hope it helps:)

Fetch Data and Populate in Text Boxes if Selected Dropdown Option

QUESTION UPDATED WITH CURRENT STATUS
I'm using Laravel 5.7 & VueJs 2.5.* ...
I want to autofill my form textboxes with the values in database when i select dropdown option. i've been finding solution since days but didn't get any success. I'm very new at this.
WHAT I WANT TO DO:
I have two invoices VendorInvoice and CustomerInvoice…
I created my VendorInvoice, fill all data and store in DB…
But when i want to create CustomerInvoice, i need to fetch & autofill the same data which i filled and stored in DB for VendorInvoice.
So when i create CustomerInvoice, I have a <select> options of VendorInvoice _no, by selecting any option CustomerInvoice form fields should auto fill with the VendorInvoice & VendorInvoiceItems data.
So i don’t have to fill same data by myself again in CustomerInvoice…
In my code:
VendorInvoice = ticketInvoice && VendorInvoiceItems = ticketInvoiceItems
CustomerInvoice = ctInvoice && CustomerInvoiceItems = ctInvoiceItems
If anyone could help to get me out from this issue i'll be very grateful. Thank You.
Here Is my HTML <select> & some ctInvoice & ctInvoiceItems fields which i want to autofill:
<form #submit.prevent="editmode ? updateCtInvoice() : createCtInvoice()">
<div class="modal-body">
<div class="row">
<!-- =====VENDOR INVOICE SELECTION===== -->
<select id="ticket_invoice_no" v-model="selectedTicketInvoiceId" #change="getRecord" name="ticket_invoice_no" type="text" class="form-control">
<option v-for="ticketInvoice in ticketInvoices" :key="ticketInvoice.id" :value="ticketInvoice.id">{{ ticketInvoice.ticket_invoice_no }}</option>
</select>
<!-- =====CUSTOMER TICKET INVOICE NUMBER===== -->
<input v-model="form.ct_invoice_no" type="text" name="ct_invoice_no" class="form-control">
<!-- =====CUSTOMER TICKET INVOICE ITEMS===== -->
<tbody>
<tr v-for="(ctInvoiceItem, key) in form.ctInvoiceItems" :key="key">
<!--Passenger Name-->
<td>
<input v-model="ctInvoiceItem.ct_passenger_name" size="40" type="text" name="ct_passenger_name" class="table-control form-control">
</td>
<!--Ticket No.-->
<td>
<input v-model="ctInvoiceItem.ct_ticket_no" size="24" type="text" name="ct_ticket_no" class="table-control form-control">
</td>
<!--Flight No.-->
<td>
<input v-model="ctInvoiceItem.ct_flight_no" size="7" type="text" name="ct_flight_no" class="table-control form-control">
</td>
</tbody>
My #change="getRecord" method:
getRecord: function(e) {
axios
.get("api/ticket-invoice/fetch/" + this.selectedTicketInvoiceId)
.then(({
data
}) => {
console.log(data);
this.form = data; // assumes the data keys maps directly to the form properties!!
})
.catch(error => {
console.log(error.response);
});
}
Route:
Route::get('ticket-invoice/fetch/{ticket_invoice}', 'API\TicketInvoiceController#fetch')->name('ticket-invoice.fetch');
My fetch(){} method:
public function fetch($id) {
$ticketInvoices = TicketInvoice::findOrFail($id);
return response() ->json([
'id' => '',
'customer_id' => '',
'ct_invoice_no' => $ticketInvoices->ticket_invoice_no,
'ct_invoice_date' => $ticketInvoices->ticket_invoice_date,
'ct_invoice_fares_total' => $ticketInvoices->ticket_invoice_fares_total,
'ct_invoice_grand_total' => $ticketInvoices->ticket_invoice_grand_total,
'ctInvoiceItems' => $ticketInvoices->ticketInvoiceItems->map(function($item) {
return [
// get the relevant $item->property for each key below
'id' => "",
'ct_invoice_id' => "",
'ct_passenger_name' => $item->passenger_name,
'ct_fares' => $item->fares,
'ct_sub_total' => $item->sub_total
];
}) ->all()
]);
}
My data() in Vue Component:
data() {
return {
editmode: true,
selectedTicketInvoiceId: false,
ticketInvoices: {},
ctInvoices: {},
customers: null,
form: new Form({
id: "",
customer_id: "",
ct_invoice_no: "",
ct_invoice_date: "",
ct_invoice_fares_total: 0,
ct_invoice_grand_total: 0,
ctInvoiceItems: [{
id: "",
ct_invoice_id: "",
ct_passenger_name: "",
ct_fares: 0,
ct_sub_total: 0
}]
})
};
},
When i select option i see in my Vue Component that specific id data fill in my form:. but its not actually fill my input fields with that data, so i could do some changes in the data and finally store it in DB as a customerInvoice.
Vue Dev Tool BEFORE SELECTING OPTION:
Vue Dev Tool AFTER SELECTING OPTION:
BUT NOT FILLING FIELDS:
I don't know Laravel 5.7 or vue but the concept remains the same
1- I would give an example of what I would do, I'll make a php file that has a select * from database and echo the result in json
2- Then use ajax, fetch to the php file and get that json, I will use the data retrieved in the javascript file
3 - I will have a function like on Dropdown Option onclick fetch or ajax, make the dropdown options equal to to the json fetched.

Yii2 Implement client side unique validation for input field

I've one field in my big form i.e.
<?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?>
Following is my ActiveForm options configuration:
<?php
$form = ActiveForm::begin([
//'id' => 'printerForm',
'enableClientValidation' => true,
'options' => [
'enctype' => 'multipart/form-data',
]
]);
?>
I want to implement client side unique validation for this. I'm using unique validator for it but its only working for server side validation.
public function rules() {
return [
[['name'], 'unique'],
]
...
other validations
...
};
Other validations working perfectly but unique client side validation is not working.
Finally I did it myself by enabling AJAX validation for a single input field and by using isAjax so that the server can handle the AJAX validation requests.
Following is the code:
In view:
<?= $form->field($model, 'name',['enableAjaxValidation' => true, 'validateOnChange' => false])->textInput(['maxlength' => 255]) ?>
And in controller:
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
$nm= $_POST['BusinessProcessProfile']['name'];
$result = Model::find()->select(['name'])->where(['name' => "$nm"])->one();
if ($result) {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return \yii\widgets\ActiveForm::validate($model, 'name');
} else {
return false;
}
}
It automatically calls validations rules defined in the Model.
For more info please refer : http://www.yiiframework.com/doc-2.0/guide-input-validation.html#client-side-validation

Zend 1.12 + Ajax - Submit form

After user clicks on submit form, I need to call Zend validation of that form without refreshing whole page. I also use zend_Layout in my website. I have seen a lot of tutorials here, but still cant make it working.
Index Controller:
class IndexController extends Zend_Controller_Action {
public function init() {
}
public function indexAction() {
$this->view->static_data = "eg. ABCDEFG";
$this->view->form = new Application_Form_Test();
}
public function ajaxAction() {
// probably some code to hande ajax
}
}
View for index/index:
...
<?php
echo date('m/d/Y h:i:s a', time());
echo $this->static_data;
?>
<hr />
<?php echo $this->form ?>
...
Form:
class Application_Form_Test extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$this->setAttrib('class', 'form1');
$this->addElement('text', 'email', array(
'label' => 'Your email address:',
'required' => true,
'filters' => array('StringTrim'),
'validators' => array(
'EmailAddress',
)
));
$this->addElement('text', 'name', array(
'label' => 'Your name:',
'required' => true,
'validators' => array(
array('validator' => 'StringLength', 'options' => array(3, 20))
)
));
// Add the submit button
$this->addElement('submit', 'submit', array(
'ignore' => true,
'label' => 'Send',
));
// And finally add some CSRF protection
$this->addElement('hash', 'csrf', array(
'ignore' => true,
));
}
}
So how can i validate form without refreshing rest of that page and see Zend Error Messages in case that form is not valid.
You can post the form to your Ajax action where you will instantiate the form and inject data from the request.
$form = new Form();
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost())) {
//save data
....
}
}
$this->view->form = $form;
You have two options:
Render the form in the view and respond with HTML. Using JavaScript replace current form with HTML returned by the Ajax request.
Get error messages using Zend_Form::getMessages() and respond with JSON.
$this-view->messages = $form->getMessages();

Categories