Search code examples
yii2jquery-select2active-form

Add new value to dropdown list


In a projects/create active form I have a field "related company account" as a dropdown (select2 by kartik). Behind this field I'd like to place a plus sign or something else to add new accounts to the dropdown with the following behavior:

  • gather all input done so far (like $input = compact(array_keys(get_defined_vars())); but probably needed on client side)
  • jump to accounts/create and pass $input
  • after submiting the new account jump back to projects/create (e.g. return $this->redirect(Yii::$app->request->referrer);) and fill the previously entered data (extract($input, EXTR_PREFIX_SAME, "arr");)

I'm struggling now with several issues:

  • Is this process according to best practice or should I change something fundamentally?
  • How is the button like? Submit button, link or some form of javascript?
  • Problem with Submit button is that not all required fields may be filled. So saving and resuming/updating the project model might not be possible.
  • Problem with link is that it is constructed before data was entered
  • Problem with javascript is that I have no glue

Any hints are welcome. Thank you in advance.


Solution

  • One alternative i would suggest is using Session.

    As for the "Add Accounts" button, i would use Submit button, and give different name to actual Submit button (two submit button in form, as answered in here). So, the projects/create view will look like this :

    <?php $form = ActiveForm::begin(); ?>
    
    ...
    ...
    ...
    
    <?= $form->field($model, 'account_id')->widget(Select2::classname(), [
        'data' => ArrayHelper::map(Account::find()->all(), "id", "name"),
        'options' => ['placeholder' => 'Select a related company account ...'],
        'pluginOptions' => [
            'allowClear' => true
        ],
    ]) ?>
    
    <?= Html::submitButton('Add Account ;)', ['class' => 'btn btn-success', 'name' => 'add_account_submit']) ?>
    
    ...
    ...
    ...
    
    <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>
    
    <?php ActiveForm::end(); ?>
    

    And then check in ProjectsController, which submit button pressed by user. If add account was pressed, then save the inputed field (i would put this function in model for clearance), else, save the model or anything. And, before all that, check if session about project is set, if yes then pre-load it to model (again, in model). Okay, like they say, one code is worth a thousand words, so, this is ProjectsController will look like :

    class ProjectsController extends Controller
    {
    
    ...
    ...
    ...
    
        public function actionCreate($category)
        {
            $model = new Projects();
    
            if (Projects::isSavedInSession()) {
                $model->loadFromSession();
            }
            if (Yii::$app->request->post('add_account_submit')) { // if add_account_submit is clicked
                $model->saveTosession(Yii::$app->request->post('Projects')); // I assume your model named Projects, if not, change this value to your model name
                return $this->redirect(['accounts/create']);
            }
            if ($model->load(Yii::$app->request->post()) && $model->save()) {
                $model->clearSession(); // we dont need the session anymore
                return $this->redirect(['index');
            }
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    
    ...
    ...
    ...
    }
    

    And Projects model will look like :

    class Projects extends \yii\db\ActiveRecord
    {
    ...
    ...
    ...
    
        public static function isSavedInSession() { // why this is static is beyond this question context
            if (Yii::$app->session->get('projects')) return true;
            return false;
        }
    
        public function loadFromSession() {
            if (Yii::$app->session->get('projects_name')) $this->name = if (Yii::$app->session->get('projects_name'));
            if (Yii::$app->session->get('projects_account_id')) $this->account_id = if (Yii::$app->session->get('projects_account_id'));
            ...
            ... // insert all model's field here
            ...
        }
    
        public function saveToSession($fields) {
            Yii::$app->session->set('projects', 1);
            foreach ($fields as $field=>$value) {
                Yii::$app->session->set('projects_' . $field, $value);
            }
        }
    
        public function clearSession() {
            Yii::$app->session->remove('projects'));
            Yii::$app->session->remove('projects_name'));
            Yii::$app->session->remove('projects_account_id'));
            ...
            ... // insert all model's field here
            ...
        }
    
    ...
    ...
    ...
    }
    

    And in the AccountsController, just tell the program to jump back to projects/create if projects session is set, like so :

    class AccountsController extends Controller
    {
    
    ...
    ...
    ...
    
        public function actionCreate($category)
        {
            $model = new Accounts();
    
            if ($model->load(Yii::$app->request->post()) && $model->save()) {
                if (Projects::isSavedInSession()) {
                    return $this->redirect(['projects/create');
                }
                return $this->redirect(['index');
            }
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    
    ...
    ...
    ...
    }
    

    Well, it's looks bit lengthy, but yeah, it's worth trying. Anyway, you could use this approach for another purpose, save current form state for example.

    Oh, one more thing, i haven't tested this in real code, so if any error exposed on my code, hit me up in comment.

    Happy coding. :)