CJuiDialog for create new model

42 followers

Introduction

In this tutorial we will learn how to realize a create interface using a dialog.

Here is a similar tutorial that uses Ajax link to achieve the goal, but we will use a simpler and more effective approach: the event onSubmit of the form.

Scenario

Let's imagine we have a classroom with many students. If the user fills in the form of the student and there is no classroom made, he will have to create a classroom first and lose the already inserted input.

We want to allow the user to create the classroom from the form of the student, without changing pages.

Preparation of the form

First of all we need a form for creating the classroom. We can generate a Crud for classroom with gii and adapt the code to our needs.

Once we are satisfied with our form and it works with the usual submit, we can use it in a dialog.

Enhance the action create

We need to enhance the action create of the classroom controller.

Let's change it this way:

public function actionCreate()
    {
        $model=new Classroom;
 
        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);
 
        if(isset($_POST['Classroom']))
        {
            $model->attributes=$_POST['Classroom'];
            if($model->save())
            {
                if (Yii::app()->request->isAjaxRequest)
                {
                    echo CJSON::encode(array(
                        'status'=>'success', 
                        'div'=>"Classroom successfully added"
                        ));
                    exit;               
                }
                else
                    $this->redirect(array('view','id'=>$model->id));
            }
        }
 
        if (Yii::app()->request->isAjaxRequest)
        {
            echo CJSON::encode(array(
                'status'=>'failure', 
                'div'=>$this->renderPartial('_form', array('model'=>$model), true)));
            exit;               
        }
        else
            $this->render('create',array('model'=>$model,));
    }

We add some small changes: in case of ajax request we write a json encoded array.

In this array we return a status (failure/success) and the whole form created with renderPartial.

The dialog

Now the back-end is done, let's write the dialog itself.

In the form of the student somewhere we add this code:

<?php echo CHtml::link('Create classroom', "",  // the link for open the dialog
    array(
        'style'=>'cursor: pointer; text-decoration: underline;',
        'onclick'=>"{addClassroom(); $('#dialogClassroom').dialog('open');}"));?>
 
<?php
$this->beginWidget('zii.widgets.jui.CJuiDialog', array( // the dialog
    'id'=>'dialogClassroom',
    'options'=>array(
        'title'=>'Create classroom',
        'autoOpen'=>false,
        'modal'=>true,
        'width'=>550,
        'height'=>470,
    ),
));?>
<div class="divForForm"></div>
 
<?php $this->endWidget();?>
 
<script type="text/javascript">
// here is the magic
function addClassroom()
{
    <?php echo CHtml::ajax(array(
            'url'=>array('classroom/create'),
            'data'=> "js:$(this).serialize()",
            'type'=>'post',
            'dataType'=>'json',
            'success'=>"function(data)
            {
                if (data.status == 'failure')
                {
                    $('#dialogClassroom div.divForForm').html(data.div);
                          // Here is the trick: on submit-> once again this function!
                    $('#dialogClassroom div.divForForm form').submit(addClassroom);
                }
                else
                {
                    $('#dialogClassroom div.divForForm').html(data.div);
                    setTimeout(\"$('#dialogClassroom').dialog('close') \",3000);
                }
 
            } ",
            ))?>;
    return false; 
 
}
 
</script>

And that's all. In this code we have:

  • A link for open the dialog
  • the dialog itself, with a div inside that will be replaced with ajax
  • the javascript function addClassroom().

This function fires an ajax request to the action create we prepared in the previous step.

The returned form will be placed in the dialog (with eventually, all errors and so on) in case of status failure, in case of status success in the example we replace the div and we close the dialog after 3 seconds.

If you use this system in the form for student, you can return, for example, the id of the newly inserted classroom and select it automatically in a drop down list.

Summary

To make a long story short:

  • Prepare the usual creation with gii generated code
  • Change the action create for answer to Ajax requests
  • Place the link/dialog/js wherever you want.

This methodology is very comfortable because it changes anything in the code of the _form, so any eventually added field in classroom will be available in both standard and dialog insert.

Total 20 comments

#7799 report it
sukhman4 at 2012/04/19 05:50am
What if i want to redirect to updated page

This code is working fine, but it adds the classroom in the background, how can i redirect the user to say classroom index action, so that updated page loads after the dialogue has finished?

#7570 report it
fr0d0z at 2012/03/30 05:17pm
Re: Bug in code

@w00tw00t111: Just looked over my code and I'd gotten it more complicated than I needed to. So, now, don't think I can verify the code in my comment. Will delete that comment.

Thanks!

#7569 report it
w00tw00t111 at 2012/03/30 04:51pm
Re: Bug In Code

fr0d0z,

Tried your code and zaccaria's original code and your code would not post data where as zaccaria's does.

Can you verify your comment's code?

#6223 report it
silintzir at 2011/12/20 05:55am
CMaskedTextField inside dialog fails

I'm trying to use a CMaskedTextField inside the dialog but it fails. Instead it displays like a normal textField. Does anybody know the reason why?

Same happens with CJuiDatePicker as well..

#5634 report it
zaccaria at 2011/10/27 02:20am
re nettrinity

data are the data passed via post.

$(this) is referrring to the form, .serialize take all the contents of the field in the form and send to the server.

Using this data=> $(this).serialize() you will create an ajax request with in the $_POST the attributes, so it can be managed to the standard action.

#5628 report it
nettrinity at 2011/10/26 11:46am
what is this? 'data'=> "js:$(this).serialize()",

I checked Jquery doc about serialize(), but still don't understand what is this code doing for you?

what is $(this) referring to? why??

Thanks a million!

#5251 report it
Sisko at 2011/09/26 10:59am
Enabling 'processing' Parameter of renderPartial

Thank you for that article. Following problem: I wanted to have another widget within the JDialog (DatePicker), so I had to set the 4th parameter of renderPartial() to 'true'. But then only the first click on the link is working. If I close the dialog and click again, the dialog doesnt show. Any idea what could go wrong here? Maybe it's the same problem as with the previous poster.

Edit: I had to stop loading jquery every request. Thanks tydeas for your info in your wiki article linked in the beginning of the tutorial.

#4787 report it
yichuanhu at 2011/08/16 03:56pm
Link does not work after clicked once

The code works well, but after I created a new model the link did not work any more (dialog did not pop up). What could be the problem? Thanks!

#4753 report it
tadas at 2011/08/11 07:02am
Ajax validation

If you want to enable ajax validation in form loaded with ajax:

In the controller then rending partial form add third true parameter, this will include also all scripts from the view:

'div' => $this->renderPartial('_form', array('model' => $model), true, true)));

In the partial form you are rendering add:

Yii::app()->clientscript->scriptMap['jquery.js'] = false;

Then jquery script will be not included second time

#4472 report it
StErMi at 2011/07/12 04:46am
Not working

For me it's not working. Maybe I don't understand well how to do this but I copy/paste the code and when I click on the submit button of the dialog form it only prints the JSON code generated by the actionCreate method.

What's wrong? I think that it's not working the CHtml::ajax

#4396 report it
fabhren at 2011/07/05 07:05am
Blank dialog eureka

Just found out and sharing for anyone to check it out. I changed the yii generated files and put locale characters with accents and saved it. Because they were generated with encoding in ansi, i had to save it in utf-8 without bom, change the error characters and save it again.

Apparently, php can't read these files and it doesn't report errors either.

#4394 report it
fabhren at 2011/07/05 04:57am
Blank dialog

Hi,

I'm rather noobish on yii and following your tutorial, I click on the link, it opens the dialog with nothing on it.

The json request returns {"status":"failure","div":null}.

Everything is done as the tutorial. Is there something I am missing ?

Thanks for the help!

I will place the code here:

<?php echo CHtml::link('Criar Nova Permissão', "",  // the link for open the dialog
    array(
        'style'=>'cursor: pointer; text-decoration: underline;',
        'onclick'=>"{editPermission(); $('#dialogPermission').dialog('open');}"));?>
| <?php echo CHtml::link('Pesquisa Avançada','#',array('class'=>'search-button')); ?>
 
 
 
<?php
$this->beginWidget('zii.widgets.jui.CJuiDialog', array( // the dialog
    'id'=>'dialogPermission',
    'cssFile'=>'jquery-ui-1.8.13.custom.css',
    'theme'=>'roriz/css/ui',
    'themeUrl'=>Yii::app()->request->baseUrl.'/themes',
    'options'=>array(
        'title'=>'Criar Nova Permissão',
        'autoOpen'=>false,
        'modal'=>true,
        'width'=>550,
        'height'=>470,
    ),
));?>
<div class="divForForm"></div>
 
<?php $this->endWidget();?>
 
<script type="text/javascript">
// here is the magic
function editPermission()
{
    <?php echo CHtml::ajax(array(
            'url'=>array('permission/create'),
            'data'=> "js:$(this).serialize()",
            'type'=>'post',
            'dataType'=>'json',
            'success'=>"function(data)
            {
                if (data.status == 'failure')
                {
                    $('#dialogPermission div.divForForm').html(data.div);
                          // Here is the trick: on submit-> once again this function!
                    $('#dialogPermission div.divForForm form').submit(editPermission);
                }
                else
                {
                    $('#dialogPermission div.divForForm').html(data.div);
                    setTimeout(\"$('#dialogPermission').dialog('close') \",3000);
                }
 
            } ",
            ))?>;
    return false; 
 
}
 
</script>

on the actionCreate:

/**
     * Creates a new model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     */
    public function actionCreate()
    {
        $model=new Permission;
 
        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);
 
        if(isset($_POST['Permission']))
        {
            $model->attributes=$_POST['Permission'];
            if($model->save())
            {
                if (Yii::app()->request->isAjaxRequest)
                {
                    echo CJSON::encode(array(
                        'status'=>'success', 
                        'div'=>"Permissão inserida com sucesso."
                        ));
                    exit;               
                }
                else
                    $this->redirect(array('view','id'=>$model->id));
            }
        }
 
        if (Yii::app()->request->isAjaxRequest)
        {
            echo CJSON::encode(array(
                'status'=>'failure', 
                'div'=>$this->renderPartial('_form', array('model'=>$model), true)));
            exit;               
        }
        else
            $this->render('create',array('model'=>$model,));
    }

and finally the view:

<?php
$this->breadcrumbs=array(
    'Entities'=>array('index'),
    'Create',
);
 
$this->menu=array(
    array('label'=>'List Entity', 'url'=>array('index')),
    array('label'=>'Manage Entity', 'url'=>array('admin')),
);
?>
 
<h1>Create Entity</h1>
 
<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>
#4288 report it
logan at 2011/06/22 11:49am
Ajax Validate

I just want to say thanks Zaccaria! I've been banging my head against a wall. I flipped a few things around to update the model, rather than create a new one.

Have you gotten ajax validation to work with this process? That's the next project that will probably make me go bald.

Thanks! Logan

#3848 report it
behdad at 2011/05/13 11:01am
updating a dropDownList

Yes, you are right, also I didn't pay attention to RussellEngland's comment which is a better demonstration of the same issue.

#3844 report it
zaccaria at 2011/05/13 08:39am
Updating a dropdownlist

You can avoid to do a second ajax call, simply return the depending dropdown in the response for successfully insert.

#3843 report it
behdad at 2011/05/13 08:03am
Typo

Sorry, actionJSONPositionTitles() doesn't return a JSON, wrong copy and paste, still it do the job.

#3842 report it
behdad at 2011/05/13 07:59am
Reloading a dropDownList after successful insert.

Zaccaria, Thanks for this fine piece of howto.

I also wanted to update a dropDownList after the success of insertion from the dialog. So this is how I did it, thought might help people.

I have already implemented a method in the Controller which returns a JSON data for the dropDownList needing a reload.

public function actionJSONPositionTitles()
    {   
        $criteria = new CDbCriteria;
        $criteria->order = 'Title ASC' ;
        $data = Model::model()->findAll($criteria);
 
        $data=CHtml::listData($data,'ID','Title');
 
        echo CHtml::tag('option',
                       array('value'=>''),'',true);
 
        foreach($data as $value=>$name)
        {
            echo CHtml::tag('option',
                       array('value'=>$value),CHtml::encode($name),true);
        }
        Yii::app()->end();
    }

Then giving the permission to the lets say Admin in accessRules()

array('allow', // allow admin user to perform 'admin' and 'delete' actions
            'actions'=>array('admin','delete','JSONPositionTitles'),
                'users'=>array('admin'),
            ),

And finally, with adding another CHtml::ajax() in success IF block we can update the dropDownList after successful insertion.

<script type="text/javascript">
// here is the magic
function add()
{
    <?php echo CHtml::ajax(array(
            'url'=>array('model/create'),
            'data'=> "js:$(this).serialize()",
            'type'=>'post',
            'dataType'=>'json',
            'success'=>"function(data)
            {
                if (data.status == 'failure')
                {
                    $('#mydialog div.divForForm').html(data.div);
                          // Here is the trick: on submit-> once again this function!
                    $('#mydialog div.divForForm form').submit(add);
                }
                else
                {
                    ".CHtml::ajax(array(
                                'type'=>'POST',
                                'url'=>CController::createUrl('JSONPositionTitles'),
                                'update'=>'#DropDown_ID'
                    ))."
                    $('#mydialog div.divForForm').html(data.div);
                    setTimeout(\"$('#mydialog').dialog('close') \",2000);
                }
 
            } ",
            ))?>;
    return false; 
 
}
 
</script>
#3166 report it
RussellEngland at 2011/03/23 10:40am
Update select

Great article, thank you :)

I added this to the controller for a success

echo CJSON::encode(array(
                        'status'=>'success', 
                        'div'=>"Classroom successfully added",
                        'option'=>CHtml::tag('option', array('value' => $model->id),
                                        CHtml::encode($model->name), true),
                        'id'=>$model->id,
                        ));

and this to the form - assuming there is a classroom_id select list

if (data.option!='') $('#Student_classroom_id').append(data.option);
                    if (data.id!=0) $('#Student_classroom_make_id').val(data.id);
#3057 report it
zaccaria at 2011/03/11 01:58am
exit

Yii::app()->end will give some complication in debug mode, as it will append the whole log table spoiling the json output.

For semplicity, as there are no more task that the application is supposed to trigger after outputting the json, I used exit.

If you are confident that in your case Yii::app()->end will safely work, you can use this one as well, but updating the post can make the developing experience a bit more hard for future developers.

And, last but not the least, I like exit...

#3054 report it
sclark at 2011/03/10 08:12pm
exit; vs Yii::app()->end();

I remember coming across a discussion somewhere that it is better to use Yii::app()->end() instead of 'exit', as in your example. Does anybody know why this is, or if it's true? If so, perhaps this post should be updated.

Leave a comment

Please to leave your comment.