I am new to CakePHP and I am trying to figure you how to make an asynchronous call from a CakePHP view to a function in the controller. I would like the controller function to return a string and have the view display this string. I would also like to to do this without using helpers. I have been trying to find examples on the web but have been unable to do so. Does anyone have a simple example? I am also using jQuery.
Thanks
CakePHP has a built-in JS Helper to help write aJax functions. The only catch is to include jquery in your head or cake will throw jQuery errors. Heres more information http://book.cakephp.org/2.0/en/core-libraries/helpers/js.html
Your Form:
<?php
echo $this->Form->create('User', array('default'=>false, 'id'=>'YourForm'));
echo $this->Form->input('username');
echo $this->Form->submit('Check Username');
echo $this->Form->end();
?>
The Ajax Function: ('update'=>'#na') is the id of the element you want to update in your view.
<?php
$data = $this->Js->get('#YourForm')->serializeForm(array('isForm' => true, 'inline' => true));
$this->Js->get('#YourForm')->event(
'submit',
$this->Js->request(
array('action' => 'checkUsername', 'controller' => 'user'),
array(
'update' => '#na',
'data' => $data,
'async' => true,
'dataExpression'=>true,
'method' => 'POST'
)
)
);
echo $this->Js->writeBuffer();
?>
The Function in User Controller
function checkUsername(){
$this->autoRender = false;
$username = $this->User->find('first', array('conditions'=>array('User.username'=>$this->request->data['User']['username'])));
if ( $username == true )
echo 'Username is taken';
else
echo 'Username is not taken';
}
EDIT**
*If you want to use jQuery to do this and not the CakePHP Helper you can use aJax to call an action, then update your element like below*
$('#element').on('click', function() {
$.ajax({
url : '/controller/action',
type: 'POST',
success : function(response){
$('#elementToUpdate').html(response);
}
});
}
});
In your Controller Action you can return the "string" you would like to show in the view
function action(){
$string = 'Show this in the view';
return $string;
}
The above example would be executed when you "Click" an element with an id of "element" then upon "Success" would change element with id of "elementToUpdate" to the String "Show this in the view" Since it was returned from the controller action.
Related
I am using kartik select2 widget in my yii2 basic app. now i have to display province names in select2 widget on ajax call. It is working fine if i put it directly in form. however not working with ajax call.
Here are my form fields:
<?= $form->field($model, 'role')->dropDownList(
ArrayHelper::map(SubAdminRoles::find()->all(), 'id', 'role_name'),
[
'prompt' => 'Select Role',
'onchange' => '
if($(this).val() != 3) {
$( "#user_area" ).html("showLoading");
$.post( "fetch-area-list?id='.'"+$(this).val(),
function(data) {
$( "#user_area" ).html(data);
})
}'
]
) ?>
<div id="user_area">
</div>
And here is my action code
public function actionFetchAreaList($id) {
// $this->layout = 'none';
$data = [];
if($id == 1) {
$provinceList = \app\modules\adminpanel\models\ProvinceMaster::findAll(['status' => 1, 'is_deleted' => 0]);
foreach($provinceList as $obj) {
$data[$obj['id']] = $obj['province_name'];
}
//print_r($data);
//exit;
} else if($id == 2) {
$subDistrictList = \app\modules\adminpanel\models\SubDistrictMaster::findAll(['status' => 1, 'is_deleted' => 0]);
foreach($subDistrictList as $obj) {
$data[$obj['id']] = $obj['sub_district_name'];
}
}
echo '<label class="control-label">Select Province</label>';
echo Select2::widget([
'name' => 'state_2',
'value' => '1',
'data' => $data,
'options' => ['multiple' => true, 'placeholder' => 'Select Province']
]);
exit;
}
now when i try to get it through ajax i comes with display:none property so i am not able to show my select2 box.
I Also tried changing display:none to display:block in select2 class. In that case i got the select box, but is simple html multiple select box not select2 widget.
How to get it from controller using ajax call?
Thanks in advance.
It is bad practice to render html inside action.
In your case widget requires related JS for initialization. But it will not include in your response.
Move all your html to view area-list and render using following code:
public function actionFetchAreaList($id) {
$this->layout = false;
// ... preparing data
return $this->renderAjax('area-list', [
// ... some view data
]);
}
Method renderAjax renders a named view and injects all registered JS/CSS scripts and files. It is usually used in response to AJAX Web requests.
I also have similar project like this.
I have 2 combobox (using select2). When select a district from the first combobox. It will call an ajax request to get province list and fill into the second combobox.
Here is my solution:
Using Select2 widget as normally in form
Using javascript to call ajax request and change data of the second combobox.
My controller response data in json format.
$('#district-selector').on('change', function() {
var districtId = $(this).val();
var url = $(this).attr('tb_href');
$('#province-selector').html('');
$.get(
url,
{
city_id: districtId
},
function(response) {
if (response.error == 0 && response.data.length) {
$('#province-selector').append(new Option('', ''));
$.each(response.data, function() {
console.log(this.id + '--' + this.title);
var newOption = new Option(this.title, this.id);
$('#province-selector').append(newOption);
});
}
$('#province-selector').trigger('change');
}
);
});
Demo: demo link
I want to post data to a controller in CakePHP, but posting with JQuery always results in"POST http//localhost/SA/myController/editUserData/1 400 (Bad Request)" error and I can't figure out why.
In my view I have the following method, that posts the data to the controller page
$scope.saveUser = function() {
$.ajax({
type: 'POST',
url: '<?php echo Router::url(array(
'controller' => 'myController',
'action' => 'editUserData',
0 => $userInfo['user']['id'],));?>',
data: { email: 'cabraham#delhi.k12'},//"my edited data for example"
success: function (data) {
alert(data);
}
});
My controller method looks like this:
public function editUserData($id) {
if($this->request->is('post') || $this->request->is('put')) {
$this->AcsaUser->save($this->request->data('email'));//edit and save the new data
echo 'ok';
}
}
Any ideas??
Two things that may be throwing it.
(1) Cake's built-in security - so exempt the method from it:
In AppController.php
public function beforeFilter() {
$this->Security->unlockedActions = array('editUserData')
}
(2) Decide how you want your editUserData method to render the view, if you're echo'ing out an 'ok' it will still pull in the default layout and look for an editUserData.ctp in the view (and cause an error if it doesn't find the file), so
To not render anything, ie a .ctp view file and the default layout.. in the editUserData($id), add
$this->autoRender = false;
To only render the view file and not the layout:
$this->autoLayout = false;
......Then lastly I wound just add this parameter in the ajax call :
dataType : 'html' // (or JSON?)
I'm having troubles getting content displayed on page load using ajax. The ajax is calling the right action in the respective controller. The first part of the action code where i update the database is working fine. But the part where i'm calling renderPartial is not working.
**EDIT***
Ok here is the controller action ::
public function actionUpdateProductData() {
Yii::import('application.components.DataScraper.*');
require_once('GetProductData.php');
$productRealTime = RealTime::model()->findAll();
if (count($productRealTime) === 0) {
$symbolData = new GetProductData();
$symbolData->getAmazonProductData();
} else {
echo CJSON::encode( array(
'status' => 'OK',
'div' => $this->renderPartial('_productDataGrid', array(
'model' => $productRealTime),
true, true ),
));
}
}
The if part is working fine. But the else portion is not working.
Here is the view index.php::
<?php
/*
* Include the ajax Stock update script
*
*/
$baseUrl = Yii::app()->baseUrl;
$cs = Yii::app()->getClientScript();
$cs->registerScriptFile($baseUrl . '/js/ajaxProductDataUpdate.js');
?>
<div>
<hr>
<ul class="breadcrumb">
<li>
Home <span class="divider">/</span>
</li>
<li>
Product Info
</li>
</ul>
<hr>
</div>
<div class="span-10">
<div id="section2">
</div>
</div>
Here is the partial view file _productDataGrid.php
<?php
$this->widget('bootstrap.widgets.TbGridView', array(
'id' => 'real-time-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'id',
'name',
'category',
'price'
'rating'
array(
'class' => 'bootstrap.widgets.TbButtonColumn',
),
),
));
?>
And here is the jQuery file which is making the ajax request
var productParameters = {
ajaxUpdate: function() {
$.ajax({
url: "/ProductAnalysis/index.php/realTime/updateProductData",
type: "GET",
dataType:"json",
error: function(xhr, tStatus, e) {
if (!xhr) {
alert(" We have an error ");
alert(tStatus + " " + e.message);
} else {
alert("else: " + e.message); // the great unknown
}
},
success: function(data) {
$.fn.yiiGridView.update('real-time-grid', {
data: $(this).serialize()
});
}
});
}
};
$(document).ready(function() {
productParameters.ajaxUpdate();
});
Upon loading the page /realTime/index.php i'm getting an error which says
else:
undefined
Obviously the ajax call is failing, but i don't know how will i fix it. Also in Firebug, the echo date() function in the controller is working, but the Gridview is not working.
Please provide some help on how to solve this. Let me know where i'm doing wrong. I can't seem to make any headway around this.
Thanks in advance,
Maxx
Your actionUpdateStockData() is echoing the date before the actual JSON content is encoded. As a result you're not transmitting correct JSON, and XHR will fail.
Remove the echo date ... line and you should be fine. And as you're just at it - you should add some response for the case where count(RealTime::model()->findAll()) === 0.
Well it seems that the gridview widget won't work with findall(). So i changed the dataprovider to simple model and it works now.
Here is the working code ::
public function actionUpdateStockData() {
Yii::import('application.components.DataScraper.*');
require_once('GetStockData.php');
$productRealTime = new RealTime();
if (count($productRealTime->model()->findAll()) === 0) {
$symbolData = new GetproductData();
$symbolData->getAmazonProductData();
} else {
echo CJSON::encode( array(
'status' => 'success',
'div' => $this->renderPartial('_productDataGrid', array(
'model' => $productRealTime),
true, true ),
));
}
}
I am working with Js helper to help me replace the contents of a "div" in my index.ctp with contents of another file changeto.ctp .
This application has a few check boxes with corresponding images. When I select a checkbox, the setting is stored in the database and the corresponding image is to be displayed. Similarly when unchecked, it is to be removed from the database and the image should disappear.
I am using the Js helper for this :
my changeto.ctp is subset of my index.ctp and has access to same variables and loads without any problems (except that any click/change events are just not responded to. The names of the Ids of the checkboxes are exactly same).
My index file contains the following Js code at the bottom of all Html:
$this->Js->get($change_checkbox1 )->event('click',
$this->Js->request(array(
'controller'=>'testing',
'action'=>'changeto'
), array(
'update'=>'#changeDiv',
'async' => true,
'method' => 'post',
'dataExpression'=>true,
'data'=> $this->Js->serializeForm(array(
'isForm' => false,
'inline' => true
))
))
);
This is generating the following Jquery/javascript
$(document).ready(function () {$("#ck0_1_8").bind("click", function (event) {$.ajax({async:true, data:$("#ck0_1_8").closest("form").serialize(), dataType:"html", success:function (data, textStatus) {$("#changeDiv").html(data);}, type:"post", url:"\/mytest-site\/options\/testing"});
return false;});
}
My controller has a function called
changeto()
{
$this->layout = 'ajax';
}
the first time I click on this the checkbox this works without any problem. However any subsequent clicks don't work. The javascript is not even being called.
My questions are :
Any ideas ?
Will it help if I somehow ask cakephp to use on() / live() insead of bind() .If yes how ?
Thanks!!
Solved this by editing a cakephp core file:
lib/Cake/View/Helper/JqueryEngineHelper.php
line 184:
change
return sprintf('%s.bind("%s", %s);', $this->selection, $type, $callback);
to
return sprintf('%s.live("%s", %s);', $this->selection, $type, $callback);
cake version 2.2.2
You have to use on instead of bind as they suggest in this question .live() vs .bind()
That's what I did was create my own Helper on View/Helper add the following method:
public function onEvent($selector, $type, $callback, $options = array()) {
$defaults = array('wrap' => true, 'stop' => true);
$options = array_merge($defaults, $options);
$function = 'function (event) {%s}';
if ($options['wrap'] && $options['stop']) {
$callback .= "\nreturn false;";
}
if ($options['wrap']) {
$callback = sprintf($function, $callback);
}
return sprintf('$(document).on("%s", "%s", %s);', $type, $selector, $callback);
}
And use that method from my views:
echo $this->MyJs->onEvent('#creditcards', 'change', $eventCode, array('stop' => false));
I want to create a registration form where I want to use jquery and also the CI validation library.
How can I do this? Upon submitting the form, I want the control to go to the validation library using jquery and return the response. Then, if all is well, I want the "form is submitted" message to be displayed on that page itself.
Edit:
here are my codes.
VIEW
<?php $this->load->view('template/header'); ?>
<div id="add_form">
<h2>Add New Category</h2>
<?php echo form_open('abc/abc_category_controller/add_new_category'); ?>
<?php
$category_data = array(
'name' => 'category_name',
'id' => 'category_name',
'value' => set_value('category_name'),
'maxlength' => '15'
);
$unit_data = array(
'name' => 'unit',
'id' => 'unit',
'value' => set_value('unit'),
'maxlength' => '10'
);
?>
<p><label for="name">Name: </label><?php echo form_input($category_data); ?></p>
<p><label for="unit">Unit: </label><?php echo form_input($unit_data); ?></p>
<p><?php echo form_submit('submit', 'Submit','id="submit"'); ?></p>
<?php echo form_close(); ?>
<?php echo validation_errors('<p class="error">'); ?>
</div><!--end add new category-form-->
<div id="success_msg">
</div>
<?php $this->load->view('template/footer') ?>
Controller- add_new_category
<?php
class Stocks_category_controller extends CI_Controller{
function add_new_category()
{
//$this->load->helper('form');
$this->load->library('form_validation');
$data['title'] = 'Add a new category';
$this->form_validation->set_rules('category_name', 'Category Name', 'required');
$this->form_validation->set_rules('unit', 'Unit', 'required');
if ($this->form_validation->run() === FALSE)
{
$this->load->view('abc/add_new_category');
}
else
{
$category_name = $this->input->post('category_name');
$unit = $this->input->post('unit');
$this->load->model('abc/abc_category_model');
$insert_id=$this->abc_category_model->add_new_category($category_name
,$unit);
if($insert_id!=NULL)
{
$this->load->view('template/success',$data);
}
else{
$data['error_msg']='Error Occurred';
$this->load->view('template/template',$data);
}
}
}
function success(){
$data['success_msg']='New Category Successfully Created';
$this->load->view('template/success',$data);
}
}
And finally the model.
function add_new_category($category_name,$unit){
$data = array(
'abc_category_name' => $category_name ,
'unit' => $unit
);
$this->db->insert('abc_category', $data);
$insert_id=$this->db->insert_id();
return $insert_id;
}
}
What I want is that when I submit the form, jquery validation should take place. And if, all is well then the SUccessful message be displayed using ajax only and page should not reload.
Also, please tell me is it possible to use jquery validation and CI validation library both and maintaining ajax at the same time?
To implement both client side and server side validations you need to use the jquery validation plugin.
So, you need to include:
jQuery javascript library
jQuery validation plugin
Load the javascript with
<script type="text/javascript" src="<?php echo base_url();?>js/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="<?php echo base_url();?>js/jquery.validate.js"></script>
<script type="text/javascript" src="<?php echo base_url();?>js/jquery.validate-rules.js"></script>
then setup your form:
$attr = array('id' => 'demo_form');
echo form_open('demo/validation_example', $attributes);
and validate your form like:
$(document).ready(function() {
$("#demo_form").validate({
rules: {
name: {
required: true
},
...
},
messages: {
name: {
required: "Name required",
},
},
errorElement: "span",
errorPlacement: function(error, element) {
error.appendTo(element.parent());
}
});
});
And use the server side validation too, because it is not a good practice to use only client side validation.
For more help, you can find a good tutorial about
how to implement the jquery validation with codeigniter
and working demo
Where you can check jquery validation, and by disabling the javascript of your browser, you can check codeigniter server side validations done with php.
I think you have to use jquery ajax for this.
First you need to post your data. Set your submit button to fire jquery ajax like this.
$.ajax({
type: "GET",
url: "/controller/validate", //your controller action handler
dataType: "json",
data: {name:value, name: value}, //set your post data here
cache:false,
success: function(data){
//handle the callback response
if(data.success)
alert("Success");
else
alert(data.errors);
}
});
Next in your controller action, validate your post data using codeigniter validator.
If it fails the validation, you need to send the errors back to your js callback function using json_encode in your view. Set the data error like this,
In your view (the view you will set for /controller/validate), do something like this
$data['errors'] = validation_errors();
echo json_encode($data);
You will have the errors back to you without refreshing the page.
When the validation is success, just set a $data['success'] and use echo json_encode using if statement so that only success data will be retrieved. I have set the ajax script callback.