Hello everybody,
Warning: This is a very long post.
This problem is driving me crazy…
I dont know if im too stupid or this isimpossible?
I read tons of threads about this topic,
but never got this working for me…
I would prefer a solution without any extensions,
because this is acutally an example just for my personal learning purpose…
And I’m afraid that some extensions maybe are to complex for me to understand everything.
Okay, now to my problem:
I want to create a "mote complex" dynamic form with 3 models.
Two of the models are tabular & dynamic.
And I can not get the ajax validation working like I want it to have.
(Validation without ajax is no problem)
Lets take the classroom / student thing for example.
So I have following Models in my form…
-
FClass
-
id
-
name
-
-
FTeacher
-
id
-
class_id
-
teacher_id
-
-
FStudent
-
id
-
class_id
-
firstname
-
lastname
-
gender
-
(The Models all have an F in front, because I also try to build this as module…)
To keep it simple as possible:
-
Every class can have multiple teachers AND multiple students.
-
Every teacher can have several classes.
-
Every student can only have one class.
So there are buttons to dynamically add or remove teachers AND students via jquery.
===========================
WORKING SOLITION (WITHOUT AJAX):
_FORM:
[spoiler]
<?php
// register jquery
Yii::app()->clientScript->registerCoreScript('jquery');
?>
<div class="form">
<?php
$form=$this->beginWidget('CActiveForm', array(
'id' => 'classroom-form',
));
// display error summary
echo $form->errorSummary(array_merge(array($class), $teachers, $students));
?>
<fieldset><legend>Class Information</legend>
<!-- CLASS NAME -->
<div class="row" id="FClass_name_id_row">
<?php echo $form->labelEx ($class,'name'); ?>
<?php echo $form->textField ($class,'name'); ?>
<?php echo $form->error ($class,'name'); ?>
</div>
<!-- TEACHERS -->
<div id="teacher_adder_div">
<?php
$num_t = count($teachers);
for($x=0; $x < $num_t; $x++){
echo '<div class="row" id="FTeacher_'.$x.'_user_id_row">';
echo $form->labelEx ($teachers[$x],"[$x]user_id");
echo $form->dropDownList ($teachers[$x],"[$x]user_id", $dropdown_teachers, array('empty' => 'Select'));
echo $form->error ($teachers[$x],"[$x]user_id");
echo '</div>';
}
?>
</div>
<input type="button" id="add-teacher" value="add" />
<input type="button" id="rem-teacher" value="remove" />
<?PHP
// render partial as json object
$newTeacherRow = CJSON::encode(
$this->renderPartial(
'_formTeacherRow',
array(
'teacher'=>new FTeacher,
'list' => $dropdown_teachers,
'form' => $form
),
true,
false
)
);
// jQuery function to add and remove new teachers
Yii::app()->clientScript->registerScript(
'manageTeachers',
'
var nr='.$num_t.';
// add a new row to div
$("#add-teacher").click(
function(){
// get div where to add new elements
var adder=$("#teacher_adder_div");
// create new div
var newRow = '.$newTeacherRow.';
// replace string "JQUERY_REPLACE_ID" with current id
var newRow = newRow.replace(/JQUERY_REPLACE_ID/g, nr);
// Append Elements to new row-div
adder.append(newRow.toString());
// count elements
nr=nr+1;
}
);
// remove row from div
$("#rem-teacher").click(
function(){
var oldDiv = $("#teacher_adder_div").children("div").eq(nr-1);
oldDiv.remove();
if(nr > 0){
nr=nr-1;
}else{
nr=0;
}
}
);
'
);
?>
</fieldset>
<!-- ############################################################### STUDENTS -->
<fieldset>
<legend>Students</legend>
<table>
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Gender</th>
</tr>
</thead>
<tbody id="student_adder">
<!-- ADD STUDENTS DYNAMICALLY -->
<?php
$num_studs = count($students);
for($d=0; $d < $num_studs; $d++){
echo '<tr id="FStudent_'.$d.'_row">';
echo '<td>';
echo $form->textField($students[$d],"[$d]firstname",array('size'=>14,'maxlength'=>14));
echo $form->error($students[$d],"[$d]firstname");
echo '</td>';
echo '<td>';
echo $form->textField($students[$d],"[$d]lastname",array('size'=>14,'maxlength'=>14));
echo $form->error($students[$d],"[$d]lastname");
echo '</td>';
echo '<td>';
echo $form->dropDownList($students[$d],"[$d]gender", FStudent::getGenderTypes(), array('empty' => 'Select'));
echo $form->error($students[$d],"[$d]gender");
echo '</td>';
echo '</tr>';
}
?>
</tbody>
</table>
<input type="button" id="add-student-row" value="add" />
<input type="button" id="rem-student-row" value="remove" />
<?PHP
// render partial as json object
$newStudentRow = CJSON::encode($this->renderPartial('_formStudentRow', array('student'=>new FStudent, 'form'=>$form), true));
// jQuery function to add and remove new copyreceivers
Yii::app()->clientScript->registerScript(
'manageStudentRows',
'
var num='.$num_studs.';
// add a new row to div
$("#add-student-row").click(
function(){
// get div where to add new elements
var adder=$("#student_adder");
// create new div
var newRow = '.$newStudentRow.';
// replace string "JQUERY_REPLACE_ID" with current id
newRow = newRow.replace(/JQUERY_REPLACE_ID/g, num)
// Append Elements to new row-div
adder.append(newRow);
// count elements
num=num+1;
}
);
// remove row from div
$("#rem-student-row").click(
function(){
var oldDiv = $("#student_adder").children("tr").eq(num-1);
oldDiv.remove();
if(num > 0){
num=num-1;
}else{
num=0;
}
}
);
'
);
?>
</fieldset>
<div class="row buttons">
<?php echo CHtml::submitButton($class->isNewRecord ? 'Save' : 'Update'); ?>
</div>
<?php $this->endWidget(); ?>
</div><!-- form -->
[/spoiler]
AJAX ADDED PARTIALS:
_formTeacherRow:
[spoiler]
<div class="row" id="FTeacher_JQUERY_REPLACE_ID_user_id_row">
<?php echo $form->labelEx ($teacher,"[JQUERY_REPLACE_ID]user_id"); ?>
<?php echo $form->dropDownList ($teacher,"[JQUERY_REPLACE_ID]user_id", $list, array('empty' => 'Select')); ?>
<?php echo $form->error ($teacher,'[JQUERY_REPLACE_ID]user_id'); ?>
</div>
[/spoiler]
_formStudentRow:
[spoiler]
<tr id="FStudent_[JQUERY_REPLACE_ID]_row">
<td>
<?php echo $form->textField ($student,'[JQUERY_REPLACE_ID]firstname',array('size'=>14,'maxlength'=>14)); ?>
<?php echo $form->error ($student,'[JQUERY_REPLACE_ID]firstname'); ?>
</td>
<td>
<?php echo $form->textField ($student,'[JQUERY_REPLACE_ID]lastname',array('size'=>14,'maxlength'=>14)); ?>
<?php echo $form->error ($student,'[JQUERY_REPLACE_ID]lastname'); ?>
</td>
<td>
<?php echo $form->dropDownList($student,"[JQUERY_REPLACE_ID]gender", FStudent::getGenderTypes(), array('empty' => 'select')); ?>
</td>
</tr>
[/spoiler]
CONTROLLER:
[spoiler]
<?
public function actionCreate(){
// get dropdown list
$dropdown_teachers = FTeacher::model()->getDropdownUser();
// create form objects
$class = new FClass;
$teachers = array(new FTeacher);
$students = array(new FStudent);
// if everything is posted
if(
isset($_POST['FClass']) &&
isset($_POST['FTeacher']) &&
isset($_POST['FStudent'])
){
$valid=true;
// collect and validate teachers (tabular)
$numTeacher = count($_POST['FTeacher']);
for($x=0; $x < $numTeacher; $x++){
if(isset($_POST['FTeacher'][$x])){
$teachers[$x] = new FTeacher;
$teachers[$x]->attributes = $_POST['FTeacher'][$x];
$valid=$teachers[$x]->validate() && $valid;
}
}
// collect and validate student (tabular)
$numStuds = count($_POST['FStudent']);
for($x=0; $x < $numStuds; $x++){
if(isset($_POST['FStudent'][$x])){
$students[$x] = new FStudent;
$students[$x]->attributes = $_POST['FStudent'][$x];
$valid=$students[$x]->validate() && $valid;
}
}
// collect and validate class (single)
$class->attributes = $_POST['FClass'];
$valid=$class->validate() && $valid;
// all data posted and successfull validated
if($valid){
// just give out all the post data ...
// saving issue comes, when i solved my validation problem.
echo "<pre>";
print_r($_POST);
echo "<pre>";
}
}
// default => render form (exept everythng was validated)
$this->render('create', array(
'class' =>$class,
'dropdown_teachers' =>$dropdown_teachers,
'teachers' =>$teachers,
'students' =>$students
));
}
?>
[/spoiler]
Okay - and now I would prefer to add ajaxValidation to this…
So I changed the following.
In the form:
[spoiler]
<?
$form=$this->beginWidget('CActiveForm', array(
'id' => 'classroom-form',
'enableAjaxValidation' => true,
'clientOptions' => array (
'validateOnChange' => false,
'validateOnType' => false,
'validateOnSubmit' => true,
)
));
?>
[/spoiler]
In the Controller:
[spoiler]
<?
...
$class = new FClass;
$teachers = array(new FTeachers);
$students = array(new FStudents);
$this->performAjaxValidation($class, $teachers, $students);
...
?>
[/spoiler]
And wrote the performAjaxValidation function:
[spoiler]
<?
protected function performAjaxValidation($class, $teachers, $students)
{
if(isset($_POST['ajax']) && $_POST['ajax']==='classroom-form'){
$class = CJSON::decode(CActiveForm::validate($class));
$teachers = CJSON::decode(CActiveForm::validateTabular($teachers));
$students = CJSON::decode(CActiveForm::validateTabular($students));
echo CJSON::encode(CMap::mergeArray($class, $teachers, $studends));
Yii::app()->end();
}
}
?>
[/spoiler]
…
Now follwing happends:
-
I leave the form blank.
-
In firebug I can see that the form ist posted.
-
I receive an JSON Object with all errors for all models / rows back.
-
ErrorSuammary / On field error displays the errors for the "class" model only.
-
The error summary AND onfield error does not display the errors for "teachers" and "students"…
This really drives me crazy.
What am I doing wrong / missing here?
Could somebody give me a hint in the right direction or show me an example?
Is there any "easy to understand" solution for this problem wich does not require any extensions?
… and further:
How could I solve it, when I want to add rows dynamically also with onChange enabled?
Hope this was not tooooo long for you.
Every help appriciated!
Best Regards & Thanks in advance
Edit: Spoilered the code to make this thread easier to read.