The goal is to set a variable (stored into AppController.php) whenever the user select a new value from a list.
nav-top.ctp
<?= $this->Form->create(false, ['id' => 'ajaxForm']) ?>
<?= __('Year') ?>
<?= AppController::year(); ?> <---- just for test
<select name="currentYear">
<option value="2020">2020</option>
<option value="2021" selected>2021</option>
</select>
<?= $this->Form->end(); ?>
...
<?php $this->append('script'); ?>
<script type="text/javascript">
$("#currentYear").on("change", function() {
let ajaxdata = $('#ajaxForm').serializeArray();
$.ajax({
type: "POST",
url: "app/setYear",
data: ajaxdata,
success: function(data) {
$(document.body).html(data);
},
error: function (xhr, status, error) {
$(document.body).html(error);
}
});
});
</script>
<?php $this->end(); ?>
AppController.php
class AppController extends Controller
{
private $_year = 2021;
...
public function year()
{
return new Date($this->_year . '-01-01');
}
public function setYear()
{
$this->_year = $this->request->getData('currentYear');
return $this->redirect($this->referer());
}
}
When I change the current item in the select the POST is correctly fired and I get the new value inside the setYear() method.
Then the page reloads, as expected. But all the queries that rely on the year() function, i.e.:
public function index()
{
$this->paginate = [
'contain' => [
'Customers',
'ProformaStates',
'ItemProformas' => ['ItemDeliveryNotes' => ['DeliveryNotes']]
],
'conditions' => [
'proforma_date >=' => AppController::year()
],
'order' => ['proforma_number' => 'desc']
];
$proformas = $this->paginate($this->Proformas);
$this->set(compact('proformas'));
}
still use the old value, like it has not changed.
Furthermore, if I enable the test line above (to printout in the reloaded page the actual value of year) I get the following error:
Using $this when not in object context
Related
Solution
Since the php is not going to be re-executed someone told me to use a return value from the controller as a parameter of the "success" function into ajax's request.
**CONTROLLER**
if(Yii::$app->request->isAjax){
$query = new \yii\db\Query();
$rows = $query->select([])
->from('task')
->where('date(start) <= date(\'' . $this->request->getBodyParam('start_date') . '\')')
->all();
$items = "";
foreach (ArrayHelper::map($rows,'id','name') as $key => $value)
$items .= '<option value="'. $key .'">'. $value .'</option>\n';
//<option value="16">test</option> example output
echo $items;
}
VIEW
$this->registerJs("$('#task-start').on('change',function(){
$(\".btn, .btn-success\").prop(\"disabled\",true);
var date = $(this).val();
$.ajax({
url: \"".Yii::$app->request->baseUrl."/task/create\",
data: {start_date: date},
type: \"post\",
success: function(dependency_options){
//dependency_options contains what's returned with 'echo' from the controller
$('#task-dependencies').find('option:not(:first)').remove();
$('#task-dependencies').append(dependency_options);
$(\".btn, .btn-success\").prop(\"disabled\",false);
},
error: function () {
console.log('ERROR')
$(\".btn, .btn-success\").prop(\"disabled\",false);
}
});
});",View::POS_READY);
Hoping this might help someone.
My problem
I have a DropDownList with no elements and I want to update the list with elements coming from a Query whenever the user changes the datepicker without refreshing the page.
An example of something similar to this would be those forms in which you type your region and it changes the dropdownlist based on what you choose on the field before.
Maybe I'm using a wrong approach to this cause I'm still new to the MVC model and the Yii2 framework so any idea on how to change it's well appreciated.
What I've tried
With this code below as is I had issues cause after the form was created the first time I could not change it later, I've tried to change the html, as you can see from the script in the success ajax function of the View but the script was executed only the first time the view was loaded.
Controller calling the render
public function actionCreate()
{
$model = new Task();
if(Yii::$app->request->isAjax){
$query = new \yii\db\Query();
$rows = $query->select([])
->from('task')
->where('date(start) <= date(\'' . $this->request->getBodyParam('start_date') . '\')')
->all();
$items = ArrayHelper::map($rows,'id','name');
$model->setItems($items);
return $this->renderPartial('_form',[
'partial' => true,
'model' => $model
]);
}
else if ($this->request->isPost) {
..unnecessary code..
return $this->render('create', [
'model' => $model,
]);
}
The view "create" basically renders the view _form (autogenerated by Gii)
View _form
<?php
//Here it takes the items in the model
//(which will contain the new items to append after ajax call)
$objects = json_encode($model->getItems() ?? []);
var_dump($model->getItems() ?? []);
//Here whenever the datepicker is change will fire ajax request
$this->registerJs("$('#task-start').on('change',function(){
var date = $(this).val();
var items = ". json_encode($model->getItems() ?? []) .";
alert(items);
$.ajax({
url: \"".Yii::$app->request->baseUrl."/task/create\",
data: {start_date: date},
type: \"post\",
success: function(){
//$('#task-dependencies').find('option:not(:first)').remove();
$.each(items, function(key, value) {
$('#task-dependencies')
.append($('<option>', { value : key })
.text(value));
});
},
error: function () {
console.log('ERROR')
}
});
});",View::POS_READY);
?>
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'start')->widget(\kartik\date\DatePicker::className(), [
...datepicker that triggers onChange...
]) ?>
//The dropdownlist that should dynamically change
<?= $form->field($model, 'dependencies')->dropDownList(
$model->getItems(),
['prompt' => 'Seleziona una o piĆ¹ dipendenze', 'multiple' => true, 'selected' => 'selected'] // options
);
?>
<?php ActiveForm::end(); ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery#3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">
You can use Pjax in, yii2:
Controller:
public function actionCreate() {
$model = new Task();
if( Yii::$app->request->post() && Yii::$app->request->isPjax ) {
$query = new \yii\db\Query();
$rows = $query->select([])
->from('task')
->where('date(start) <= date(\'' . $this->request->getBodyParam('start_date') . '\')')
->all();
$items = ArrayHelper::map($rows,'id','name');
$model->setItems($items);
// Alert widget renders a message from session flash.
// Yii::$app->session->setFlash('info', "Ok....");
return $this->renderAjax('_form',[ // Or renderPartial
'partial' => true,
'model' => $model
]);
}
// else if ($this->request->isPost) {
// ..unnecessary code..
return $this->render('create', [
'model' => $model,
]);
}
}
view
// submitEvent: The jQuery event that will trigger form handler. Defaults to "submit".
<?php Pjax::begin(['submitEvent' => 'change']); ?>
<?php $form = ActiveForm::begin([
'options' => ['data-pjax' => ''],
]);
// if(hasFlash ....){ // If used ...
// Yii::$app->session->getFlash('info')
// }
?>
<?= $form->field($model, 'start')->widget......
<?= $form->field($model, 'dependencies')->dropDownList(
$model->getItems(),
// ......
<?php ActiveForm::end(); ?>
<?php Pjax::end(); ?>
Tip: The above codes will meet your goal. But you can use the following code to change more and respond to Pjax events.
jQuery or JavaScript...
$(document).on('pjax:send', function() {
// code ... Example: $('#loading').css({"visibility":"visible"}); //show()
});
$(document).on('pjax:complete', function() {
// code ... // hide()
});
// Or use if Pjax::$submitEvent = 'submit'
// $(document).on('change', '#id', function(event) { $(this).submit(); });
If you still have problems, check the date format method in SQL and ....
Good luck.
I'm migrating my site from Yii to Yii2 and trying to make a basic loan calculator. was reading the documentation and some answers on stack that suggested to use validationUrl.
Below is my code i've done so far. but i can't get my form validation to work. It allows me to submit an empty form. and doesn't show any errors?
Also where do i put my calculations, in actionloanValidate() or actionIndex() ?
<?php
$form = ActiveForm::begin([
'id' => $model->formName(),
'enableAjaxValidation' => true,
'validationUrl' => Url::toRoute(['calculator/default/loan-validate'])
]);
?>
<?= $form->field($model, 'price') ?>
<?= $form->field($model, 'downpayment') ?>
<?= $form->field($model, 'rate') ?>
<?= $form->field($model,'yearloan')->dropDownList(
['1' => '1 (12 months)', '2' => '2 (24 months)');
?>
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary btn-block', 'tabindex' => '3']) ?>
<?php ActiveForm::end(); ?>
<script>
$(document).ready(function () {
var $form = $("#<?= $model->formName() ?>");
$form.on("beforeSubmit", function (event, messages) {
event.preventDefault();
$.ajax({
"type":"POST",
"url":$form.attr('action'),
"data":$form.serialize(),
"beforeSend":function( xhr ) {},
"dataType":"json",
"cache": true,
"success":function(data){
$("#totalLoanAmount").html(data.totalLoanAmount);
$("#monthlyInstallment").html(data.monthlyInstallment);
$("#loanCalcTable").html(data.loanCalcTable);
},
});
return false;
});
});
</script>
in my controller i have this
public function actionIndex() {
$model=new calculatorForm;
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
//DO CALCULATION HERE!!
}
return $this->render('loan-calculator', [
'model' => $model,
]);
}
public function actionloanValidate()
{
$model=new calculatorForm;
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
else {
return $this->renderAjax('loan-calculator', [
'model' => $model,
]);
}
}
my model rules are
public function rules() {
return [
//loan-calculator validation
[['price', 'downpayment', 'rate', 'yearloan'], 'required']
];
}
You are overriding the validation check by defining beforeSubmit in your javascript. You are also likely getting a cancelled ajax request as you need to call event.stopPropagation() after event.preventDefault()
If you are truly trying to achieve only required validation, don't use ajaxValidation.
On beforeSubmit validate the form
event.preventDefault();
event.stopPropagation(); //stops going to submit which will cancel your ajax
//a trick added to ensure validation is triggered on untouched fields
data = $form.data("yiiActiveForm");
$.each(data.attributes, function() {
this.status = 3;
});
$form.yiiActiveForm("validate");
if ($("#form").find(".has-error").length) {
return false;
}
$.ajax({
"type":"POST",
"url":$form.attr('action'),
"data":$form.serialize(),
"beforeSend":function( xhr ) {},
"dataType":"json",
"cache": true,
"success":function(data){
$("#totalLoanAmount").html(data.totalLoanAmount);
$("#monthlyInstallment").html(data.monthlyInstallment);
$("#loanCalcTable").html(data.loanCalcTable);
},
});
As well, the calculcation should be done in the actionIndex (the action of the form)
this is my view code working on auto complete , but its showing me 500 internal server error
I am not sure what is going wrong if i'm not using the right url or some other problem.
public function search(){
$this->loadComponent('RequestHandler');
if ($this->request->is('ajax'))
{
$name = $this->request->query['term'];
$resultArr = $this->Invoices
->find()
->where(
['Invoices.name LIKE' => ($name . '%')],
['Invoices.name' => 'string']);
$resultsArr = [];
foreach ($resultArr as $result)
{
$resultsArr[] = ($result['name']);
}
$this->set('resultsArr', $resultsArr);
// This line is what handles converting your array into json
// To get this to work you must load the request handler
$this->set('_serialize', ['resultsArr']);
}
}
this is my view code:
<?php echo $this->Form->input('name', ['type' => 'text']); ?>
$.ajax({
type: "POST",
url: "<?php echo Router::url(array('controller' => 'Clinics','action' => 'search')); ?>",
success: function(response) {
$("#name").autocomplete({ source: response });
}
});
According to the error message in your comments, the loadComponent method isn't defined.
I would suggest loading the RequestHandler component near the top of your controller, for example:
class ClinicController extends AppController {
public $components = array('Request');
There is also another syntax for loading components on the fly:
$this->Request = $this->Components->load('Request');
Reference: https://book.cakephp.org/2.0/en/controllers/components.html
I am trying to implement a search module by using AJAX.
There is an index.ctp file in my Items Controller and I have linked my index.ctp file of Items to my search.ctp file which is present under Pages controller as below:
<li><?= $this->Html->link(__('Search Products'),['controller'=>'Pages','action' => 'search']) ?></li>
For the search.ctp pages the URL displayed is :
http://onlineelectronic.com/pages/search
In my search.ctp file the code is as follows:
<head>
<title> Search Results</title>
<?php echo $this->Html->script('//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js', array('inline' => false));?>
<script type="text/javascript">
$(document).ready(function() {
$("#Submit1").click(function () {
$.ajax({
type: 'post',
url: '/Items/searchData",
data: {
name: "search"
},
beforeSend: function(){
$("#resultField").html("Loading...");
},
success: function(response) {
jQuery('#resultField').val(response);
},
error: function(response, error) {
alert("Search :Error"+response.error());
},
dataType: 'json',
global: false
});
return false;
});
});
</script>
</head>
<div>
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Search Item') ?></legend>
<?php
echo $this->Form->input('search',['label'=>'Search']);
?>
</fieldset>
<?= $this->Form->button('Search Items',['label'=>'Submit1']); ?>
<?= $this->Form->end() ?>
</div>
<div id="resultField">
</div>
In my ItemsContoller page the searchData method is implemented like this:
class ItemsController extends AppController
{
public $helpers = ['Form', 'Html', 'Time'];
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function search(){
//dummy
}
/**
*obtains search result for a given string.
*/
public function searchData()
{
$this->layout = 'ajax';
echo "here";
$search_data=[];
var_dump($search_data);
//$search_results = [];
if ($this->request->is('post')) {
$search_data= $this->request->data;
$search_data=implode("|",$search_data);
$search_results = $this->Items->find('all', array('conditions'=>array('Items.itemName LIKE'=>"%$search_data%")));
if(!empty($search_results)) {
$this->set(compact($search_results));
$this->set('_serialize',array('search_results'));
return json_encode($search_results);
}
}
}
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['index', 'view','search','searchData']);
}
}
My issue is that the SearchData method is not being called and I am not getting any javascript errors also.How do i make sure that the method gets called on pressed after pressing the button.Is it due to the url in json?
I see 2 possible problems. First in ItemsController, you have to allow searchData method
// change this line
$this->Auth->allow(['index', 'view','search']);
// to this
$this->Auth->allow(['index', 'view','searchData']);
also make sure, that you have proper jQuery selector
// try to change this line
<?= $this->Form->button('Search Items',['label'=>'Submit1']); ?>
// to this
<?= $this->Form->button('Search Items',['id'=>'Submit1']); ?>
Edit: make more corrections to javascript:
Data passed with ajax should be double quoted
data: {
name: "search"
},
add return false; to the end of click handler, so you prevent submiting form and reloading page
Also note that you must have template for searchData in Template/Items/search_data.ctp
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 ),
));
}
}