I am using Yii Booster Yii extension for UI. I need to update the content of the tabs using ajax. Using below code I am able to get the content using renderPartial but instead I want to make ajax call and update the content.
$this->widget('bootstrap.widgets.TbTabs', array(
'type' => 'tabs',
'tabs' => array(
array('label' => 'Home', 'content' => $this->renderPartial('home', NULL, true), 'active' => true),
array('label' => 'Test', 'items' => array(
array('label' => 'Sub Tab 1', 'content' => $this->renderPartial('testpage', NULL, true)),
array('label' => 'Sub Tab 2', 'content' => 'some content ')
)),
)
)
);
Do I need to use jquery or something else to work with Yii Booster widgets ?. This is the first time I am using php/yii/extensions.
I am a bit confused so Yii Booster has integrated Bootstrap widgets so that they can be used with php. But for client side do I need to use jquery to manipulate bootstrap widgets ?
Are there any tutorials for using Yii Booster, I know the link to yii booster site, but there we just have simple examples nothing related to events, ajax.
Got this working. As #Sergey said I need to use 'shown' event. I gave ids to each tab to identify specific tab and load content. Here is the code
<?php $this->widget('bootstrap.widgets.TbTabs', array(
'id' => 'mytabs',
'type' => 'tabs',
'tabs' => array(
array('id' => 'tab1', 'label' => 'Tab 1', 'content' => $this->renderPartial('tab1', null, true), 'active' => true),
array('id' => 'tab2', 'label' => 'Tab 2', 'content' => 'loading ....'),
array('id' => 'tab3', 'label' => 'Tab 3', 'content' => 'loading ....'),
),
'events'=>array('shown'=>'js:loadContent')
)
);?>
We have 3 tabs, the content will be loaded using loadContent javascript function.
<script type="text/javascript">
function loadContent(e){
var tabId = e.target.getAttribute("href");
var ctUrl = '';
if(tabId == '#tab1') {
ctUrl = 'url to get tab 1 content';
} else if(tabId == '#tab2') {
ctUrl = 'url to get tab 2 content';
} else if(tabId == '#tab3') {
ctUrl = 'url to get tab 3 content';
}
if(ctUrl != '') {
$.ajax({
url : ctUrl,
type : 'POST',
dataType : 'html',
cache : false,
success : function(html)
{
jQuery(tabId).html(html);
},
error:function(){
alert('Request failed');
}
});
}
preventDefault();
return false;
}
Here we get the target, that should be the anchor element of the tab that we clicked, get the href value which should be the id that we configured. Decide the url based on this id, load content and update the div's. The id of div's are the same as href.
I haven't found way to load content of the initial active tab, there should be way to fire loadContent function, for now I have used renderPartial to get the content instead
I extended the TbTabs class with #Abdullahs example. It's tested with YiiBooster, but should work with other Bootstrap plugins as well.
Usage:
$this->widget(
'ext.ajaxTabs.widgets.ajaxTabs',
array(
'reload' => true, // Reload the tab content each time a tab is clicked. Delete this line to load it only once.
'tabs' => array(
array(
'label' => 'Static',
'content' => 'Static content'
),
array(
'label' => 'Static rendered',
'content' => $this->renderPartial('index', null, true),
),
// And finally AJAX:
array(
'label' => 'AJAX',
'content' => 'loading ....',
'linkOptions' => array('data-tab-url' => Yii::app()->createAbsoluteUrl('site/'))
)
),
)
);
/protected/extensions/ajaxTabs/widget/ajaxTabs.php:
<?php
Yii::import('bootstrap.widgets.TbTabs');
class ajaxTabs extends TbTabs
{
/**
* #var bool Reload the tab content each time the tab is clicked. Default: load only once
*/
public $reload = false;
public function run()
{
$id = $this->id;
/** #var CClientScript $cs */
$cs = Yii::app()->getClientScript();
$cs->registerScript(
__CLASS__ . '#' . $id . '_ajax', '
function TbTabsAjax(e) {
e.preventDefault();
var $eTarget = $(e.target);
var $tab = $($eTarget.attr("href"));
var ctUrl = $eTarget.data("tab-url");
if (ctUrl != "" && ctUrl !== undefined) {
$.ajax({
url: ctUrl,
type: "POST",
dataType: "html",
cache: false,
success: function (html) {
$tab.html(html);
},
error: function () {
alert("Request failed");
}
});
}
if(' . ($this->reload ? 0 : 1) . '){
$eTarget.data("tab-url", "");
}
return false;
}'
);
$this->events['shown'] = 'js:TbTabsAjax';
parent::run();
}
}
Its a new version to loadContent
function loadContent(e){
var targetUrl = e.target.getAttribute('data-tab-url');
var contentID = e.target.getAttribute('href');
$(contentID).load(targetUrl, function(){
$('.tabs').tabs(); //reinitialize tabs
});
e.preventDefault();
return false;}
Related
Right now when I update my plugin page the settings save and does what I want besides calling my action hook, test handler. Overall, the ajax request seems to be working and adding my options to the database but not calling my test handler function. Is it possible to post to the options.php and call a custom function to perform a few more things?
Here is my jQuery code that submits the form data to WordPress options.php
jQuery(document).ready(function($) {
$('#yacht-form').on( 'submit', function(event) {
console.log($("input#submit").attr("disabled","disabled"));
var ylocations = $('#ylocations').val();
if (ylocations == 0) {
$('.button-save').html('<p>Error! Empty Value.</p>').show().fadeIn();
$('.button-save').addClass('button-error');
$('.button-save').delay(5000).fadeOut();
$("input#submit").removeAttr("disabled");
return false;
}
var settings = $(this).serialize();
console.log(settings);
// submit the data
$.post( 'options.php', settings, { action: 'test_handler'} ).error(
function() {
alert('error');
}).success( function() {
$('.button-save').html('<p>Changes Saved!</p>').show().fadeIn();
$('.button-save').delay(3000).fadeOut();
});
$("input#submit").removeAttr("disabled");
return false;
});
});
Here is the code I am calling the action
function test_handler() {
$post_arr = array(
'post_title' => 'Test post',
'post_content' => 'Test post content',
'post_status' => 'publish',
'post_author' => get_current_user_id(),
'tax_input' => array(
'hierarchical_tax' => $hierarchical_tax,
'non_hierarchical_tax' => $non_hierarchical_tax,
),
'meta_input' => array(
'test_meta_key' => 'value of test_meta_key',
),
);
wp_insert_post($post_arr);
wp_die();
}
add_action( 'wp_ajax_test_handler', 'test_handler' );
I've got a number of links containing parameters which will open a dialog which is populated with an ajax call.
sample link:
Attach File
Here's the trigger for the modal:
$(".attach_timesheet_file").on("click", function(e) {
e.preventDefault();
var url = "<?=Yii::app()->createUrl('admin/timesheetNew/attachTimesheet')?>";
var id = $(this).data("id");
var weekStart = $(this).data("week-start");
var weekEnd = $(this).data("week-end");
$.ajax({
type: 'POST',
url:url,
data: {
id: id,
week_start: weekStart,
week_end: weekEnd
},
success: function(data) {
var modal = $("#attachFileModal");
try{
modal.html(data);
}
catch(error)
{
console.log(error);
}
modal.dialog('open');
return true;
}
})
});
The basic action called by the ajax:
public function actionAttachTimesheet(){
$projectId = Yii::app()->request->getPost('id', null);
$reportedWeekStart = Yii::app()->request->getPost('week_start', null);
$reportedWeekEnd = Yii::app()->request->getPost('week_end', null);
$this->renderPartial("_attachTimesheet", [
'projectId' => $projectId,
'reportedWeekStart' => $reportedWeekStart,
'reportedWeekEnd' => $reportedWeekEnd,
'dataProvider' => TimesheetAdditionalFile::additionalFilesDataProvider($projectId, $reportedWeekStart, $reportedWeekEnd)
], false, true);
}
And finally the CGridView widget inside the dialog:
$this->widget('zii.widgets.grid.CGridView', [
'id' => 'files-grid',
'dataProvider' => $dataProvider,
'columns' => [
[
'name' => 'filename',
'header' => 'File Name',
'value' => 'CHtml::link($data["filename"], Yii::app()->baseUrl . TimesheetNew::FILES_FOLDER . $data["filename"], ["class" => ($data["filetype"] == "pdf")?"fancybox_pdf":"fancybox_picture"])',
'type'=>'raw',
'headerHtmlOptions'=>array('style'=>'width: 250px'),
],
[
'class' => 'CButtonColumn',
'template' => '{delete}',
'buttons' => [
'delete' => [
'label' => 'Delete',
'imageUrl' => null,
'url' => 'Yii::app()->createUrl("admin/timesheetNew/deleteFile", ["id" => $data["id"]])'
]
],
'deleteConfirmation'=>'Are you sure you want to delete this file?',
]
]
]);
I've also used qq.FileUploader as well as fancybox inside the modal, but these do not seem to interfere with anything.
When I try to click any such "attach file" link, the dialog opens just fine and everything works as intended. I'm seeing my gridview, and I can add and delete files. However, when I close the dialog, it won't open this link or any other "attach file" links.
The error I'm getting in the console is this after re-clicking a link:
Uncaught TypeError: modal.dialog is not a function
I'm only experiencing this when using the gridview, when I comment out this widget code, I can freely open and close these dialogs at will.
Any help would be greatly appreciated :)
The solution was rather easy. By adding these lines at the top of the view file, dialogs can once again be opened and closed indefinitely.
Yii::app()->getClientScript()
->scriptMap = array(
'jquery.js' => false,
'jquery-ui.min.js' => false
);
I need some data added default in the editor.
Like I want a template to get loaded in editor when I click on edit template option..
Can anyone suggest me some tip??
Here you can see how it can be done
$dataa= $this->getTemplate1();
$fieldset->addField('content', 'editor', array(
'name' => 'content',
'label' => Mage::helper('abandonedcart')->__('Content'),
'title' => Mage::helper('abandonedcart')->__('Content'),
'style' => 'width:700px; height:500px;',
'wysiwyg' => true,
'required' => true,
'state' => 'html',
'config' => $wysiwygConfig,
'value'=> $dataa,
));
if (Mage::getSingleton('adminhtml/session')->getAbandonedcartData()) {
$form->addValues(Mage::getSingleton('adminhtml/session')->getAbandonedcartData());
Mage::getSingleton('adminhtml/session')->setAbandonedcartData(null);
} elseif (Mage::registry('abandonedcart_data')) {
$form->addValues(Mage::registry('abandonedcart_data')->getData());
}
return parent::_prepareForm();
}
and calling a function to have data
public function getTemplate1() {
$emailTemplate = Mage::getModel('core/email_template')->loadDefault('abandonedcart_abandonedcart_group_email_template');
$emailTemplate['template_text'];;
$template_id = Mage::getStoreConfig('abandonedcart/abandonedcart_group/email_template');
$emailTemplate = Mage::getModel('core/email_template')->loadDefault($template_id);
return $processedTemplate = $emailTemplate->getProcessedTemplate();
}
I am trying to create a dependent combobox system in my Yii application.
First combobox is populated with States and the second one is dynamically generated with ajax and renderPartial() method.
Code below:
View
<?php
$this->widget('ext.combobox.EJuiComboBox', array(
'model' => $adMulti,
'attribute' => 'state_id',
// data to populate the select. Must be an array.
'data' => CHtml::listData(State::model()->findAll(), 'id', 'name'),
'assoc' => true,
// options passed to plugin
'options' => array(
// JS code to execute on 'select' event, the selected item is
// available through the 'item' variable.
'onSelect' => 'getCities(item.value);',
// If false, field value must be present in the select.
// Defaults to true.
'allowText' => false,
),
// Options passed to the text input
'htmlOptions' => array(
'style' => 'height: 36px',
),
));
?>
<script type="text/javascript">
function getCities(state) {
$.ajax({
url: '<?php echo $this->createUrl('ad/ajaxCities'); ?>',
data: {state_name: state},
type: 'POST',
success: function (data) {
$('#city_id-carrier').html(data);
}
});
}
</script>
<div id="city_id-carrier" class="textboxes"></div>
AdController
public function actionAjaxCities()
{
$stateName = isset($_POST['state_name']) ? $_POST['state_name'] : FALSE;
if ($stateName) {
$state = State::model()->findByAttributes(array(
'name' => $stateName
));
$stateId = $state->id;
$cities = City::model()->findAllByAttributes(array(
'state_id' => $stateId
));
$this->renderPartial('_cities', array(
'cities' => $cities,
'stateId' => $stateId
), FALSE, TRUE
);
}
}
_cities.php
<?php
$this->widget('ext.combobox.EJuiComboBox', array(
'model' => AdMulti::model(),
'attribute' => 'city_id',
// data to populate the select. Must be an array.
'data' => CHtml::listData($cities, 'id', 'name'),
'assoc' => true,
// options passed to plugin
'options' => array(
// JS code to execute on 'select' event, the selected item is
// available through the 'item' variable.
// 'onSelect' => 'getLocalities(item.value);',
// If false, field value must be present in the select.
// Defaults to true.
'allowText' => false,
),
));
?>
The code is working and creating the combobox for the first time. But when I change the value in state combobox, something weird happens. A new combobox is created, but the values shown are still from the first combobox generated.
I am getting an error "TypeError: this.input is undefined" in Firebug Console.
I tried creating unique id for combobox using uniqid() but it isn't affecting the id of select element of the combobox.
If I change
$('#city_id-carrier').html(data)
to
$('#city_id-carrier').append(data)
it is working well but with multiple combobox generated.
Any ideas/suggestions to make this work?
I've found a solution to get this working. Instead of creating the combobox dynamically, place the combobox once and then populate it dynamically upon each request. Much like dependent dropdown.
A combobox is a combination of a dropdown and textbox. So, note down the id of the hidden dropdown and update it upon ajax update.
Code:
View:
<?php
$this->widget('ext.combobox.EJuiComboBox', array(
'model' => $adMulti,
'attribute' => 'state_id',
// data to populate the select. Must be an array.
'data' => CHtml::listData(State::model()->findAll(), 'id', 'name'),
'assoc' => true,
// options passed to plugin
'options' => array(
// JS code to execute on 'select' event, the selected item is
// available through the 'item' variable.
'onSelect' => 'getCities(item.value);',
// If false, field value must be present in the select.
// Defaults to true.
'allowText' => false,
),
));
?>
<script type="text/javascript">
function getCities(state) {
$.ajax({
url: '<?php echo $this->createUrl('ad/ajaxCities'); ?>',
data: {state_id: state},
type: 'POST',
beforeSend: function() {
$('#AdMulti_city_id_combobox').val(''); // emptying textbox in case a value is previously selected.
},
success: function (data) {
$('#AdMulti_city_id').html(data); // populating the hidden dropdown.
}
});
}
</script>
<?php
$this->widget('ext.combobox.EJuiComboBox', array(
'model' => $adMulti,
'attribute' => 'city_id',
// data to populate the select. Must be an array.
'data' => CHtml::listData(array(''), 'id', 'name'),
'assoc' => true,
// options passed to plugin
'options' => array(
'allowText' => false,
),
));
?>
AdController
public function actionAjaxCities()
{
$stateName = isset($_POST['state_id']) ? $_POST['state_id'] : FALSE;
if ($stateName) {
$state = State::model()->findByAttributes(array(
'name' => $stateName
));
$cities = City::model()->findAllByAttributes(array(
'state_id' => $state->id
));
$data = CHtml::listData($cities, 'id', 'name');
foreach ($data as $id => $name) {
echo CHtml::tag('option', array('value' => $id),
CHtml::encode($name), TRUE);
}
}
}
I have a custom module right now in order to text AJAX functionality with Drupal Messages. The module has the following code (test_error.module):
<?php
function test_error_user_view_alter(&$build) {
//_assassinations_player_profile($build);
}
function test_error_form_alter(&$form, &$form_state, $form_id) {
$form['test_error'] = array(
'#type' => 'button',
'#name' => 'Error',
'#value' => t('Error'),
'#ajax' => array(
'callback' => 'test_error_error',
'wrapper' => 'the-wrapper-div-field',
),
);
$form['test_warning'] = array(
'#type' => 'button',
'#name' => 'Error',
'#value' => t('Error'),
'#ajax' => array(
'callback' => 'test_error_warning',
'wrapper' => 'the-wrapper-div-field',
),
);
return $form;
}
function test_error_error() {
drupal_set_message("Header error", 'error');
}
function test_error_warning() {
drupal_set_message("Header warning", 'warning');
}
Then, in page.tpl.php, I have the following to output $messages:
<div id="messages-wrap"><?php print $messages; ?></div>
The AJAX function happens when the button is clicked - setting the message - while the message only displays after the page reloads. I tried to hack my way through the return AJAX call like so:
jQuery(document).ready( function(){
jQuery('.ajax-processed').each( function(){
jQuery(this).unbind();
});
jQuery('#edit-test-warning, #edit-test-error').click( function() {
var the_form = jQuery(this).closest('form');
var form_action = the_form.attr('action');
var details = the_form.serialize();
jQuery.ajax({
type: "POST",
url: form_action,
data: details
}).done( function(){
jQuery('#messages-wrap').load("/ #messages-wrap");
});
});
});
The binding of a click event on the #edit-test-x buttons never occurs because of Drupal's native ajax.js preventing it by adding its ajax-x classes.
It's driving me crazy that this problem is so persistent and so difficult to solve when the goal is such a simple thing. What am I missing? Google searching and StackOverflow browsing has come up surprisingly fruitless.
Thanks!
At first, here is some explanations:
1. So you trying to render drupal messages as part of the form, you must to render drupal messages manually.
2. With using '#ajax' key, there is no additional (custom) JS required.
3. On the button click the form is rebuilded, so, all you need is to place message rendering code inside the form logic.
Code example for the simple case:
function test_error_form(&$form, &$form_state) {
$form['test_error'] = array(
'#type' => 'button',
'#value' => t('Error'),
'#ajax' => array(
'callback' => 'test_error_error',
'wrapper' => 'the-error-container',
),
);
$form['test_warning'] = array(
'#type' => 'button',
'#value' => t('Error'),
'#ajax' => array(
'callback' => 'test_error_warning',
'wrapper' => 'the-error-container',
),
);
return $form;
}
function test_error_error($form) {
drupal_set_message("Header error", 'error');
return array(
'#type' => 'markup',
'#markup' => theme('status_messages'),
);
}
function test_error_warning($form) {
drupal_set_message("Header warning", 'warning');
return array(
'#type' => 'markup',
'#markup' => theme('status_messages'),
);
}
Note, that you must have div with id="the-error-container" on the page.
If you want to render new drupal messages on the standard place, try to use ajax commands, like this:
$commands = array();
$commands[] = ajax_command_replace($selector, theme('status_messages'));
return array(
'#type' => 'ajax',
'#commands' => $commands,
);
where $selector - is a css/jquery selector of your message area.
Please ask, if there is some unclear.