CJuiDialog for create new model

65 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

#12672 report it
nfdevil at 2013/04/04 02:57pm
Nesting forms isn't allowed

I didn't get this to work at first. The form element was being deleted by jquery's html() method in Chrome. So I went to the Yii IRC and we solved it there. Found out that nested forms aren't allowed ;)

To fix this, just put the div where your 'classroom creation' form will be in

<div class="divForForm"></div>

at the END of the student's _form.php, so under the endWidget of the student form (where the form tag is being closed)

#12222 report it
Andrés at 2013/03/07 08:56am
Not working with CJuiAutocomplete

Thank you very much for this great article. I've been able to implement the code and I see the dialog and save a new record. However, in my create view I've two CJuiAutocomplete that aren't working and I don't know why. Thank you for any clue.

#12220 report it
Alaa Abdelhaq at 2013/03/07 08:05am
Problem with 'data'=> "js:$(this).serialize()",

I have a problem when I wanna send some variables to be displayed inside the form Instead of using

'data'=> "js:$(this).serialize()",

I use this

'data'=> array(
                    'time_from'=> 'js:start.format("HH:MM:00")', 
                    'time_to'=> 'js:end.format("HH:MM:00")', 
                    'channel_id' => $channel->id,
                    'schedule_date'=>  'js:start.format("yyyy-mm-dd")'
                ),

Its working fine but whne I submit the form in the dialog it takes me to the full page instead in reloading form inside the dialog! How can I fix this? Thanks!

#11825 report it
broker at 2013/02/06 08:15am
dialog options

Hi, In order to set dialog options this line formDialog.js#13 has to be changed like following. dialog.dialog(); ==> dialog.dialog(options);

#11507 report it
Hesam at 2013/01/17 09:40am
Re: to use () or not to use ()

It's getting funny since we're trying to interpret sb's else code :). The one million dollar answer to your question is the way JavaScript handles functions. If you do not use the parantheses, the same way in the above example, you're passing the function itself to .submit, let's say the object of the addClassroom function. However, if you use (), you're telling Javascript that you're interested in the result of the function, the function return value, not the function itself. Hence, if you use (), the function is called and unknown return value is returned to .submit as handler, which won't work eventually. I hope it helped.

#11499 report it
Sukhwinder at 2013/01/17 12:47am
ok i got the point

By reading the jquery API at http://api.jquery.com/submit/ I under stood that actually the line

$('#dialogClassroom div.divForForm form').submit(addClassroom);

is binding an handler to submit event. But on thing still not clear, as per Jquery API we need to pass handler function to .submit(), can we pass a function without parenthesis? Thanks

#11498 report it
Sukhwinder at 2013/01/17 12:42am
Thanks

Thank you Hesam, for your reply

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);
                }

so when no form is submitted and the statement

if (data.status == 'failure')

returns true, so the empty form is loaded by the line

$('#dialogClassroom div.divForForm').html(data.div);

at this point the next line

$('#dialogClassroom div.divForForm form').submit(addClassroom);

will also get executed because it is in the same "if" statement, so will this line not submit the form?

but as per ur reply , this line will get executed when the form is submittd by the user.

This thing is confusing me Thanks

#11496 report it
pod2g at 2013/01/16 09:14pm
Re: why the same function is called again

Thanks Hesam for the explanation !

#11492 report it
Hesam at 2013/01/16 12:36pm
Re: why the same function is called again

It works like this. First, when the form the dialogue is opened by clicking on the link produced in the from. However, it's empty and then actionCreate is called through addClassroom JavaScript function. Since create form is not processed yet in the actionCreate and there is no $_Post variable, the JSON return two data: 1-it's failure status, and 2- the whole create form html by this line:

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

Only now the user can see the actual form on the JDialogue window. So far, addClassroom JavaScript function has been run once to only load the from. That's why the form needs the second addClassroom in submit function to actually pass the data from the dialogue to the actionCreate. I'm not expert in javascript, but I believe that's how it works.

#11483 report it
Sukhwinder at 2013/01/16 06:35am
why the same function is called again

After digging for long time i could not under stand the line


$('#dialogClassroom div.divForForm form').submit(addClassroom);


why the same function is again called? and why the argument in the .submit() is not passed like .submit(addClassroom()) because addClassroom is a function?

Please elaborate

#11412 report it
Hesam at 2013/01/11 03:01pm
Reply to renathy question

Hi Renathy, I had a very same problem, and initially I thought that it was Ajax problem, as some other users reported it. However, since you can see the rendered form initially in your dialog box, it means that the isajax function worked properly and returned status=failure, so the from is loaded in dialog box. However, if you have a typo, as I had, when you hit the submit button nothing happens. My typo was in model _form-->, and I hadn't replaced the classroom to author, see below:

$('#dialogAuthor div.divForForm form').submit(addClassroom);
#11053 report it
Zugluk at 2012/12/12 10:11am
A way to do it without ajax

Great tutorial dude, much appreciated.

However I would like to know if there was a way to do it but without ajax submitting... calling directly the controller action... My purpose would be to upload files but it's impossible using javascript so I would like to call directly controller action without ajax post. As if I was on /myModel/create.

Any Idea ?

#9979 report it
Freeflow at 2012/09/26 07:39am
Just adding new data to the dropDownList

First of all thanx for this useful tutorial. I want to add one thing, which is similar to the comments of Russel England and behdad, with the difference, that after adding a new classroom, the new entry is automatically selected in the dropDownList. To achieve this I added the following code to the controller:

...
if($model->save())          
{
   if (Yii::app()->request->isAjaxRequest)
   {
       $newId = $model->getPrimaryKey();
       $newClassroom = $model->find('id=:newId', array(':newId' => $newId))->name;
       echo CJSON::encode(array(
            'status'=>'success', 
            'div'=>"Classroom ".$newClassroom." successfully added",
        'value'=>array(0=>$newId, 1=>$newClassroom),
       ));
       exit;               
   }

That means that I have selected the new entry from my classroom table and pass name and id via CJSON::encode to the _form.php

Here the data is fetched anyway and so I had only to add this to the javascript:

$('#Person_classroom_id').prepend('<option value=\"' + data.value[0] +'\">' + data.value[1] + '</option>');
                    $('#Person_classroom_id option[value=\"' + data.value[0] +'\"]').attr('selected', true);

The first line puts the new entry at the top of the select-field and the 2nd line selects it. That's all.

#9728 report it
hibernator at 2012/09/07 02:26am
very nice!

thanks for the tutorial!

#9009 report it
renathy at 2012/07/12 09:27am
ajax not working

I have copied above code and it doesn't work for me. I have changed Classroom to ProducType.

Dialog box is opening, form ir generated correctly and create action is called.

However, when I enter data into ProductType dialog, my code goes to producttype/create action, but it seems that it is not Ajax request.

First, Yii::app()->request->isAjaxRequest is always false Second, for test purpose I have added additional parameter $isajax=1 and this parameter is sent to create action. Also I have changed the following line if (Yii::app()->request->isAjaxRequest)

to

if (Yii::app()->request->isAjaxRequest || $isajax == 1) //added additional test parameter.

So, now create action return the following:

echo CJSON::encode(array( 'status'=>'success', 'div'=>"Type successfully added" )); exit;

As a result I have a blank page with text Type successfully added. So there is no ajax, but simply text is echoed. What it could be?

#7799 report it
Sukhwinder 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.

Leave a comment

Please to leave your comment.

Write new article