Zend 1.12 + Ajax - Submit form - javascript

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();

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');
}
}

How to submit multiple forms in laravel?

I have a simple form which contains a button to open another form in a pop up modal, the form looks like this
Now as you can see above prebids input plus button, when the user clicks the plus button it opens the modal which contains a form like this below.
Now I want to submit the forms in the following an order
First submit: base form (norma way)
Second submit: form inside a pop up (via ajax)
Here is my store function to submit the forms in a page controller
public function store(Request $request)
{
$page = Page::create([
'title' => $request->get('title'),
'articles' => $request->get('articles'),
'status' => $request->get('status'),
]);
// dd($request);
$page->save();
$bidder = Bidder::create('page_id' -> $page->id);
// save bidders informtion to the database using ajax
if($request->ajax())
{
$rules = array(
'params_name.*' => 'required',
'params_value.*' => 'required',
'bidders_name.*' => 'required',
);
$error = Validator::make($request->all(), $rules);
if($error->fails())
{
return response()->json([
'error' => $error->errors()->all()
]);
}
$params_name = $request->params_name;
$params_value =$request->params_value;
$bidders_name =$request->bidders_name;
for($count = 0; $count < count($params_name); $count++)
{
$data = array(
'params_name' => $params_name[$count],
'params_value' => $params_value[$count],
'bidders_name' => $bidders_name[$count],
);
$insert_data[] = $data;
}
bidder_parameters::insert($insert_data);
return response()->json([
'success' => 'Data Added successfully.'
]);
}
return redirect("/pages")->with("sucess", "data saved");
}
And here is ajax for submitting form inside a pop up
$("#paramsForms").on('submit', function(e) {
e.preventDefault();
$.ajax({
url: '/pages',
type: "POST",
data: $(this).serialize(),
dataType: 'json',
beforeSend:function() {
$("#save").attr('disabled', 'disabled');
},
success:function (data) {
console.log(data);
alert('Data successfull saved');
},
error:function (error) {
console.log(error)
console.log('Data not saved');
}
})
})
Now when I click submit I get the following error
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null (SQL: insert into `pages` (`title`, `articles`, `status`, `updated_at`, `created_at`) values (?, ?, ?, 2019-11-06 11:29:31, 2019-11-06 11:29:31))"
Note: checking dd($request) for both forms in a store function, I get the following
+request: ParameterBag {#44
#parameters: array:4 [
"_token" => "Wgozk9jnyUnJkL35vPhso9sUr7lbMD8cSgMVuN2s"
"bidders_name" => array:1 [
0 => "Biden"
]
"params_name" => array:1 [
0 => "democratic"
]
"params_value" => array:1 [
0 => "10"
]
]
}
Note: The problem is when I click submit on pop modal it try to send the base form at first
What do I need to change to get what I want?
Note: The problem is when I click submit on pop modal it try to send the base form at first
It means laravel is treating your both form as one and when u try to submit the pop up form it submited the base form. Please make sure you both form are separated.
There might possible that u have not close one of the form tag and the save changes button of pop up form is acting as the base form submit button.

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

Yii: Javascript & CSS not loading after Ajax calls

I have created a view with tabs and each tab has a form with ajax submit and gridview. I am using renderpartial in tabs widget to render the form and gridview and after clicking submit it filters the gridview. Everything looks fine within the tab till I click the submit button. After clicking submit it filters the gridview as expected.. but it does not load the bootstrap javascript and css so the layout is totally messed up and the tabs and menu bar all appears as a list and the page keeps on loading.
Anyone know why is it not loading the required scripts and css which I have preload in main config. Do I have to specify something seperately when calling ajax function from a view.
EDIT: Code Added
Code for tabs widget(producers.php)
<?php $this->widget(
'bootstrap.widgets.TbTabs',
array(
'type' => 'tabs',
'tabs' => array(
array('label' => 'Monthly' ,'id' => 'tab1',
'content' => $this->renderPartial('_prod_monthly',array('dataProvider' => $dataProvider,'dataProvider2' => $dataProvider2 ,'dataProvider3' => $dataProvider3, 'opm' => $opm, 'month' => $month),true,true),
'active' => true),
array('label' => 'Weekly' ,'id' => 'tab2',
'content' => $this->renderPartial('_prod_weekly',array('dataProvider4' => $dataProvider4,'dataProvider5' => $dataProvider5 ,'dataProvider3' => $dataProvider3, 'opm' => $opm, 'week' => $week),true,true)),
array('label' => 'Daily', 'id' => 'tab3', 'content' => 'ABC' )),
)); ?>
Code for partial view (_prod_monthly.php):
$form = $this->beginWidget(
'bootstrap.widgets.TbActiveForm',array(
'id' => 'form_monthly',
'enableAjaxValidation'=>true,
'type' => 'inline',
'method' => 'get',
'htmlOptions' => array('class' => 'well','onsubmit'=>"return false;",/* Disable normal form submit */
'onkeypress'=>" if(event.keyCode == 13){ send2(); } " /* Do ajax call when user presses enter key */),)); ?>
Select Month:
<?php echo CHtml::dropDownList('month', $month,
$sel_month); ?>
Select Employee:
<?php echo CHtml::dropDownList('opm', $opm,
$sel_opm); ?>
<?php $this->widget('bootstrap.widgets.TbButton',
array(
'buttonType'=>'button',
'label'=>'Submit',
'type' =>'primary',
'htmlOptions'=> array('onclick' => 'send2()'),)
);
$this->endWidget();
$this->widget('bootstrap.widgets.TbExtendedGridView', array(
'sortableRows'=>true,
'afterSortableUpdate' => 'js:function(id, position){ console.log("id: "+id+", position:"+position);}',
'type'=>'striped bordered hover',
'dataProvider'=>$dataProvider,
'type'=>'striped bordered responsive',
'template' => "{summary}\n{items}\n{extendedSummary}\n{exportButtons}"));?>
<script type="text/javascript">
function send2()
{
var data=$("#form_monthly").serialize();
$.ajax({
type: 'GET',
url: '<?php echo Yii::app()->createAbsoluteUrl("op/producers"); ?>',
data:data,
success:function(data){
document.write(data);
},
error: function(data) { // if error occured
alert("Error occured.please try again");
alert(data);
},
dataType:'html'});
}
</script>
Thanks.
There is no Yii based solution for handling UI-Widgets with AJAX right now and will never come.
Probably, this will never happen again in Yii2. The main Problem is about your missing JS-Ressources and Binding (as you know already). Yii will generate those codes (which you are missing) within the layout process, which is not called on "AJAX"-Requests. Don't try to put in your JS-Ressources by yourself. Yii generated JS-Cods are realy tricky ... and not that easy to handle manualy. This will come up with a lot of problems.
My solution: I dont use any of the "UI"-Features included in Yii. They come along with a lot of problems (Ajax, Multiple announces, etc.). Its not hard to write that features yourself or using jQueryUI.
Take a look at the Tab feature and try write it yourself .. and be happy ! :)

CGridView AjaxRequests brokes javascript custom CFormatter

I'm having problems with CGridView and one custom Formatter that uses javascript. When CGridView triggers an ajax request of any kind, my Formatter that works with javascript stops working.
Let's try one trivial example:
The Formatter
class DFormatter extends CFormatter {
public function formatTest(){
$js = <<<EOD
console.log("test");
EOD;
$cs = Yii::app()->getClientScript();
$cs->registerScript("testjs", $js);
return false;
}
}
The view:
<?php $this->widget('zii.widgets.grid.CGridView',
array(
'id' => 'development-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'id',
array(
'name' => 'testField',
'type' => 'test',
),
array(
'class' => 'CButtonColumn',
),
),
)); ?>
After the first Ajax Request, the javascript code used in the formatter stops working, How can I get my javascript code works after every ajax call made by CGridView Widget?
Thanks.
You should not put scripts in the formatter.
Remove:
$js = <<<EOD
console.log("test");
EOD;
$cs = Yii::app()->getClientScript();
$cs->registerScript("testjs", $js);
Configure your grid view:
<?php $this->widget('zii.widgets.grid.CGridView', array(
//.. other options
// ADD THIS
'afterAjaxUpdate' => 'function() { afterAjaxUpdate(); }',
Add to <head> of /layouts/main.php:
<script src="<?= Yii::app()->getBaseUrl(true); ?>/js/myCustomJS.js"></script>
Create new JS file at /js/myCustomJS.js:
function afterAjaxUpdate() {
// do your formatting here
}

Categories