Tuesday, July 9, 2013

Yii: Handling variable number of inputs

Sometimes an application requires a form with variable number of inputs. For example, a task management application can provide a screen where you can add one or more tasks to your task list. An example of such an application is shown in the following screenshot:


By default, the page will display one task and two buttons: Add task will add another empty task and Save will reload a form with all the tasks added. Let's check how we can solve it.

Getting ready

Create a fresh application using yiic webapp.

How to do it...

For our example, we will not save any data into the database. Instead, we will learn how to get a form with variable number of fields up and running, and how to collect data submitted with it.
  1. Therefore, we will start with the task model. As we agreed not to use database, CFormModel will be enough. protected/models/Task.php:
  2. Now, the controller protected/controllers/TaskController.php:
  3. setAttributes($taskData);
                    if ($model->validate())
                        $models[] = $model;
                }
            }
            if (!empty($models)) {
    // We've received some models and validated them.
    // If you want to save the data you can do it here.
            }
            else
                $models[] = new Task();
            $this->render('index', array(
                'models' => $models,
            ));
        }
    
        public function actionField($index) {
            $model = new Task();
            $this->renderPartial('_task', array(
                'model' => $model,
                'index' => $index,
            ));
        }
    
    }
    
  4. Now, the pretected/views/task/index.php view:
    • renderPartial('_task', array( 'model' => $models[$i], 'index' => $i, )) ?>
    'tasks-add')) ?> clientScript->registerCoreScript ("jquery") ?>
  5. Finally, a partial protected/views/task/_task.php:
  6. That is it. Now run the index action of the task controller and check it in action, as shown in the following screenshot:

How it works...

Let's review how it works starting from the controller's index action. As we are working with more than one data item, we need to collect them accordingly. Same as with a single model, a form will pass its data in $_POST['model_name']. The only difference is that in our case, there will be an array of data items as follows:
        if (!empty($_POST['Task'])) {
            foreach ($_POST['Task'] as $taskData) {
                $model = new Task();
                $model->setAttributes($taskData);
                if ($model->validate())
                    $models[] = $model;
            }
        }
For each data item, we are creating a Task model, setting its attributes with data item and if it is valid, storing a model into the $models array as follows:
if (!empty($models)) {
// We've received some models and validated them.
// If you want to save the data you can do it here.
        }
        else
            $models[] = new Task();
If the $models array is not empty, then there is data passed from the form and it is valid. If there is no data in the $models, then we still need at least one item to show it in the form. We are not actually saving data in this example. When we use Active Record models, we can actually save data using $model->save().Then, we just render the index view when we render a form as follows:

    renderPartial('_task', array(
        'model' => $models[$i],
        'index' => $i,
    ))
    ?>

As there are many models, we need to render fields for each one. That is done in the _task view partial:
  • Note how we use active labels and active fields. For each one, we specify a model and a name in format [model_index]field_name. The preceding code will generate the following HTML:
  • Fields such as Task[0][title] are a special PHP feature. When submitted, parameters with such names are automatically parsed into PHP arrays. Now, let's review how the Add task button works:
     'tasks-add')) ?>
    clientScript->registerCoreScript("jquery") ?>
    
    
    We are adding a button with a class tasks-add and a jQuery script that attaches the onClick handler to it. On click, we send an AJAX request to the field action of our controller with parameter index. Parameter's value equals the number of data items in the form. The field action responds with a part of an HTML form that we append to what we already have.

    There's more…

    You can find additional information about handling multiple inputs in the official guide at the following URL:
    1. http://www.yiiframework.com/doc/guide/en/form.table

    2 comments:

    1. this is good article. So can you send for me this source code. donrucos1@yahoo.com Thank you

      ReplyDelete
    2. Grateful for you writing this blog.

      ReplyDelete