phalcon dependent dropdown list population with jquery - php

in phalcon framework i want to populate dependent drop down category list. But i'm facing some problems in my code:
#1. On select category list -> subcategory list shows: undefined (it's not populating options with value)
#2. if database have no data console.log shows: Undefined variable: resData in my controller
#3. if select category which value is '0' its not disabling the subcategory list again
What i'm doing wrong in my code?
[Module.php]
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;
$di->setShared('view', function () use ($config){
$view = new View();
$view->setViewsDir(APP_PATH . $config->appB->viewsDir);
# Register Volt Template
$view->registerEngines(array(
".volt" => function($view, $di) use ($config) {
$volt = new Volt($view, $di);
$volt->setOptions(
array(
'compiledPath' => APP_PATH . $config->appB->cacheDir,
'compiledExtension' => '.php',
'compiledSeparator' => '_',
'compileAlways' => true,
'autoescape' => false,
'stat' => true
)
);
$compiler = $volt->getCompiler();
$compiler->addFunction('strtotime','strtotime');
return $volt;
}
));
return $view;
});
[CONTROLLER]
public function entryAction()
{
$formAction = 'backend/index/insert';
$this->view->setVar('action',$formAction);
$this->view->product_title = '';
$this->view->product_price = '';
$this->view->product_keyword = '';
$this->view->product_image = '';
$this->view->product_desc = '';
$category = Categories::find();
$this->view->setVar('categories',$category);
$this->view->pick("index/entry");
}
public function getSubcategoryAction()
{
$id = $this->request->getPost('id');
$data = Subcat::findBycategory_id($id);
$resData = array();
foreach($data as $result)
{
$resData[] = array('id' => $result->id, 'category_id' => $result->category_id, 'subcategory' => $result->subcategory_name);
}
echo(json_encode($resData));
//$this->view->setVar('subcategory',$resData);
}
[ENTRY VOLT]
Category:<select name="category" id="category">
<option value="0">Choose Category ...</option>
{% for category in categories %}
<option value="{{category.id}}">{{category.categoryname}}</option>
{% endfor %}
</select><br/>
sub-Category:<select name="subcategory" id="subcategory" disabled="disabled"><option value="0">Choose Sub-Category ...</option></select>
<br/>
Products:<select name="products" id="products" disabled="disabled"><option value="0">Choose a Product ...</option></select>
<br/>
[JQUERY]
$("select[name='category']").on("change", function(e){
e.preventDefault();
var value = $(this).val();
$("select[name='subcategory']").attr("disabled", false);
$.ajax({
type: "POST",
url: "http://localhost/shopping/backend/index/getSubcategory",
data:'id='+value,
}).done(function(response){
$("#subcategory").not(":first").remove();
response = JSON.parse(response);
response.forEach(function(value){
$('#subcategory').append('<option value="'+value.id+'">'+value.subcategory+'</option>');
});
}).fail(function(){
console.log('error: Please reload page and try again!');
}).always(function(){
console.log('Complete:');
});
});

Notice that in the first lines in your controller you are disabling the views, so Volt is never processed. As you are working now, jQuery only receives JSON results, so you are appending JSON rather than Volt.
You have to choose a path: either use Volt, in which case you have to remove lines 1 in both actions and process the view with parameters, or you keep sending JSON data to jQuery and you set it up properly for processing JSON response (check this answer )
In your case, getSubcategoryAction() would look like:
public function getSubcategoryAction()
{
//$this->view->disable(); //Replaced by:
$this->view->setRenderLevel(
View::LEVEL_ACTION_VIEW
);
$id = $this->request->getPost('id');
$data = Subcat::findBycategory_id($id);
foreach($data as $result)
{
$resData[] = array('id' => $result->id, 'category_id' => $result->category_id, 'subcategory' => $result->subcategory_name);
}
$this->view->setVar('categories', $resData);
}
This is assuming that you have set Volt as your render engine in your DI and your Volt template corresponds to ../app/views/index/getSubcategory.phtml, i.e.:
<?php
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Volt;
// Register Volt as a service
$di->set(
'voltService',
function ($view, $di) {
$volt = new Volt($view, $di);
$volt->setOptions(
[
'compiledPath' => '../app/compiled-templates/',
'compiledExtension' => '.compiled',
]
);
return $volt;
}
);
// Register Volt as template engine
$di->set(
'view',
function () {
$view = new View();
$view->setViewsDir('../app/views/');
$view->registerEngines(
[
'.volt' => 'voltService',
]
);
return $view;
}
);
The Volt registration code was copied from: https://docs.phalconphp.com/en/3.4/volt Modify the directories according to your App structure.

Follow This and just update your code...
[JQuery]
$("select[name='category']").on("change", function(e){
e.preventDefault();
var value = $(this).val();
if(value === '0'){$("select[name='subcategory']").attr("disabled", true); $("select[name='products']").attr("disabled", true);}else{$("select[name='subcategory']").attr("disabled", false);}
$.ajax({
type: "POST",
url: "http://localhost/shopping/backend/index/getSubcategory",
data:'id='+value,
}).done(function(response){
$("#subcategory").find('option').not(":first").remove();
$("#products").find('option').not(":first").remove();
response = JSON.parse(response);
response.forEach(function(value){
$('#subcategory').append('<option value="'+value.id+'">'+value.subcategory+'</option>');
});
}).fail(function(){
console.log('error: Please reload page and try again!');
}).always(function(){
console.log('Complete:');
});
});

Related

Problem updating dynamically ActiveForm values with Ajax + RenderPartial/Ajax

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.

Yii2 add tag to blog post

I am totally confused about how should i save tag_id and post_id in the table when I still don't have the post_id. I made dropdownlist with options from database table
<?= Html::activeDropDownList($model, 'post_id', $ddItems, ['class' => 'form-control dd-list', 'prompt' => '']) ?>
That list contains tags(PHP, Java etc.).
User can have up to 3 tags on post. I tried to pass tags through GET and take in the controller with \Yii:$app->request->get(['id']) but without result.
my jQuery:
var ddList = $('.dd-list');
var tagList = $('.tag-container');
ddList.on('change', function () {
var tagHolder = document.createElement('div');
tagHolder.setAttribute('class', 'tag-holder');
var selected = $('.dd-list option:selected').text();
tagHolder.setAttribute('id', selected);
console.log(selected);
if(tagList.find('div').length > 2){
alert('You can have most 3 tags!');
return false;
}
if(tagList.find('#'+selected).length){
return false;
}else{
tagHolder.append(selected);
tagList.append(tagHolder);
$.ajax({
type : 'GET',
dataType : 'text',
url : '../post/create?tag=' + selected
});
}
});
actionCreate :
public function actionCreate()
{
$model = new Post();
$id = \Yii::$app->user->identity->id;
$ddItems = ArrayHelper::map(Tag::find()->all(),'tag_id', 'tag_name');
if ($model->load(Yii::$app->request->post())) {
date_default_timezone_set('Europe/Berlin');
$param = \Yii::$app->request->get();
$model->user_id = $id;
$model->date_create = date('m/d/Y');
if($model->save())
{
$tag = \Yii::$app->request->get(['tag']);
return $this->redirect(['view', 'id' => $model->post_id]);
}
throw new \yii\db\Exception("Some problem with DB connection ocurred!");
} else {
return $this->render('create', [
'model' => $model,
'ddItems' => $ddItems
]);
}
}
I am getting error : undefined tag. I am trying to get only one tag (because still don't know how to pass more).
What is the right way to reach my goal? I am in total mess from about 1 day! Ton of thanks in advance!
EDIT
I tried to send that tag with on click event like this :
$('.create-post').on('click', function () {
var tag = $('input[name=chosen-tag]').val();
console.log(tag);
$.ajax({
type : 'GET',
dataType : 'text',
url : '../post/create?tag=' + tag
});
});
I could not understand your programming scenario, But..
Change
$tag = \Yii::$app->request->get(['tag']);
To
$tag = \Yii::$app->request->get('tag');

Use cron to fetch a view which contains jQuery content?

Is there a way I can set a cron to run a view on my Drupal site? I have a jQuery script on it which I would like to run once an hour.
I tried;
php http://mysite/myview
but I guess I am waaay off the mark. I get;
Could not open input file:
Here's the js which I am using on my rss feed;
// $Id
(function ($) {
Drupal.behaviors.tweetcast = {
attach: function (context, settings) {
$('div.tagspeak:not(.tags-processed)', context).addClass('tags-processed').each(function () {
var mynid, sinceid, updateinfo, sinceurl, author, tweethtml;
var currentsinceid = $('.currentsinceid').val();
var firstring = $(this).html();
var twostring = firstring.replace(/\s/g,'').replace(/^%20OR%20/gim,'').replace(/%20OR$/gim,'').replace(/%20OR%20%$/gim,'').replace(/%20OR%20$/gim,'').replace(/%$/gim,'').replace(/%20OR$/gim,'').toLowerCase();
var threestring = twostring.replace(/%20or%20/gim,'%20OR%20');
$(this).text(threestring);
var fourstring = threestring.replace(/%20OR%20/gim,' ');
var fivestring = fourstring.replace(/%23/gim,'#');
$(this).closest('.views-row').append('<div class="views-field views-field-replies"></div>');
tweethtml = $(this).closest('.views-row').find('div.views-field-replies').eq(0);
var twitter_api_url = 'http://search.twitter.com/search.json';
$.ajaxSetup({ cache: true });
$.getJSON( twitter_api_url + '?callback=?&rpp=1&q=' + threestring + '&since_id=' + currentsinceid, function(data) {
$.each(data.results, function(i, tweet) {console.log(tweet);
if(tweet.text !== undefined) {
var tweet_html = '<div class="tweetuser">' + tweet.from_user + '</div>';
tweet_html += ' <div class="sinceid">' + tweet.id + '</div>';
tweethtml.append(tweet_html);
}
});
});
$.fn.delay = function(time, callback){
jQuery.fx.step.delay = function(){};
return this.animate({delay:1}, time, callback);
}
$(this).delay(2000, function(){
title = $(this).closest('.views-row').find('div.views-field-title').eq(0).find('span.field-content').text();
link = $(this).closest('.views-row').find('div.views-field-path').eq(0).find('span.field-content').text();
author = $(this).closest('.views-row').find('div.tweetuser').eq(0).text();
mynid = $(this).closest('.views-row').find('div.mynid').text();
sinceid = $(this).closest('.views-row').find('div.sinceid').eq(0).text();
updateinfo = {sinceid: sinceid, mynid: mynid, author: author, title: title, link: link, tags: fivestring};
sinceurl = Drupal.settings.basePath + 'sinceid';
$.ajax({
type: "POST",
url: sinceurl,
data: updateinfo,
success: function(){}
});
});
});
}}
})
(jQuery);
And here is the php my module;
<?php
// $id:
function sinceid_menu() {
$items = array();
$items['sinceid'] = array(
'title' => 'sinceid',
'page callback' => 'sinceid_count',
'description' => 'sinceid',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
function sinceid_count()
{
$sinceid=$_POST['sinceid'];
$mynid=$_POST['mynid'];
$author=$_POST['author'];
$title=$_POST['title'];
$link=$_POST['link'];
$tags=$_POST['tags'];
db_update('node')
->expression('sinceid', $sinceid)
->condition('nid', $mynid)
->execute();
$sql = db_query("SELECT author FROM {authordupe} WHERE author = $author");
$result = db_query($sql);
$how_many_rows = $result->rowCount();
if($how_many_rows == 0) {
db_insert('authordupe')
->fields(array(
'author' => $author,
'title' => $title,
'link' => $link,
'tags' => $tags,
))
->execute();
}
}
The basic code to run a view in Drupal is
$view = views_get_view('<your_view_machine_name>');
$view->set_display('<optional_display_name>');
$view->execute();
That said, you say have a jQuery script that runs within a view. Seriously? What is your script doing? I'd say that jQuery it's best used when you want a script to be run at client's side. Can't you write php code to accomplish whatever you need and put it in a cron hook?

Show data from 2 tables + zend framework

I want to show data from my database into my table in html. (when i click on a link)
Table "births" fields:
district
year1999
year2000
year2001
year2002
year2003
year2004
year2005
year2006
year2007
year2008
year2009
Table "deaths" fields:
district
year1999
year2000
year2001
year2002
year2003
year2004
year2005
year2006
year2007
year2008
year2009
I will get the data from my database true an ajax call in javascript. I link to an action in my indexcontroller.
Javascript code:
$("#wijken ul li a").click(function(e){
district = ($(this).text());
loadTable(district);
});
function loadTable(district){
var param1 = district;
$.ajax({
url: 'index/getdata',
type: "POST",
data: {param1: param1},
dataType: 'json',
success: function(result)
{
var htmlContent = "";
// HOW CAN I PARSE THE DATA?
htmlContent += '</tbody></table>';
$('#tabel').html(htmlContent);
},
error: function(request, status, error){
alert(request.responseText);
}
});
}
My IndexController:
public function getdataAction()
{
// DISABLE VIEW
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
// WIJK CLICKED
$district = $this->_request->getParam('param1');
// GET THE BIRTHS/YEAR
$birthMapper = new Frontoffice_Model_BirthMapper();
$array = $birthMapper->read($district);
$this->_response->setBody(json_encode($array));
}
My BirthMapper:
public function read($wijk = null)
{
$table = $this->_dbTable;
$columns = array('wijk' => 'wijk',
'year1999' => 'year1999',
'year2000' => 'year2000',
'year2001' => 'year2001',
'year2002' => 'year2002',
'year2003' => 'year2003',
'year2004' => 'year2004',
'year2005' => 'year2005',
'year2006' => 'year2006',
'year2007' => 'year2007',
'year2008' => 'year2008',
'year2009' => 'year2009',
);
$select = $table->select()
->from($table,
$columns
)
->where('wijk = :wijk')
->bind(array(':wijk' => $wijk))
;
if ($row = $table->fetchRow($select)) {
return $row->toArray();
}
throw new Exception('The requested Births cannot be found');
}
Now I can handle the year1999,year2000,year2001,year2002,year2003,year2004,year2005,year2006,year2007,year2008,year2009 fields in my javascript as result.year1999. But how can I do this for multiple tables? (In javascript and controller)

Options not getting added again when the form is submitted +zendFramework

i have to dependent combo box
$this->addElement('Select', 'Category',array(
'label' => 'Category:',
'AutoComplete'=> true,
'multiOptions' => array('0' => '-Category-',$a->GetCategories(),'2' => '-Add category-'),
'required' => true ));
$this->addElement('Select', 'SubCategory',array(
'label' => 'Sub Category:',
'AutoComplete'=> true,
//'multiOptions' => array('0' => '-Select Category-'),
'required' => true ));
the second one is filled using ajax
<script type="text/javascript">
//for send data i'll use jquery library
$(document).ready( function(){
$('#Category').change(function() {
var message=$('#Category option:selected').text();
if (message != '') {
$.ajax({
type: "GET",
dataType : 'json',
url: 'http://localhost/EverTags1/Authentification1/public/Product/add',
async: false,
data:{"message" : message},
success: function (respond) {
var json=JSON.stringify(respond);
var objet = eval('(' + json + ')');
e=objet.length;
var str = "";
for ( var count = 0 ; count < e; count++ ) {
str += "<option value='" + count + "'>" + objet[count].name+ "</option>"
}
$("#SubCategory").empty().append(""+str);
}
});
}
});
});
</script>
The elements were loaded correctly in the second combobox. But when I submitted the content of the second combobox disappears. how can i make them displayed
you need to update multioptions after each ajax request. i used session to do that
public function getsubcategoriesAction()
{
if($this->_request->isXmlHttpRequest())
{
$session = new Zend_Session_Namespace('mySession');
$this->getRequest()->param('id',1)
$model = new Application_Model_DbTable_Subcategory();
$result = $model->getSubcategories($category);
// save the result to session
$session->result = $result;
$this->_helper->json($result);
}
}
and in the action that render the form
public function createAction()
{
//some code here
if($this->getRequest()->isPost()){
$session = new Zend_Session_Namespace('mySession');
$subCategory = $form->getelement('subCategory');
$subCategory->addMultiOptions($session->result); // get the result back from session
//some code here
}
}
you need also to enable sessions in you application.ini
resources.session.save_path = APPLICATION_PATH "/../data/session"
resources.session.use_only_cookies = true
resources.session.remember_me_seconds = 864000
Does adding the selected='selected' attribute to the first option of #SubCategory fix it?

Categories