Use zend-decorator to format Zend_Form_Element_Radio in a table column with oher Zend_Form_Elements in rows - zend-framework

I want use decorators to format as table the following Zend_Form, placing a description in the first column and the Zend_Form_Element_Radio's options in second column and add 2 select in every row as you can see in the html example later.
I need a concrete/working example.
FORM
class My_Form extends Zend_Form
{
const KIND_1 = 'dineer1';
const KIND_2 = 'dineer2';
const KIND_3 = 'dineer3';
const KIND_4 = 'dineer4';
const KIND_5 = 'dineer5';
const KIND_6 = 'dineer6';
public static $KINDS = array(
1 => self::KIND_1,
2 => self::KIND_2,
3 => self::KIND_3,
4 => self::KIND_4,
5 => self::KIND_5,
6 => self::KIND_6,
);
const DRINK_C = 'c';
const DRINK_M = 'm';
const DRINK_W = 'w';
public static $DRINKS = array(
self::DRINK_C => "cole",
self::DRINK_M => "milk",
self::DRINK_W => "water",
);
const FOOD_B = 'b';
const FOOD_F = 'f';
const FOOD_M = 'm';
const FOOD_P = 'p';
const FOOD_V = 'v';
const FOOD_W = 'w';
public static $FOODS = array(
self::FOOD_B => "burger",
self::FOOD_F => "fruit",
self::FOOD_M => "Meat",
self::FOOD_P => "pizza",
self::FOOD_V => "vegetables",
self::FOOD_W => "Wursterl",
);
public function init()
{
$_please_select = array("" => " please select ");
$this->setMethod(Zend_Form::METHOD_POST);
$input_lunch = new Zend_Form_Element_Radio('lunch');
$input_lunch ->setMultiOptions(self::$KINDS) ;
$this->addElement($input_lunch );
foreach (self::$KINDS as $k => $_descriprion) {
$input_drink = new Zend_Form_Element_Select('drink_' . $k);
$input_drink->addMultiOptions(self::$DRINKS);
$input_food = new Zend_Form_Element_Select('food_' . $k);
$input_food->addMultiOptions($_please_select)
->addMultiOptions(self::$FOODS);
$this->addElement($input_drink);
$this->addElement($input_food);
}
}
}
expected HTML
<html>
<body>
<form action="/" method="POST">
<table>
<thead>
<tr>
<th></td>
<th>kind</td>
<th>drink</td>
<th>food</td>
</tr>
</thead>
<tbody>
<tr>
<td>Description row 1</td>
<td><input type="radio" name="lunch" value "dinner1"></td>
<td>
<select name="drink_1">
<option value="w">Water</option>
<option value="m">Milk</option>
<option value="b">Beer</option>
</select>
</td>
<td>
<select name="food_1">
<option value="">please select</option>
<option value="b">Burger</option>
<option value="f">Fruit</option>
<option value="m">Meat</option>
<option value="p">Pizza</option>
<option value="v">Vegetable</option>
<option value="w">Wurstel</option>
</select>
</td>
</tr>
<tr>
<td>Description row 2</td>
<td><input type="radio" name="lunch" value "dinner2"></td>
<td>
<select name="drink_2">
<option value="w">Water</option>
<option value="m">Milk</option>
<option value="b">Beer</option>
</select>
</td>
<td>
<select name="food_2">
<option value="">please select</option>
<option value="b">Burger</option>
<option value="f">Fruit</option>
<option value="m">Meat</option>
<option value="p">Pizza</option>
<option value="v">Vegetable</option>
<option value="w">Wurstel</option>
</select>
</td>
</tr>
<tr>
<td>Description row 3</td>
<td><input type="radio" name="lunch" value "dinner3"></td>
<td>
<select name="drink_3">
<option value="w">Water</option>
<option value="m">Milk</option>
<option value="b">Beer</option>
</select>
</td>
<td>
<select name="food_3">
<option value="">please select</option>
<option value="b">Burger</option>
<option value="f">Fruit</option>
<option value="m">Meat</option>
<option value="p">Pizza</option>
<option value="v">Vegetable</option>
<option value="w">Wurstel</option>
</select>
</td>
</tr>
<tr>
<td>Description row 4</td>
<td><input type="radio" name="lunch" value "dinner4"></td>
<td>
<select name="drink_4">
<option value="w">Water</option>
<option value="m">Milk</option>
<option value="b">Beer</option>
</select>
</td>
<td>
<select name="food_4">
<option value="">please select</option>
<option value="b">Burger</option>
<option value="f">Fruit</option>
<option value="m">Meat</option>
<option value="p">Pizza</option>
<option value="v">Vegetable</option>
<option value="w">Wurstel</option>
</select>
</td>
</tr>
<tr>
<td>Description row 5</td>
<td><input type="radio" name="lunch" value "dinner5"></td>
<td>
<select name="drink_5">
<option value="w">Water</option>
<option value="m">Milk</option>
<option value="b">Beer</option>
</select>
</td>
<td>
<select name="food_5">
<option value="">please select</option>
<option value="b">Burger</option>
<option value="f">Fruit</option>
<option value="m">Meat</option>
<option value="p">Pizza</option>
<option value="v">Vegetable</option>
<option value="w">Wurstel</option>
</select>
</td>
</tr>
<tr>
<td>Description row 6</td>
<td><input type="radio" name="lunch" value "dinner6"></td>
<td>
<select name="drink_6">
<option value="w">Water</option>
<option value="m">Milk</option>
<option value="b">Beer</option>
</select>
</td>
<td>
<select name="food_6">
<option value="">please select</option>
<option value="b">Burger</option>
<option value="f">Fruit</option>
<option value="m">Meat</option>
<option value="p">Pizza</option>
<option value="v">Vegetable</option>
<option value="w">Wurstel</option>
</select>
</td>
</tr>
</tbody>
<table>
</form>
</body>
</html>

I would suggest a solution based on this answer here https://stackoverflow.com/a/8451723/212940
Your form:-
class My_Form extends Zend_Form
{
const KIND_1 = 'dineer1';
const KIND_2 = 'dineer2';
const KIND_3 = 'dineer3';
const KIND_4 = 'dineer4';
const KIND_5 = 'dineer5';
const KIND_6 = 'dineer6';
public static $KINDS = array(
1 => self::KIND_1,
2 => self::KIND_2,
3 => self::KIND_3,
4 => self::KIND_4,
5 => self::KIND_5,
6 => self::KIND_6,
);
const DRINK_C = 'c';
const DRINK_M = 'm';
const DRINK_W = 'w';
public static $DRINKS = array(
self::DRINK_C => "cole",
self::DRINK_M => "milk",
self::DRINK_W => "water",
);
const FOOD_B = 'b';
const FOOD_F = 'f';
const FOOD_M = 'm';
const FOOD_P = 'p';
const FOOD_V = 'v';
const FOOD_W = 'w';
public static $FOODS = array(
self::FOOD_B => "burger",
self::FOOD_F => "fruit",
self::FOOD_M => "Meat",
self::FOOD_P => "pizza",
self::FOOD_V => "vegetables",
self::FOOD_W => "Wursterl",
);
public function init()
{
$this->addDecorators(
array(
array('ViewScript', array('viewScript' => 'forms/_form_test.phtml'))
)
); //added as part of answer. Note all default decorators are still available.
$_please_select = array("" => " please select ");
$this->setMethod(Zend_Form::METHOD_POST);
$input_lunch = new Zend_Form_Element_Radio('lunch');
$input_lunch ->setMultiOptions(self::$KINDS) ;
$this->addElement($input_lunch );
foreach (self::$KINDS as $k => $_descriprion) {
$input_drink = new Zend_Form_Element_Select('drink_' . $k);
$input_drink->addMultiOptions(self::$DRINKS);
$input_food = new Zend_Form_Element_Select('food_' . $k);
$input_food->addMultiOptions($_please_select)
->addMultiOptions(self::$FOODS);
$this->addElement($input_drink);
$this->addElement($input_food);
}
$this->setElementDecorators(array('ViewHelper'));//added as part of answer
}
}
As you can see it requires only two small changes.
Then you need to create the file scripts/forms/_form_test.phtml which contains:-
<form
id='contact'
action='<?php echo $this->element->getAction(); ?>'
method='<?php echo $this->element->getMethod(); ?>'
>
<table>
<thead>
<tr>
<th></td>
<th>kind</td>
<th>drink</td>
<th>food</td>
</tr>
</thead>
<tbody>
<?php
$elements = $this->element->getElements();
$options = $this->element->lunch->getMultiOptions();
foreach($options as $key => $option){
echo "<tr>\n";
echo "<td>Description row $key</td>\n";
echo "<td><input type='radio' name='lunch' value='$option'</td>\n";
echo "<td>{$elements['drink_' . $key]}</td>\n";
echo "<td>{$elements['food_' . $key]}</td>\n";
echo "</tr>\n";
}
?>
</tbody>
<table>
</form>
The file _form_test.phtml is effectively your decorator for rendering the form. This is a very flexible way of using Zend_Form and I learnt how to do this by reading this article here
The default decorators, such as 'error' should still be available with this method.
On my system I got the exact html output you asked for. Try it and see.

This might not be the prefect solution but it might help you!
class My_Form extends Zend_Form
{
const KIND_1 = 'dineer1';
const KIND_2 = 'dineer2';
const KIND_3 = 'dineer3';
const KIND_4 = 'dineer4';
const KIND_5 = 'dineer5';
const KIND_6 = 'dineer6';
public static $KINDS = array(
1 => self::KIND_1,
2 => self::KIND_2,
3 => self::KIND_3,
4 => self::KIND_4,
5 => self::KIND_5,
6 => self::KIND_6,
);
const DRINK_C = 'c';
const DRINK_M = 'm';
const DRINK_W = 'w';
public static $DRINKS = array(
self::DRINK_C => "cole",
self::DRINK_M => "milk",
self::DRINK_W => "water",
);
const FOOD_B = 'b';
const FOOD_F = 'f';
const FOOD_M = 'm';
const FOOD_P = 'p';
const FOOD_V = 'v';
const FOOD_W = 'w';
public static $FOODS = array(
self::FOOD_B => "burger",
self::FOOD_F => "fruit",
self::FOOD_M => "Meat",
self::FOOD_P => "pizza",
self::FOOD_V => "vegetables",
self::FOOD_W => "Wursterl",
);
public function init()
{
$_please_select = array("" => " please select ");
$this->setMethod(Zend_Form::METHOD_POST);
$this->setDisableLoadDefaultDecorators(true);
$countRows = count(self::$KINDS)+1;
foreach (self::$KINDS as $k => $_descriprion) {
$rowForm = new Zend_Form_SubForm();
$input_lunch = new Zend_Form_Element_Radio('lunch',
array('disableLoadDefaultDecorators' => true,
'label' => 'kind',
'label_placement' => 'prepend'));
$input_lunch ->setMultiOptions(self::$KINDS) ;
$input_lunch->addDecorators(array(
array('ViewHelper'),
array('Label',
array('style' => 'display:block;font-weight:bold', // just for this example
'placement'=>'PREPEND',
'tag'=>'span', //if you want to use other tag
'disableFor'=>true)
),
array(array('data' => 'HtmlTag'), array('tag' => 'td', 'rowspan' => $countRows )),
));
$this->addElement($input_lunch);
// add label just for the first element
$drinkLabel = array('disableLoadDefaultDecorators' =>true);
$drinkDecorators = array(
array('ViewHelper'),
array(array('data' => 'HtmlTag'), array('tag' => 'td')),
);
if ($k == 1) {
$drinkLabel['label'] = 'drink';
$drinkDecorators = array(
array('ViewHelper'),
array('Label',
array('style' => 'display:block;font-weight:bold',
'placement'=>'PREPEND',
'tag'=>'span', //if you want to use other tag
'disableFor'=>true)
),
array(array('data' => 'HtmlTag'), array('tag' => 'td')),
);
}
$input_drink = new Zend_Form_Element_Select('drink', $drinkLabel);
$input_drink->addMultiOptions(self::$DRINKS);
$input_drink->addDecorators($drinkDecorators);
$input_food = new Zend_Form_Element_Select('food', $drinkLabel);
$input_food->addMultiOptions($_please_select)
->addMultiOptions(self::$FOODS);
$input_food->addDecorators($drinkDecorators);
$rowForm->addElement($input_drink);
$rowForm->addElement($input_food);
$rowForm->setSubFormDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'tr')),
));
$rowForm->setDisableLoadDefaultDecorators(true);
$this->addSubForm($rowForm, 'row_' . $k);
}
$this->setSubFormDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'tr')),
));
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'table')),
'Form'
));
}
}
Also, here are some resources to work with table and Zend_Form
http://www.packtpub.com/article/create-shopping-cart-using-zend-framework-2
http://davidcaylor.com/2008/03/24/building-table-based-forms-in-zend_form/
http://blog.kosev.net/2010/06/tutorial-create-zend-framework-form.html

you do not can to do what you want accomplish with a view helper, in my view!
you should make a custom form printing, such as:
<form action="/" method="POST">
<table>
<thead>
<tr>
<th></th>
<th>kind</th>
<th>drink</th>
<th>food</th>
</tr>
</thead>
<tbody>
<?for ($i = 1; $i <= 6; $i++) : ?>
<?
$drink = 'drink_' . $i;
$food = 'food_' . $i;
?>
<tr>
<td>Description row <?=$i?></td>
<td>
<?=$this->form->$drink?>
</td>
<td>
<?=$this->form->$food?>
</td>
</tr>
<? endfor;?>
</tbody>
</table>
</form>
lunch for the field, it is impossible to use it as you ask, you should think of another strategy. At the moment I can think of!

Well here my version of the answer.
Into the init I've add a custom decorator RadioTable to the form, and prefix path to load the decorator
then I decorate all the elements as a normal table, this's important because all my efforts are to keep the facilities offer by standard decorators like "Error"
Please note the Custom_Form_Element_FirstSelect and the ->setSeparator in the radio element
I also add the submit button
public function init()
{
$_please_select = array("" => " please select ");
// I add a my custom decorator
$this->addPrefixPath('Custom_Form_Decorator',
'Custom/Form/Decorator',
'decorator');
$this->setMethod(Zend_Form::METHOD_POST);
$input_lunch = new Zend_Form_Element_Radio('lunch');
$input_lunch ->setMultiOptions(self::$KINDS)
->setSeparator("\t__RADIO_SEPARATOR__\t") //set custom separator I'll use in custom decorator
->setDecorators(array(
'ViewHelper', // add a standard decorator
'Label', // I'll use the label decorator to show text
));
$this->addElement($input_lunch );
foreach (self::$KINDS as $k => $_description) {
// to "mark" the first select I extend the Zend_Form_Element_Select
// with Custom_Form_Element_FirstSelect
$input_drink = new Custom_Form_Element_FirstSelect('drink_' . $k);
$input_drink->addMultiOptions(self::$DRINKS)
->setDecorators(array(
'ViewHelper', // add a standard decorator
array(array('data' => 'HtmlTag'),array('tag' => 'td')), // add a standard decorator
));
$input_food = new Zend_Form_Element_Select('food_' . $k);
$input_food->addMultiOptions($_please_select)
->addMultiOptions(self::$FOODS)
->setDecorators(array(
'ViewHelper',
array(array('data' => 'HtmlTag'),array('tag' => 'td')),
));
$this->addElement($input_drink);
$this->addElement($input_food);
}
// add a the submit button
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('SUBMIT');
$submit->setDecorators(array(
'ViewHelper',
'Errors',
array(array('data' => 'HtmlTag'), array('tag' => 'td', )),
array(array('row' => 'HtmlTag'), array('tag' => 'tr')),
));
$this->addElement($submit);
// add the custom decorators to the whole form
$this->setDecorators(array(
'RadioTable',
array('HtmlTag', array('tag' => 'table', )),
'Form'
));
}
the Custom_Form_Element_FirstSelect
class Custom_Form_Element_FirstSelect extends Zend_Form_Element_Select{
}
the Custom_Form_Decorator_RadioTable
class Custom_Form_Decorator_RadioTable extends Zend_Form_Decorator_Abstract {
public function render($content){
$wrap = '';
// I'll take the element
$radioElement = $this->getElement()->getElement('lunch');
// then I render it and explode in array using the separator
$arrayRadio = explode("\t__RADIO_SEPARATOR__\t", $radioElement->__toString());
$count = 0;
$arrayElement = $this->getElement()->getElements();
// loop on all form elements and I render them
foreach ($arrayElement as $keyForm => $element){
// I skip the radio element
if($element instanceof Zend_Form_Element_Radio){
continue;
}
if($element instanceof Custom_Form_Element_FirstSelect ){
// when I found the custom select element I'll prefix with "open-row-tag" and radio button in column
$wrap .= "<tr>". "<td>". $arrayRadio[$count++] . "</td>" . $element->__toString();
// note that the select elements are already decorated
} else if($element instanceof Zend_Form_Element_Select){
// after the last select I close the row
$wrap .= $element->__toString() ."</tr>";
}
if($element instanceof Zend_Form_Element_Submit){
// add the SUBMIT button
$wrap .= $element->__toString() ;
}
}
return $content.$wrap;
}
}
Of course I can use the name of the element instead of use a custom element class to open the table row, but in this way I'm feel more flexible.

Related

How to post multiple inputs in database

I'm working with Codeigniter, on a sales targets' form for salesmen.
They have to input values for each product, locality, year, etc.
Product and locality are already get with existing database: no need to set rules (see controller).
When I checked the post (with enable_profiler of Codeigniter), I get this:
The problem is these datas don't insert into the database table.
I read and tested a lot, but always blocked.
Here is my model:
public function add($params)
{
$this->db->insert($this->table, $params);
return $this->db->insert_id();
}
My controller:
$this->load->library('form_validation');
$this->form_validation->set_rules('year', 'Year', 'required|integer');
$this->form_validation->set_rules('prevision', 'Prevision', 'required');
$this->form_validation->set_rules('value', 'Value', 'required|integer');
if ($this->form_validation->run()) {
$params = array(
'year' => $this->input->post('year'),
'prevision' => $this->input->post('prevision'),
'locality_id' => $this->input->post('locality'),
'product' => $this->input->post('product'),
'value' => $this->input->post('value'),
);
$this->Objectif_model->add($params);
redirect('admin/objectif');
} else {
$this->layout('admin/objectif/add');
}
And my view with inputs:
<?php foreach ($products as $product) : ?>
<tr class="form-group">
<td class="bg-warning">
<?= $product->grp_product; ?>
</td>
<td class="bg-warning product">
<?= $product->code; ?>
</td>
<?php foreach ($localities as $locality ) : ?>
<td>
<input type="text" class="form-control valeur" placeholder="Value k€" name="value[]" data-validation="number" data-validation-ignore="./" data-validation-optional="true" />
<input type="text" name="year[]" />
<input type="text" name="prevision[]" />
<input type="text" name="product[]" value="<?= $product->code ?>" />
<input type="text" name="localite_id[]" value="<?= $locality->id ?>" />
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
Hope you will help me.
I find my answer.
In my controller, I had to pass each post in a variable and made a loop.
//action post in a var for each data
$id = $this->input->post('id');
$prev = $this->input->post('prevision');
$year= $this->input->post('year');
$locality_id = $this->input->post('locality_id');
$product = $this->input->post('product');
$value= $this->input->post('value');
if ($this->form_validation->run()) {
$count_id = count($id);
if (isset($id)) {
for ($i = 0; $i < $count_id; $i++) {
$obj[$i] = array(
'id' => $id[$i],
'prevision' => $prev,
'year' => $year,
'locality_id' => $locality_id[$i],
'product' => $product[$i],
'value' => $value[$i]*1000
);
} // endfor
if (isset($obj)) {
$this->Objectif_model->add_obj($obj);
} // endif
}// endif isset
}// endif form validation

Dynamic form input fields in Cakephp 3

I have seen this: https://waltherlalk.com/blog/dynamic-form-input-fields and have been active in this: Dynamically add form field rows - cakePHP. I have reached the stage where the setup is as per the original tutorial with changes made as per the Stackoverflow post from monsur.hoq.
The form is working fine but, upon saving, it only saves the 'student' part of the data: nothing is sent to grades. The add part of my controller currently looks like this:
public function add()
{
$student = $this->Students->newEntity();
if ($this->request->is('post')) {
$student = $this->Students->patchEntity($student, $this->request->data);
if ($this->Students->save($student)) {
$this->Flash->success(__('The student has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The student could not be saved. Please, try again.'));
}
}
$this->set(compact('student'));
$this->set('_serialize', ['student']);
}
All code is as per bake or the tutorial shaped by the monsur.hoq post.
If anyone could help me to arrive at a working Cakephp3 example of the Walther Lalk tutorial I'd be very grateful.
The debugging toolbar shows the following SQL being produced on submitting the form:
INSERT INTO students (name, created, modified)
VALUES
(
'Test Two', '2016-09-13 16:04:07',
'2016-09-13 16:04:07'
)
All that serves to do is confirm the problem. Debugging in PHP Storm on form submission reveals the following:
$_POST = {array} [3]
_method = "POST"
name = "Test Four"
Grade = {array} [1]
0 = {array} [3]
id = ""
subject = "Maths"
grade = "3"
The add.ctp is as follows:
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading"><?= __('Actions') ?></li>
<li><?= $this->Html->link(__('List Students'), ['action' => 'index']) ?></li>
<li><?= $this->Html->link(__('List Grades'), ['controller' => 'Grades', 'action' => 'index']) ?></li>
<li><?= $this->Html->link(__('New Grade'), ['controller' => 'Grades', 'action' => 'add']) ?></li>
</ul>
</nav>
<div class="students form large-9 medium-8 columns content">
<?= $this->Form->create($student) ?>
<fieldset>
<legend><?= __('Add Student') ?></legend>
<?php
echo $this->Form->input('name');
?>
</fieldset>
<fieldset>
<legend><?php echo __('Grades');?></legend>
<table id="grade-table">
<thead>
<tr>
<th>Subject</th>
<th>Grade achieved</th>
<th> </th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="2"></td>
<td class="actions">
Add grade
</td>
</tr>
</tfoot>
</table>
</fieldset>
<script id="grade-template" type="text/x-underscore-template">
<?php echo $this->element('grades');?>
</script>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
<script>
$(document).ready(function() {
//I changed undescore default template settings
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g
}
var
gradeTable = $('#grade-table'),
gradeBody = gradeTable.find('tbody'),
gradeTemplate = _.template($('#grade-template').remove().text()),
numberRows = gradeTable.find('tbody > tr').length;
gradeTable
.on('click', 'a.add', function(e) {
e.preventDefault();
$(gradeTemplate({key: numberRows++}))
.hide()
.appendTo(gradeBody)
.fadeIn('fast');
})
.on('click', 'a.remove', function(e) {
e.preventDefault();
$(this)
.closest('tr')
.fadeOut('fast', function() {
$(this).remove();
});
});
if (numberRows === 0) {
gradeTable.find('a.add').click();
}
});
</script>
Change from CakePHP 2 to CakePHP 3 fields name conventions,
Grade.{$key}.grade to grades.{$key}.grade
Create View/Elements/grades.ctp file with the following contents.
https://waltherlalk.com/blog/dynamic-form-input-fields
<?php
$key = isset($key) ? $key : '<%= key %>';
?>
<tr>
<td>
<?= $this->Form->hidden('grades.{$key}.id') ?>
<?= $this->Form->text('grades.{$key}.subject'); ?>
</td>
<td>
<?= $this->Form->select("grades.{$key}.grade",
[
'A+',
'A',
'B+',
'B',
'C+',
'C',
'D',
'E',
'F'
],
[
'empty' => '-- Select grade --'
]); ?>
</td>
<td class="actions">
Remove grade
</td>
</tr>

How can I add a custom attribute to a popup_menu using CGI.pm?

I use the following Perl code to generate an HTML popup menu with CGI.pm:
$html->popup_menu(
-name => "to",
-values => [#TO, $param_to],
-labels => {%TO, $param_to => $param_to,},
-default => $param_to,
-onchange => $onchange,
-class => "form-control"
);
The generated menu looks like this:
<select name="to" onchange="if (this.value=='support#abc.com' || document.theForm.supportform.value==1) document.theForm.submit();" class="form-control">
<option value=""> select recipient </option>
<option value="sales#abc.com">Sales Inquiry</option>
<option value="support#abc.com">Technical Support</option>
<option value="jobs#abc.com">Jobs # abc</option>
<option value="investor-relations#abc.com">Investor Relations</option>
<option value="webmaster#abc.com">abc Webmaster</option>
</select>
How can I add the attribute required to the <select> element?
According to the CGI documentation:
Many routines will do something useful with a named argument that it doesn't recognize.
So just add another named argument -required:
$html->popup_menu(
-name => "to",
-values => [#TO, $param_to],
-labels => {%TO, $param_to => $param_to,},
-default => $param_to,
-onchange => $onchange,
-class => "form-control",
-required => "required"
);
This will generate something like:
<select name="to" ... required="required">
However, generating HTML with CGI.pm is a pain and not very maintainable. It's better to use a templating library like Template Toolkit. Templates allow you to separate your Perl code and your HTML (mostly), so you can have something like this:
popup.tt
<select name="to" onchange="if (this.value=='support#abc.com' || document.theForm.supportform.value==1) document.theForm.submit();" class="form-control">
[% FOR option IN options %]
<option value="[% option.value %]">[% option.text %]</option>
[% END %]
</select>
my_script.cgi
use strict;
use warnings;
use CGI;
use Template;
my $tt = Template->new or die Template->error;
my $q = CGI->new;
print $q->header;
my $options = [
{ value => '', text => 'select recipient' },
{ value => 'sales#abc.com', text => 'Sales Inquiry' },
{ value => 'support#abc.com', text => 'Technical Support' },
{ value => 'jobs#abc.com', text => 'Jobs # abc' },
{ value => 'investor-relations#abc.com', text => 'Investor Relations' },
{ value => 'webmaster#abc.com', text => 'abc Webmaster' }
];
$tt->process('foo.tt', { options => $options }) or die $tt->error;
Output
<select name="to" onchange="if (this.value=='support#abc.com' || document.theForm.supportform.value==1) document.theForm.submit();" class="form-control">
<option value="">select recipient</option>
<option value="sales#abc.com">Sales Inquiry</option>
<option value="support#abc.com">Technical Support</option>
<option value="jobs#abc.com">Jobs # abc</option>
<option value="investor-relations#abc.com">Investor Relations</option>
<option value="webmaster#abc.com">abc Webmaster</option>
</select>

Input Form Using Codeigniter and Bootstrap 3

I'm using Codeigniter, styled with Bootstrap 3 to build a website.
I can't stylize the text-fields built by the PHP/Form Helper, as I'm not sure where to use the tags, every solution I've tried has resulted in either an extra text field, or just the addon appearing, or nothing at all.
Controller
public function __construct ()
{
parent::__construct();
}
public function index ()
{
// Fetch all users
$this->data['users'] = $this->user_m->get();
// Load view
$this->data['subview'] = 'admin/user/index';
$this->load->view('admin/_layout_main', $this->data);
}
public function edit ($id = NULL)
{
// Fetch a user or set a new one
if ($id) {
$this->data['user'] = $this->user_m->get($id);
count($this->data['user']) || $this->data['errors'][] = 'User could not be found';
}
else {
$this->data['user'] = $this->user_m->get_new();
}
// Set up the form
$rules = $this->user_m->rules_admin;
$id || $rules['password']['rules'] .= '|required';
$this->form_validation->set_rules($rules);
// Process the form
if ($this->form_validation->run() == TRUE) {
$data = $this->user_m->array_from_post(array('name', 'email', 'password'));
$data['password'] = $this->user_m->hash($data['password']);
$this->user_m->save($data, $id);
redirect('admin/user');
}
// Load the view
$this->data['subview'] = 'admin/user/edit';
$this->load->view('admin/_layout_main', $this->data);
}
public function delete ($id)
{
$this->user_m->delete($id);
redirect('admin/user');
}
public function login ()
{
// Redirect a user if he's already logged in
$dashboard = 'admin/dashboard';
$this->user_m->loggedin() == FALSE || redirect($dashboard);
// Set form
$rules = $this->user_m->rules;
$this->form_validation->set_rules($rules);
// Process form
if ($this->form_validation->run() == TRUE) {
// We can login and redirect
if ($this->user_m->login() == TRUE) {
redirect($dashboard);
}
else {
$this->session->set_flashdata('error', 'That email/password combination does not exist');
redirect('admin/user/login', 'refresh');
}
}
// Load view
$this->data['subview'] = 'admin/user/login';
$this->load->view('admin/_layout_modal', $this->data);
}
public function logout ()
{
$this->user_m->logout();
redirect('admin/user/login');
}
public function _unique_email ($str)
{
// Do NOT validate if email already exists
// UNLESS it's the email for the current user
$id = $this->uri->segment(4);
$this->db->where('email', $this->input->post('email'));
!$id || $this->db->where('id !=', $id);
$user = $this->user_m->get();
if (count($user)) {
$this->form_validation->set_message('_unique_email', '%s should be unique');
return FALSE;
}
return TRUE;
}
}
View
<div class="modal-body">
<?php echo validation_errors(); ?>
<?php echo form_open();?>
<div class="container">
<div class="modal-header">
<h3>Log in</h3>
<p>Please log in using your credentials</p>
</div>
<table class="table">
<tr>
<td>Email</td>
<td><?php echo form_input('email'); ?></td>
</tr>
<tr>
<td>Password</td>
<td><?php echo form_password('password'); ?></td>
</tr>
<tr>
<td></td>
<td><?php echo form_submit('submit', 'Log in', 'class="btn btn-primary"'); ?></td>
</tr>
</table>
<div class="modal-footer">
© <?php echo date('Y'); ?> <?php echo $meta_title; ?>
</div>
<?php echo form_close();?>
</div>
</div>
</div>
Bootstrap
<form class="form-signin" role="form">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="text" class="form-control" placeholder="email" required autofocus value="<?php echo set_value('email') ?>">
<input type="password" class="form-control" placeholder="password" required value"<?php echo set_value('password') ?>">
<label class="checkbox">
<input type="checkbox" value="remember-me"> Remember me
</label>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
You just pass everything as an array to form_input
<?php echo form_input(['name' => 'email', 'id' => 'email', 'class' => 'form-control', 'value' => set_value('email')]); ?>
Here's your entire form as presented,
<?php echo form_open('controller/method', ['class' => 'form-signin', 'role' => 'form']); ?>
<h2 class="form-signin-heading">Please sign in</h2>
<?php echo form_input(['name' => 'email', 'id' => 'email', 'class' => 'form-control', 'value' => set_value('email'), 'placeholder' => 'Email']); ?>
<?php echo form_password(['name' => 'password', 'id' => 'password', 'class' => 'form-control', 'placeholder' => 'Password']); ?>
<label class="checkbox">
<?php echo form_checkbox(['name' => 'remember_me', 'value' => 1]); ?>
</label>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
<?php echo form_close(); ?>

ZF 1.11 Form don't show my whitespaces

I want to create a multi-level combobox, like the following example:
<select style="white-space: pre;">
<option value="0">1 - categoria 0</option>
<option value="1"> 1.1 - categoria 1</option>
<option value="2"> 1.1.1 - categoria 2</option>
<option value="3"> 1.1.1.1 - categoria 3</option>
<option value="4"> 1.1.1.1.1 - categoria 4</option>
<option value="5"> 1.1.1.1.1.1 - categoria 5</option>
</select>
The result should be:
1 - categoria 0
1.1 - categoria 1
1.1.1 - categoria 2
1.1.1.1 - categoria 3
1.1.1.1.1 - categoria 4
1.1.1.1.1.1 - categoria 5
I'm creating my ZF 1.11 form like this:
class Admin_Form_Category extends Zend_Form
{
public $elementDecorators2 = array(
'ViewHelper',
'Errors',
array(array('data' => 'HtmlTag'), array('tag' => 'td', 'class' => 'elementSelect')),
array('Label', array('tag' => 'td')),
array(array('row' => 'HtmlTag'), array('tag' => 'tr')),
);
public function __construct($options = null)
{
//parent::__construct($options);
$view = new Zend_View();
$baseUrl = $view->baseUrl();
// Translating the form
$translate = Zend_Registry::get('translate');
$this->setName('formcategory');
$this->setAttrib('accept-charset', Zend_Registry::get('config')->resources->view->encoding);
$this->setMethod('post');
$this->setEnctype(Zend_Form::ENCTYPE_MULTIPART);
/* HERE IS MY COMBOBOX */
$parent = new Zend_Form_Element_Select('parent');
$parent->addErrorMessage($translate->_('You must select an parent'));
$parent->setLabel($translate->_('Parent'))
->setDecorators($this->elementDecorators2)
->setRequired(false)
->addFilter('StripTags')
->setValue( isset($options[ $parent->getName() ]) ? $options[ $parent->getName() ] : '');
$model = new App_Models_Category();
$data = $model->fetchAll();
$parent->addMultiOption('','');
foreach($data as $row){
$itemLevel = str_repeat(" ",$row['level']);
$parent->addMultiOption($row['category'],$itemLevel.$row['name']);
}
$this->addElement($parent);
/* ... */
}
}
But, for some reason the combobox is create like this:
<select id="parent" name="parent">
<option selected="selected" label="" value=""></option>
<option label="teste" value="14">teste</option>
<option label="test3" value="16">test3</option>
<option label="test4" value="17">test4</option>
<option label="&nbsp;final" value="23">&nbsp;final</option>
<option label="&nbsp;&nbsp;final2" value="24">&nbsp;&nbsp;final2</option>
</select>
And the result is:
teste
test3
test4
final
final2
Then, the whitespaces are not shown...
Someone can help me?!
I forgot to say, my ZF form have this css:
select {
white-space: pre;
}
$this->setAttrib('escape', false); doesn't work with Zend_Form_Element_Select.
Currently, there is no way to do that using a native method since Zend_View_Helper_FormSelect contains a bug already reported here (ZF-9388).
The only solution is to override the formSelect view helper and make it work the way you want. For instance, you would need to write something like this:
$opt = '<option'
. ' value="' . $value . '"'
. ' label="' . $label . '"';
instead of this:
$opt = '<option'
. ' value="' . $this->view->escape($value) . '"'
. ' label="' . $this->view->escape($label) . '"';
in the _build() method.
By default ZF escapes input from Zend_Form, you can prevent this by adding the following to your Zend_Form_Element:
$this->setAttrib('escape', false);