: yii ajaxSubmitButton won't recognize my action url - yii

I have an update form, and within that form there is a table where I want to populate using ajax after filling in a couple of fields. I have tried using ajaxSubmitButton but somehow it just doesn't trigger the action that I want.
Here is my view:
<?php
echo CHtml::ajaxSubmitButton('Insert', array('myController/insertProgress'), array(
'type' => 'POST',
'success' => 'function(){
alert("success");
}',
'data' => array(
'progress' => 'js:$("#progress").val()',
),
)
);
?>
myController:
public function actionInsertProgress() {
$data = $_POST['progress'];
//do stuff here, including echoing the table row
}
When I click the submit button, it doesn't trigger the insertProgress action, but instead the main form action which is actionEdit. It's as if the URL that I provided is being ignored.
The url for this form goes something like this:
(sitename)/(modulename)/myController/edit/id/57
Thank you.
EDIT: I do have another submit button to update the whole form, which triggers the actionEdit action.
EDIT2: this is what the widget produces:
<script type="text/javascript">
/*<![CDATA[*/
jQuery(function($) {
jQuery('body').on('click','#yt0',function(){jQuery.ajax({'type':'POST','data':{'progress':$("#progress").val()},'url':'http://inarac.id/adm/topikkajian/insertProgress','cache':false});return false;});
});
/*]]>*/
</script>

If you are using module you should add the modele path to your link
$myLink = Yii::app()->getBaseUrl(true) .
'/index.php/moduleName/myController/insertProgress';
<?php
echo CHtml::ajaxSubmitButton('Insert',
$myLink, array(
'type' => 'POST',
'success' => 'js:function(){
alert("success");
}',
'data' => array(
'progress' => 'js:$("#progress").val()',
),
)
);
?>

Related

how to pass the id of the click CButtonColumn of CGridView going to the controller in Yii1.x?

I have a custom CButtonColumn within the CGridView.
one of the custom buttons is firing a CJuiDialog. now the problem is,
how to pass the id when clicked, so that the Controller will get the id, then I can do pass a model and renderPartial it inside the CJuiDialog?
here's what i have so far
'see' => array(
'label' => 'View',
'url' => 'Yii::app()->controller->createUrl("mycontrollerName/view,array("id" => "$data->id"))',
'click' => "function( e ){
e.preventDefault();
$( '#view-dialog' ).data('id',$(this).attr('id'))
.dialog( { title: 'View Details' } )
.dialog( 'open' ); }"
),
having given that code snippet.. in the controller action, I want to have the id ..is it $_GET['id'] ?, or $_POST['id'] ?..it doesn't matter for as long as I can get it so that I can use it to query in the model function
There are a few syntax errors in your code, but more importantly, you shouldn't wrap $data->id in any quotes.
'see' => array(
'label' => 'View',
'url' => 'Yii::app()->createUrl("mycontrollerName/view", array("id" => $data->id))',
'click' => "function( e ){
e.preventDefault();
$( '#view-dialog' ).data('id',$(this).attr('id'))
.dialog( { title: 'View Details' } )
.dialog( 'open' ); }",
),
So you are trying to pass the id value in the javascript code.
This is more of a jQuery issue rather than having much to do with Yii.
Run console.log($(this).attr('id')); you will probably see that you get an 'undefined' value. That is because the tag generated by Yii for the button does not contain an id parameter.
The easiest solution is to use jQuery to work with the url parameter.
e.g.
$( '#view-dialog' ).data('id',$(this).attr('href'))
if the entire URL is not needed, you could use a regex to parse only the numerical ID.
Alternatively you will have to pass the id in the buttons option parameter.
e.g.
'see' => array(
'label' => 'View',
'url' => 'Yii::app()->createUrl("mycontrollerName/view", array("id" => $data->id))',
'options' => array('id' => $data->id),
'click' => "function( e ){
...
However, please note that Yii will not render the value of $data->id in the 'option' parameter as this is not evaluated in CButtonColumn.
You will have to override Yii's CButtonColumn (see http://www.yiiframework.com/wiki/372/cbuttoncolumn-use-special-variable-data-for-the-id-in-the-options-of-a-button/)
Personally, if I were you, I'd implement some Javascript in some external code and have a regex to parse the id from the URL.

Pass Radio Button Value Onchange

In Yii, the list view used as a search result.
Controller
public function actionSearch()
{
$key=$_GET['Text'];
$criteria = new CDbCriteria();
$criteria->addSearchCondition('username',$key,true,"OR");
$criteria->select = "`username`,`country`";
$data=new CActiveDataProvider('User',
array('criteria'=>$criteria,'pagination'=>array('pageSize'=>5),
));
$this->render('search', array(
'ModelInstance' => User::model()->findAll($criteria),
'dataProvider'=>$data,
));
}
search.php
<?php
//THE WIDGET WITH ID AND DYNAMICALLY MADE SORTABLEATTRIBUTES PROPERTY
$this->widget('zii.widgets.CListView', array(
'id'=>'user-list',
'dataProvider'=>$dataProvider,
'itemView'=>'results',
'template' => '{sorter}{items}{pager}',
));
?>
<?php echo CHtml::radioButtonList('type','',array(
'1'=>'Personal',
'2'=>'Organization'),array('id'=>'type'),array( 'separator' => "<br/>",'style'=>'display:inline')
);
?>
result.php
<?php echo $data->username."<br>"; ?>
<?php echo $data->country; ?>
The user model fields are id, name , country, type, The search result shows the name and country. Now want to filter the results based on the radio button onchange (personal/organisation).
You could try to use $.fn.yiiListView.update method passing list view's id (user-list in your case) and ajax settings as arguments. data property of ajax settings is what can be used to specify GET-parameters that will be passed to your actionSearch to update the list view. So you have to analyze these parameters in the action and alter CDbCriteria instance depending on them.
The following script to bind onchange handler to your radio button list is to be registered in the view:
Yii::app()->clientScript->registerScript("init-search-radio-button-list", "
$('input[name=\"type\"]').change(function(event) {
var data = {
// your GET-parameters here
}
$.fn.yiiListView.update('user-list', {
'data': data
// another ajax settings if desired
})
});
", CClientScript::POS_READY);
You also may consider the following code as an example based on common technique of filtering CGridView results.
By the way, for performance reasons you can render your view partially in the case of ajax update:
$view = 'search';
$params = array(
'ModelInstance' => User::model()->findAll($criteria),
'dataProvider' => $data
);
if (Yii::app()->request->isAjaxRequest)
$this->renderPartial($view, $params);
else
$this->render($view, $params);

Connecting a YII ajaxButton to a controller to execute server side functions?

I have the following button:
<?php $this->widget('bootstrap.widgets.TbButton', array(
'label'=>'myLabel',
'buttonType'=>'ajaxButton',
'url'=>'someUrl',
'type'=>'primary', // null, 'primary', 'info', 'success', 'warning', 'danger' or 'inverse'
'size'=>'small', // null, 'large', 'small' or 'mini'
'ajaxOptions'=>array(
'type' => 'POST',
'beforeSend' => '
function( request ) {
//alert(request);
}'
,
'success' => 'function( data ) {
//alert(data);
}'
,
'data' => array(
'actionName' => "INCREMENT"
)
),
)); ?>
So, the tricky part is, how do I connect this button to actual backend code? I would assume it's done by posting to a URL. In my case I've got a URL set as:
'url'=>'someUrl'
Does this mean I must create a view, controller and model so there is a URL to post to? isn't there an easier way without going through that effort?
You do not necessarily need a new view. But you will need an action that catches this request.
In Yii each action has a unique url that refers to it, and there are functions that generate such a url for us, namely createUrl. There are other versions of createUrl also, the one here is from CController.
So you'd modify your url property as:
'url'=>$this->createUrl('controller-name/action-name')
Then in your controller add the action:
public function actionActionname(){
// do your server-side stuff
// maybe also return some message back to client-side view
if(success)
echo "Y";
else echo "N";
Yii::app()->end();
}

Creating Lookup field for Yii

I'm trying to make a lookupfield-like in my application.
The intention is that the user click on a browse-button, and it pops-up a dialog(widget) with a grid(CGridView) inside. The user could select a row, and the 'Description' column is sent to a textField into my form.
I've already done this part by registering the following script in the form:
Yii::app()->clientScript->registerScript('scriptName', '
function onSelectionChange()
{
var keys = $("#CGridViewUsuario > div.keys > span");
$("#CGridViewUsuario > table > tbody > tr").each(function(i)
{
if($(this).hasClass("selected"))
{
$("#Funcionario_UsuarioId").val($(this).children(":nth-child(1)").text());
}
});
}
');
And my widget:
<?php $this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog',
'options'=>array(
'title'=>'Usuário',
'width' => 'auto',
'autoOpen'=>false,
),
));
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider' => Usuario::model()->searchByLogin($model->UsuarioId),
'id' => 'CGridViewUsuario',
'filter' => Usuario::model(),
'columns' => array(
'Login',
'Nome',
),
'htmlOptions' => array(
'style'=>'cursor: pointer;'
),
'selectionChanged'=>'js:function(id){ onSelectionChange(); }',
));
$this->endWidget('zii.widgets.jui.CJuiDialog');
?>
Now there are two tasks for me to do:
When the user clicks the browse button, the CGridView should appear
with the filter already filled with the input he typed in the form.
Put the CGridView filters to work.
Not forgetting that, If all this runs successfully, when the user clicks on the save button, I'll have to save the corresponding ID of the lookupField in the model.
You can, simply provide a callback function for the dialog's open event, and in the callback function
use jquery selectors to select the input filters(of the gridview) you want to select, and populate its values from whichever field in the form you want:
$("#CGridViewUsuario .filters input[name='Userio[login]']").val($("#Funcionario_UsuarioId").val());
// replace the names/ids to whatever you are using,
// if you want to set multiple values, then you might have to run a loop or each() or something of that sort
then call the server to update the gridview according to the values you populated, using jquery.yiigridview.js' $.fn.yiiGridView.update function:
$.fn.yiiGridView.update("CGridViewUsuario", {
data: $("#CGridViewUsuario .filters input").serialize()
});
you can see the jquery.yiigridview.js file in the generated html, or in your assets folder, and within that you'll find the $.fn.yiiGridView.update function.
To subscribe to the dialog's open event you can pass the function name to the 'open' option of the dialog's 'options' field:
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog',
'options'=>array(
'title'=>'Usuário',
// other options
'open'=>'js:dialogOpenCallback'
),
));
And you can define the function in your registerScript() call itself:
<?php
Yii::app()->clientScript->registerScript('scriptName', '
function onSelectionChange()
{...}
function dialogOpenCallback(event,ui){
$("#CGridViewUsuario .filters input[name='Userio[login]']").val($("#Funcionario_UsuarioId").val());
// replace the names/ids to whatever you are using,
$.fn.yiiGridView.update("CGridViewUsuario", {
data: $("#CGridViewUsuario .filters input").serialize()
});
}
');
Further you can change how you are calling your onSelectionChange() function:
'selectionChanged'=>'js:onSelectionChange'//'js:function(id){ onSelectionChange(); }',
and change your function signature: function onSelectionChange(id).
Almost forgot, change your dataprovider and filter of the gridview, to model instances, and not static instances.

Yii - Design question - Keeping dialoges separate from the main view file and accessing them

I have a case where on my view file there are 6 links and clicking on them opens CJuiDialog boxes . I am keeping all the 6 dialog boxes code in the same view file along with links and that is causing the file to be big and loading them all together once .The ideal scenario is the dialog boxes should get loaded only when the use clicks on the links .
So is there any way we can keep only the links code in the main view file and keeping all the dialogue boxes in separate files and loading them only when the user clicks on the links
I mean
index.php ( view containing only links)
_dialog1 ( containing code for first dialog )
_dialog2 ( containing code for second dialog )
_dialog3 ( containing code for third dialog )
_dialog4 ( containing code for fourth dialog )
_dialog5 ( containing code for fifth dialog )
_dialog6 ( containing code for sixth dialog )
Sample Code
//First Dialog code
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog1',
'options'=>array(
'title'=>'Dialog box 1',
'autoOpen'=>false,
'modal'=>true,
),
));
echo 'First dialog content here';
$this->endWidget('zii.widgets.jui.CJuiDialog');
echo CHtml::link('open dialog', '#', array(
'onclick'=>'$("#mydialog1").dialog("open"); return false;',
));
//2nd dialog code
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog2',
'options'=>array(
'title'=>'Dialog box 1',
'autoOpen'=>false,
'modal'=>true,
),
));
echo 'dialog2 content here';
$this->endWidget('zii.widgets.jui.CJuiDialog');
echo CHtml::link('open dialog', '#', array(
'onclick'=>'$("#mydialog2").dialog("open"); return false;',
));
Solution I came with
//In controller
public function actionOpenDialog1()
{
$data = array();
$this->renderPartial('_dialogContent1', $data, false, true);
}
public function actionOpenDialog2()
{
$data = array();
$this->renderPartial('_dialogContent2', $data, false, true);
}
//In index.view
<div id="data">
</div>
<?php
echo CHtml::ajaxButton ("Open first dialog", CController::createUrl('dialogTesting/openDialog1'),array('update' => '#data'));
echo CHtml::ajaxButton ("Open second dialog", CController::createUrl('dialogTesting/openDialog2'),array('update' => '#data'));
?>
//_dialogContent1.php
<?php
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog1',
'options'=>array(
'title'=>'Dialog box 1',
'autoOpen'=>true,
'modal'=>true,
),
));
echo 'first dialog content here';
$this->endWidget('zii.widgets.jui.CJuiDialog');
?>
//_dialogContent2.php
<?php
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog1',
'options'=>array(
'title'=>'Dialog box 1',
'autoOpen'=>true,
'modal'=>true,
),
));
echo 'first dialog content here';
$this->endWidget('zii.widgets.jui.CJuiDialog');
?>
Thanks a lot for your help
Regards
Kiran
Naming files
The convention in Yii is to name the view files:
index.php: a full view
_dialog1.php: a partial view (included from another view)
Sub views
Then you can include partial views with CController::renderPartial():
$this->beginWidget(...);
$this->renderPartial('_dialog1', array('var1' => 23, 'var2' => "var"));
$this->endWidget(...);
Factorizing code
This should make your source node much lighter. But I suggest you go farther and avoid duplicating all those widget calls. To do this, you should define a structure for your dialog parameters and loop over it. Something like:
$dialogs = array(
'mydialog1' => array(
'file' => '_dialog1',
'options' => array('title' => "My title 1",),
),
'mydialog2' => array(
'file' => '_dialog12,
'options' => array('title' => "My title 2",),
),
);
$defaultOptions = array(
'autoOpen' => false,
'modal' => true,
);
foreach ($dialogs as $id => $dialog) {
$this->beginWidget(
'zii.widgets.jui.CJuiDialog',
array(
'id' => $id,
'options' => CMap::mergeArray($defaultOptions, $dialog['options']),
)
);
// ... include partial view ...
This factorization will make your code more compact, but it will above simplify the future changes. Using data structures to avoid code duplication is a well-known practice.
AJAX
Lastly, if you really want the partial views to be loaded dynamically, that means you have to use AJAX. Be careful, because your page may be less reactive from a user point of view. If all your forms amount to a few Kb of HTML, then there's no nedd for AJAX. But if you go this way, then you'll need to:
Add an CJuiDialog containing just <div id="dialog-ajax"></div>.
Create another action that will apply renderPartial() on the dialog views.
Replace the content of the previous foreach loop with code that writes JS like function dialog1() {jQuery("#dialog-ajax").load(...);}. You'll need to hack if you want to change dynamically the widget title.
Bind some events (clicks) to these JS functions.
Another way would be to make your aJAX action render a full CJuiDialog, it might be simpler and avoid JS hacks. Anyway, I'm not sure you really need AJAX.
My answer
//In controller
public function actionOpenDialog1()
{
$data = array();
$this->renderPartial('_dialogContent1', $data, false, true);
}
public function actionOpenDialog2()
{
$data = array();
$this->renderPartial('_dialogContent2', $data, false, true);
}
//In index.view
'#data'));
echo CHtml::ajaxButton ("Open second dialog", CController::createUrl('dialogTesting/openDialog2'),array('update' => '#data'));
?>
//_dialogContent1.php
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog1',
'options'=>array(
'title'=>'Dialog box 1',
'autoOpen'=>true,
'modal'=>true,
),
));
echo 'first dialog content here';
$this->endWidget('zii.widgets.jui.CJuiDialog');
?>
//_dialogContent2.php
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id'=>'mydialog1',
'options'=>array(
'title'=>'Dialog box 1',
'autoOpen'=>true,
'modal'=>true,
),
));
echo 'first dialog content here';
$this->endWidget('zii.widgets.jui.CJuiDialog');
?>
You can simply include one empty CJuiDialog in your page, and when it needs to be displayed load the contents with jQuery AJAX (load is the simplest, and probably enough) that returns a render of the appropriate view before opening the dialog.