Zend_Form with Ajax/json - zend-framework

I'm a bit lost using Zend_Form with Ajax. I have a form in a class extending Zend_Form called from my controller, that way :
GoodAddGroup.php
class Default_Form_GoodAddGroup extends Zend_Form {
(...)
public function init()
{
$this->setMethod('post');
$this->setAction("process-add-group");
$this->setName("addgroupgood");
// Load Elements class
require "Form/Elements.php";
$magElements = new Elements();
// Category
$categoryElement = $magElements->getCategorySelectField();
$categoryElement->setDecorators($this->elementDecorators);
// Barcode
$barcodeElement = $magElements->getGoodBarcodeTextField();
$barcodeElement->setDecorators($this->elementDecorators);
(...)
// Add elements to the form
$this->addElements(array(
$categoryElement,
//$codeElement,
$barcodeElement,
$serialElement,
$warehouseElement,
$submitButtonElement
));
$this->setDecorators($this->formDecorators);
}
}
In GoodsController.php
private function getAddGroupForm()
{
return new Default_Form_GoodAddGroup();
}
public function addGroupAction()
{
// Initialize the form for the view.
$this->view->form = $this->getAddGroupForm();
}
public function processAddGroupAction()
{
$form = $this->getAddGroupForm();
(...)
if ($_POST)
{
if ($form->isValid($_POST))
{
// Do things
} else {
$this->view->form = $form;
}
}
}
Basically, the form has a category select field, when selecting a category, a second "code" selector is added filled with the items related to this category. When the page with the form is displayed (http://myapp/goods/add-group), everything works fine, the ajax call does its job, the second select field is added and well fed, but as you can see, the form processing is done with the processAddGroupAction(), this method get the instance of the form to get its values and to re-display it in case of problem. But that way, my "new" select field doesn't exist anymore, so i can never validate the form.
It's my first attempt using ajax/json with Zend, i think i need help at this poind.
Thank you.
EDIT : added the view code as requested
<script>
$(function(){
$("#cats").change(function(){
getSelectBox(this);
});
$("#code").parent().parent().css('display', 'none');
getSelectBox($("#cats"));
});
function getSelectBox(element)
{
if($(element).val() != '')
{
$("#code").parent().parent().css('display', 'block');
if ($('#code').length <= 0) {
$("#cats").after('<select name="code" id="code" style="margin-left:10px"></select>');
}
$.getJSON("/goods/json-get-codes-from-category", {id: $(element).val(), ajax: "true"}, function(j){
console.log(j);
var options = "";
jQuery.each(j, function(i, val) {
options += '<option value="' + i + '">' + i + val + '</option>';
});
$("#code").html(options);
});
}
}
</script>
<?php echo $this->form; ?>

You can add the select element 'code' in the form, but don't display it in the view (it will be created from the js). So when the form is posted the 'code' will also be validated since it is in the $_POST.
After post you have to display the select box without $("#cats").change({..}). You can accomplish it by spiting the js code into functions
<script>
$(function(){
$("#cats").change(function(){
getSelectBox(this);
});
getSelectBox($("#cats"));
});
function getSelectBox(element)
{
if($(element).val() != '')
{
if ($('#code').length <= 0) {
$("#cats").after('<select name="code" id="code" style="margin-left:10px"></select>');
}
$.getJSON("/goods/json-get-codes-from-category", {id: $(element).val(), ajax: "true"}, function(j){
console.log(j);
var options = "";
jQuery.each(j, function(i, val) {
options += '<option value="' + i + '">' + i + val + '</option>';
});
$("#code").html(options);
});
}
}
</script>
Hope this helps

Ok, i found the solution! I post it in case it can be useful for other people.
I knew what was the problem, but unable to know how to achieve it.
At form processing time, the new select element does not exist for the action as it has been added in the view with javascript. To fix this, we need to inform the action about the new field, to do so, we first need to override the method “isValid” of Zend_Form in the form class :
public function isValid($values)
{
$values = $this->_modifyElements($values);
return parent::isValid($values);
}
Then create a method "_modifyElements" that will modify the form by adding the new element :
protected function _modifyElements($values)
{
// Search for codes
$dbu = new DbUtils();
$codes = $dbu->getCodesFromCategory($values['cats']);
// Remove the current code element
$this->removeElement('code');
// Create a new element
$codeElement = new Zend_Form_Element_Select('code', array());
$codeElement->setLabel(_('Code :'));
$codeElement->setRequired(true);
$codeElement->addMultiOptions($codes);
$codeElement->setValue($values['code']);
$codeElement->setDecorators($this->elementDecorators);
$this->addElement($codeElement);
return $values;
}
We have to override the method "populate" too :
public function populate(array $values)
{
$values = $this->_modifyElements($values);
return parent::populate($values);
}
And voilà. It works for me ;>
All the credits about this go to Mohamed : http://jamandcheese-on-phptoast.com/2009/12/13/on-fly-elements-in-zend_form/

Related

List.JS - Filter with comma separated values

I'm trying to create a dropdown to filter based on dates, but I would like to be able to comma separate the dates instead of initilizing each field as a separate filter, which would be slow when you account for hundreds of dates, and each list item having 30+ dates each.
I thought maybe list.js supports comma separated fields, but I can't find any obvious solution online.
Here is a simple codepen with the code working with a single field:
https://codepen.io/mauricekindermann/pen/QWyqzQL
This: <span class="stat filter_dates">1999,2099,2199</span>
Instead of this: <span class="stat filter_dates">1999</span>
Is this possible? Or do I need to initiate each date as a separate filter?
I didn't get any answers and couldn't find an obvious soultion to this. So the final result uses this method (JS script inside a PHP file)
<script>
var options = {
valueNames: [
'id',
<?
while($item= mysqli_fetch_array($query))
{
?>
'filter_<?=$item['id']?>',
<?
}
?>
]
};
$(document).ready(function()
{
if($('#filter').length > 0)
{
$('#filter').change(function ()
{
var selection = this.value;
<?
$i=0;
mysqli_data_seek($query, 0);
while($item= mysqli_fetch_array($query))
{
if($i==0){
$type='if';
} else {
$type='else if';
}
?>
<?=$type?>(selection == '<?=$item['id']?>')
{
userList.filter(function (item) {
return (item.values().<?='filter_'.$item['id']?> == selection);
});
}
<?
$i++;
}
if($i > 0){
?>
else {
userList.filter();
}
<?
}
?>
});
};
});
</script>

Using ECT template with Chaplin

Hello I want to use ect templates with the Chaplin js. I am not able to integrate the ect template the way Handlebar is integrated as per Chaplin boiler plate.Please help thanks.
I found the way for this you just need to override the render function as per the syntax of the ect template in your application base view class.
I did it in this way
render : function() {
var $templateHtml, html, templateFunc;
if (this.disposed) {
return false;
}
var data = { message : 'Hello, World!' };
html = ECT({ root : 'js/templates' }).render(this.templateName,data)
if (!this.noWrap) {
this.$el.html(html);
}
else {
$templateHtml = $(html);
if ($templateHtml.length > 1) {
throw new Error('There must be a single top-level element when ' + 'using `noWrap`.');
}
this.undelegateEvents();
this.setElement($templateHtml, true);
}
return this;
}

Need Help with Zend Form dropdown menu validation

I'm working on a zend framework project and I needed the user to select school and then it goes to the next form then select the grade.
Eg User select ABC High School and then select "Grade 8"
Both School and Grade dropdown menu is soft coded fetching the data from the database.
My problem is that when the user selected a school and then on the next grade form if they don't select any values and click onto "submit" it return a validation error "Value is required and can't be empty" which is correct but the dropdown menu then goes empty.
I wanted to know how to repopulate the values back to the grade dropdown menu if the form doesn't validate.
Thanks so much
Here is my code
Here is the function i generate the grade values (Fetching from the database)
public function processSchoolSelectionAction()
{
$form = $this->getSchoolSelectionForm();
if ($form->isValid($_POST))
{
// getting the values
$schoolId = $form->getValue('school');
$schoolYear = new Application_Model_DbTable_SchoolYear();
$schoolYearValues = $schoolYear->getYearValues($schoolId);
array_unshift($schoolYearValues, array ('key' =>'' , 'value' =>'Please Specify'));
$form = $this->getYearSelectionForm();
$form->year->addMultiOptions($schoolYearValues);
$form->schoolId->setValue($schoolId);
$this->view->form = $form;
}
else
{
$data = $form->getValues();
$form->populate($data);
$this->view->form = $form;
}
}
Code processing the year selection form
public function processYearSelectionAction()
{
$form = $this->getYearSelectionForm();
if ($form->isValid($_POST))
{
// getting the values
$schoolId = $form->getValue('schoolId');
$yearId = $form->getValue('year');
$textbookList = new Application_Model_DbTable_TextbookList();
if ($textbookList->checkTextbookExist($schoolId, $yearId))
{ // check if textbookExist
}
else
{
$this->view->message = "Sorry, But the list you requested is currently not available for ordering online.";
}
}
else
{
$data = $form->getValues();
$form->populate($data);
$this->view->form = $form;
}
}
School selection form
<?php
class Application_Form_SchoolSelection extends ZendX_JQuery_Form
{
public function init()
{
$this->setName('schoolSelection');
$school = new Application_Model_DbTable_School;
$schoolValues = $school->getSchoolValues();
array_unshift($schoolValues, array ('key' =>'' , 'value' =>'Please Specify'));
$schoolElement = new Zend_Form_Element_Select('school');
$schoolElement->addMultiOptions($schoolValues);
$schoolElement->setLabel('School');
$schoolElement->setRequired(true);
$schoolElement->setRegisterInArrayValidator(false);
$submitElement = new Zend_Form_Element_Submit('submit');
$submitElement->setLabel("Next");
$this->addElements(array(
$schoolElement,
$submitElement
));
}
}
?>
Grade (Year) selection form
<?php
class Application_Form_YearSelection extends ZendX_JQuery_Form
{
public function init()
{
$this->setName('yearSelection');
$yearElement = new Zend_Form_Element_Select('year');
$yearElement->setLabel('Year');
$yearElement->setRequired(true);
$yearElement->setRegisterInArrayValidator(false);
$schoolIdElement = new Zend_Form_Element_Hidden('schoolId');
$submitElement = new Zend_Form_Element_Submit('submit');
$submitElement->setLabel("Next");
$this->addElements(array(
$yearElement,
$schoolIdElement,
$submitElement
));
}
}
?>
This is how I done that:
In Controller when form is creating, pass data from request:
$some_selected_data = $this->_getParam('param_from_request'); // you need to validate this
$form = new Application_Form_SchoolSelection( array('some_data' => $some_selected_data) );
Then, in Form Class get that value like this:
$data = $this->getAttrib('some_data'); // the key value of array above
and just ask
if($data) {
// get value from DB and
//SET VALUE TO Zend_Form_Element
}
Obviously, you need to repopulate the options of your Select field.
In your processYearSelectionAction, on the validation failure part just grab the schoolId you stored in the hidden field and use it the same way as you did in your processSchoolSelectionAction to populate your field options.

using jQuery selector with a dom element

I have a javascript function that receives a dom element as a parameter. In this function I am trying to get to the closest ancestor 'form'. I wanted to use:
function submit_form(obj)
{
var form1 = $(obj).closest("form");
alert (form1.id);
}
here obj is a dom element of submit type. I just don't seem to get it working.
Could someone please help?
You want
function submit_form(obj)
{
var form1 = $(obj).closest("form")[0];
alert (form1.id);
}
The result of jQuery selection/traversal functions always is an array, possibly with one element only, but still an array. Index into the array to get actual DOM elements.
However, you can do this without jQuery as long as obj is an input/select/textarea element.
function submit_form(obj)
{
var form1 = obj.form;
alert (form1.id);
}
For the sake of completeness, you could also stay within jQuery and do
function submit_form(obj)
{
var $form1 = $(obj).closest("form");
alert ( $form1.attr("id") );
}
function closest(obj, tagName) {
// Go up in the tree until you find the ancestor form
while (obj.parent !== null) {
if (obj.nodeType === 1) {
parent = obj.parentNode;
if (parent.tagName === tagName) {
return parent;
}
}
}
// If no form exists return null
if (obj.tagName !== tagName) {
return null;
}
}
Use it in this way
var closestForm = closest(obj, 'FORM')
form1 would be a jQuery object, so you can either use .attr("id"), or access the DOM element itself using [0].id:
var form1 = $(obj).closest("form");
alert(form1.attr("id"));
var form2 = $(obj).closest("form")[0];
alert(form2.id);

Symfony: exclude empty values from form save

I have a many to many relation between Product and Properties. I'm using embedRelation() in my Product form to edit a Product and it's Properties. Properties includes images which causes my issue. Every time I save the form the updated_at column is updated for file properties even when no file is uploaded.
Therefore, I want to exclude empty properties when saving my form.
I'm using Symfony 1.4 and Doctrine 1.2.
I'm thinking something like this in my ProductForm.class.php, but I need some input on how to make this work.
Thanks
class ProductForm extends BaseProductForm
{
public function configure()
{
unset($this['created_at'], $this['updated_at'], $this['id'], $this['slug']);
$this->embedRelation('ProductProperties');
}
public function saveEmbeddedForms($con = null, $forms = null)
{
if (null === $forms)
{
$properties = $this->getValue('ProductProperties');
$forms = $this->embeddedForms;
foreach($properties as $p)
{
// If property value is empty, unset from $forms['ProductProperties']
}
}
}
}
I ended up avoiding Symfony's forms and saving models instead of saving forms. It can be easier when playing with embedded forms. http://arialdomartini.wordpress.com/2011/04/01/how-to-kill-symfony%E2%80%99s-forms-and-live-well/
Solved it by checking if posted value is a file, and if both filename and value_delete is null I unset from the array. It might not be best practice, but it works for now.
Solution based on http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms
class ProductPropertyValidatorSchema extends sfValidatorSchema
{
protected function configure($options = array(), $messages = array())
{
// N0thing to configure
}
protected function doClean($values)
{
$errorSchema = new sfValidatorErrorSchema($this);
foreach($values as $key => $value)
{
$errorSchemaLocal = new sfValidatorErrorSchema($this);
if(array_key_exists('value_delete', $values))
{
if(!$value && !$values['value_delete'])
{
unset($values[$key]);
}
}
// Some error for this embedded-form
if (count($errorSchemaLocal))
{
$errorSchema->addError($errorSchemaLocal, (string) $key);
}
}
// Throws the error for the main form
if (count($errorSchema))
{
throw new sfValidatorErrorSchema($this, $errorSchema);
}
return $values;
}
}