I am using default Yii2 library for auto-complete. How can I make it, so it is reading values from DB while user is typing?
This is code I have so far, but query is done when the page is created:
echo AutoComplete::widget([
'name' => 'tradeName',
'model' => TradeNames::find()->select('name')->all(),
'options' => [
'class' => 'form-control'
],
'clientOptions' => [
'source' => array_column(TradeNames::find()->select('name')->asArray()->all(), 'name'),
},
],
]);
I followed this advice
jqueryui.com/autocomplete/#multiple and have written next code
<div id="autocomplete" class="ui-widget">
<?= \yii\jui\AutoComplete::widget([
'attribute' => 'attribute',
'name' => 'tradeName',
'clientOptions' => [
'source' => \Yii::$container->get('JsExpression',['function(request, response) {
response( $.ui.autocomplete.filter( window.dataAsArray, extractLast( request.term ) ) );
}']),
'select' => \Yii::$container->get('JsExpression',['function(event, ui) {
var terms = split( this.value );
terms.pop();
terms.push( ui.item.value );
terms.push( "" );
this.value = terms.join( ", " );
return false;
}']),
'focus' => \Yii::$container->get('JsExpression',['function() {
return false;
}']),
]
]) ?>
</div>
<script>
window.dataAsArray = ['item1', 'item2', 'item3'];
function split( val ) {
return val.split( /,\s*/ );
}
function extractLast( term ) {
return split( term ).pop();
}
$(document).ready( function() {
$('#autocomplete').on('keydown', function( event ) {
if ( event.keyCode === $.ui.keyCode.TAB && $( this ).autocomplete( "instance" ).menu.active ) {
event.preventDefault();
}
});
});
</script>
maybe it help to someone
try this
use yii\jui\AutoComplete;
use yii\web\JsExpression;
<?php
$data = TradeNames::find()
->select(['name as value', 'name as label','id as id'])
->asArray()
->all();
echo 'Trade Names' .'<br>';
echo AutoComplete::widget([
'name' => 'tradeName',
'id' => 'trade_name',
'clientOptions' => [
'source' => $data,
// 'minLength'=>'3',
'autoFill'=>true,
'select' => new JsExpression("function( event, ui ) {
$('#memberssearch-family_name_id').val(ui.item.id);//#memberssearch-family_name_id is the id of hiddenInput.
}")],
]);
?>
<?= Html::activeHiddenInput($model, 'tradeName')?>
Related
JQuery's Autocomplete supports categories in the autocomplete results. Like in the link below (just type the letter 'a'):
https://jqueryui.com/resources/demos/autocomplete/categories.html
Yii2's jQuery Autocomplete widget has a source parameter that can take in an array for the results of the Autocomplete. But when I give it a multi-dimensional array, trying to get categories like the link above, it breaks the Autocomplete. See below:
AutoComplete::widget([
'name' => 'search_terms',
'options' => [
'style' => 'width:100%;',
],
'clientOptions' => [
'source' => ['NA' => ['USA', 'CAN'], 'EUR' => ['RUS', 'SPN']],
],
])
How do I get categories working in Yii2's Autocomplete widget?
This type of widgets is just a wrapper of Javascript plugin allowing you to register it using PHP code (configure properties using PHP arrays instead of Javascript objects, etc.). If you investigate the sources of AutoComplete widget and parent classes, you will not find any special processing of source property. That means you need to follow jQuery UI plugin docs, click the "view source" link here to show code. JS part looks like this:
<script>
$( function() {
$.widget( "custom.catcomplete", $.ui.autocomplete, {
_create: function() {
this._super();
this.widget().menu( "option", "items", "> :not(.ui-autocomplete-category)" );
},
_renderMenu: function( ul, items ) {
var that = this,
currentCategory = "";
$.each( items, function( index, item ) {
var li;
if ( item.category != currentCategory ) {
ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
currentCategory = item.category;
}
li = that._renderItemData( ul, item );
if ( item.category ) {
li.attr( "aria-label", item.category + " : " + item.label );
}
});
}
});
var data = [
{ label: "anders", category: "" },
{ label: "andreas", category: "" },
{ label: "antal", category: "" },
{ label: "annhhx10", category: "Products" },
{ label: "annk K12", category: "Products" },
{ label: "annttop C13", category: "Products" },
{ label: "anders andersson", category: "People" },
{ label: "andreas andersson", category: "People" },
{ label: "andreas johnson", category: "People" }
];
$( "#search" ).catcomplete({
delay: 0,
source: data
});
} );
</script>
As you can see, you are passing categories wrong. Try this instead:
'source' => [
['label' => 'USA', 'category' => 'NA'],
['label' => 'CAN', 'category' => 'NA'],
['label' => 'RUS', 'category' => 'EUR'],
['label' => 'RUS', 'category' => 'SPN'],
],
Also for this case maybe you need to include additional JS (above plugin registration) to completely reproduce example.
I have a textInput in _form.php. Then, I change it become select2. But after I change it, the validation is not working. What's the problem? How can I make a validation in select2 so that it cannot be blank?
This is the code:
_form.php
<?php
$formatJs = <<< 'JS'
var formatPenerima = function (penerima) {
if (penerima.loading) {
return penerima.text;
}
var markup =
'<div class="row">' +
'<div class="col-sm-4">' +
'<b style="margin-left:5px">' + penerima.text + '</b>' +
'</div>' +
'<div class="col-sm-3"><i class="fa fa-phone"></i> ' + penerima.telepon + '</div>' +
'<div class="col-sm-4"><i class="fa fa-envelope"></i> ' + penerima.mail + '</div>' +
'</div>';
return '<div style="overflow:hidden;">' + markup + '</div>';
};
var formatPenerimaSelection = function (penerima) {
return penerima.id || penerima.text;
}
JS;
// Register the formatting script
$this->registerJs($formatJs, \yii\web\View::POS_HEAD);
// Script to parse the results into the format expected by Select2
$resultsJs = <<< JS
function (data, params) {
params.page = params.page || 1;
return {
results: data.results, // check here
/*pagination: {
more: (params.page * 30) < data.total_count
}*/
};
}
JS;
$url = Url::to(['/paket/jsonlist']);
// Render your widget
// Get data from dummy data
echo $form->field($model, 'no_induk')->widget(Select2::className(), [
'name' => 'kv-repo-template',
'value' => '',
'initValueText' => '',
'options' => ['placeholder' => 'Cari pemilik ...', 'id' => 'pengambil'],
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 1,
'ajax' => [
'url' => $url,
'dataType' => 'json',
'delay' => 250,
'data' => new JsExpression('function(params) { return {q:params.term, page: params.page}; }'),
'processResults' => new JsExpression($resultsJs),
'cache' => true
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('formatPenerima'),
'templateSelection' => new JsExpression('formatPenerimaSelection'),
],
])->label('Pemilik');
?>
and this is my model rules:
public function rules()
{
return [
[['no_induk', 'nama', 'no_telepon', 'email', 'kategori_paket', 'nama_pengirim'/*, 'tanggal_sampai'*/],
'required', 'message' => '{attribute} tidak boleh kosong.'],
[['id_satpam_pengentry'], 'required', 'message' => 'Nama satpam tidak boleh kosong.'],
[['kategori_paket', 'status', 'id_satpam_pengentry', 'id_satpam_penyetuju'], 'integer'],
[['tanggal_sampai', 'tanggal_pengambilan'], 'safe'],
[['no_induk', 'email'], 'string', 'max' => 255],
[['nama', 'nama_pengirim', 'nama_pengambil'], 'string', 'max' => 128],
[['no_telepon'], 'string', 'max' => 64]
];
}
you can set "required = true" in the "options" array like this
'options' => ['placeholder' => 'Cari pemilik ...', 'class' => 'form-control', 'required' => true],
you have to put
class => "form-control" in your options and remove id, so validation will work
echo $form->field($model, 'no_induk')->widget(Select2::className(), [
'name' => 'kv-repo-template',
'value' => '',
'initValueText' => '',
'options' => ['placeholder' => 'Cari pemilik ...', 'class' => 'form-control'],
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 1,
'ajax' => [
'url' => $url,
'dataType' => 'json',
'delay' => 250,
'data' => new JsExpression('function(params) { return {q:params.term, page: params.page}; }'),
'processResults' => new JsExpression($resultsJs),
'cache' => true
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('formatPenerima'),
'templateSelection' => new JsExpression('formatPenerimaSelection'),
],
])->label('Pemilik');
I'm using Zend\Form\Element\MultiCheckbox with Zend\Form\View\Helper\FormMultiCheckbox:
MyFieldset.php
// namespace ...;
// use ....;
class MyFieldset extends Fieldset
{
// ...
public function init()
{
parent::init();
$this->add(
[
'type' => 'multi_checkbox',
'name' => 'mymulticheckbox',
'options' => [
'label' => _('global label'),
'label_attributes' => [
'class' => 'col-md-3',
],
'value_options' => [
[
'value' => 'foo',
'label' => 'FOO',
],
[
'value' => 'bar',
'label' => 'BAR',
],
[
'value' => 'buz',
'label' => 'BUZ',
],
]
],
]
);
}
// ...
}
myform.phml
use Zend\Form\View\Helper\FormMultiCheckbox;
echo $this->formMultiCheckbox($myFieldset->get('mymulticheckbox'), FormMultiCheckbox::LABEL_PREPEND);
It works, but the "global label" is not displayed. It gets displayed, when I'm using Zend\Form\View\Helper\FormElement, but the FormMultiCheckbox seems to ignore the "global label".
How to make FormMultiCheckbox display the label of the checkbox list?
Have you tried with formRow(). For me it works. This does not appear to be managed in formMultiCheckbox(). See lines 182-193, file zend-form/src/View/Helper/FormRow.php.
// Multicheckbox elements have to be handled differently as the HTML standard does not allow nested
// labels. The semantic way is to group them inside a fieldset
if ($type === 'multi_checkbox'
|| $type === 'radio'
|| $element instanceof MonthSelect
|| $element instanceof Captcha
) {
$markup = sprintf(
'<fieldset><legend>%s</legend>%s</fieldset>',
$label,
$elementString
);
How do you create an event when the user clicks on any part of the calendar? and then store it in the database as a new event?
I know you have to use: select: function(start, end, allDay) to get the "start" and "end" times. But after I get this data how do I pass it to the database?
Thanks!
You may use an ajax request to store the new event in your DB.
There is a demo on the projects homepage, which can easily be adapted.
Via jQuery for example like this :
select: function(start, end, allDay) {
var title = prompt('Event Title:');
if (title) {
calendar.fullCalendar('renderEvent',
{
title: title,
start: start,
end: end,
allDay: allDay
},
true // make the event "stick"
);
/**
* ajax call to store event in DB
*/
jQuery.post(
"event/new" // your url
, { // re-use event's data
title: title,
start: start,
end: end,
allDay: allDay
}
);
}
calendar.fullCalendar('unselect');
}
If you need to react on a specific click, you can also try this, but you have to grep the event end or duration by yourself.
dayClick: function(date, allDay, jsEvent, view) {
var title = prompt('Event Title:');
/**
* again : ajax call to store event in DB
*/
jQuery.post(
"event/new" // your url
, { // re-use event's data
title: title,
start: date
allDay: allDay
}
);
}
here i am sharing what ever i do.
i create a popup and get apartment number and pin number from user
and then through ajax i create a new event by requesting a custom insert_data.php file
here is the code for fullcalendar.php
selectable: true,
selectHelper: true,
select: function(start, end, allDay) {
//alert(start);
var title = $("#dialog").dialog();
//$(".popup").show();
//$(".title").focus();
var start = start; //Date.parse(start) / 1000;
var end = end; //Date.parse(end) / 1000;
var allDay = allDay;
//alert(allDay);
$("#save_frm").click(function(){
var pin_number = $("#pin_number").val();
var apartment_number =$("#apartment_number").val();
//alert(start);
//alert(end);
//alert(allDay);
$.ajax({
type: "POST",
url: "<?php echo WP_PLUGIN_URL; ?>/wp-fullcalendar/insert_data.php",
data: { apartment_number: apartment_number, pin_number: pin_number, start: start, end: end, allDay: allDay }
}).done(function( msg ) {
alert( "Data Saved: " + msg );
$("#dialog").dialog("close");
window.location.reload(true);
calendar.fullCalendar('unselect');
calendar.fullCalendar('refetchEvents');
});
});//select function end here
// calendar.fullCalendar('unselect');
},
//and then ajax request goes to insert_data.php bellow code should be place seperate file
require("../../../wp-load.php"); //connection with database
require("../../../wp-config.php");
//print_r(explode(" ",$_POST['end']));
$start = explode(" ",$_POST['start']);
//coding for extracting date
$start_date=$start[3].'-';
//$start_date.=$start[1].'-';
if($start[1]=='Jan')
{
$start_date.='01';
}
else if($start[1]=='Feb')
{
$start_date.='02';
}
else if($start[1]=='Mar')
{
$start_date.='03';
}
else if($start[1]=='Apr')
{
$start_date.='04';
}
else if($start[1]=='May')
{
$start_date.='05';
}
else if($start[1]=='Jun')
{
$start_date.='06';
}
else if($start[1]=='Jul')
{
$start_date.='07';
}
else if($start[1]=='Aug')
{
$start_date.='08';
}
else if($start[1]=='Sep')
{
$start_date.='09';
}
else if($start[1]=='Oct')
{
$start_date.='10';
}
else if($start[1]=='Nov')
{
$start_date.='11';
}
else if($start[1]=='Dec')
{
$start_date.='12';
}
$start_date.='-'.$start[2];
//coding for extracting date end here
$start_time = $start[4];
$end = explode(" ",$_POST['end']);
$end_time = $end[4];
global $wpdb;
//$table_name = $wpdb->prefix . "em_events";
//$wpdb->insert( $table_name, array( 'album' => $_POST['album'], 'artist' => $_POST['artist'] ) );
// Create post object
$apartment_number = $_POST['apartment_number'];
$pin_number = $_POST['pin_number'];
$post_date = $start_date.' ' .$start_time;
$post = array( 'ID' => ''
, 'post_author' => '1'
, 'post_date' => ''
, 'post_date_gmt' => ''
, 'post_content' => $apartment_number
, 'post_tittle' => $apartment_number
, 'post_excerpt' => $apartment_number
, 'post_status' => 'publish'
, 'comment_status' => 'closed'
, 'ping_status' => 'closed'
, 'post_password' => $pin_number
, 'post_name' => $apartment_number
, 'to_ping' => ''
, 'pinged' => ''
, 'post_modified' => ''
, 'post_modified_gmt' => ''
, 'post_content_filtered'=> '0'
, 'post_parent' => '0'
, 'guid' => '1'
, 'menu_order' => '0'
, 'post_type' => 'event'
, 'post_mime_type' => $post_date
, 'comment_count' => '0'
);
// Insert the post into the database
$post_id=wp_insert_post( $post, $wp_error );
if($wpdb->insert( 'wp_em_events', array(
'post_id' => $post_id
, 'event_slug' => $_POST['apartment_number']
, 'event_owner' => 1
, 'event_status' => 1
, 'event_name' => $_POST['apartment_number']
, 'event_start_time' => $start_time
, 'event_end_time' => $end_time
, 'event_all_day' => 0
, 'event_start_date' => $start_date
, 'event_end_date' => $start_date
, 'post_content' => $_POST['apartment_number']
, 'event_rsvp' => 0
, 'event_rsvp_date' => $end_date
, 'event_rsvp_time' => '00:00:00'
, 'event_spaces' => 0
, 'event_private' => 0
, 'location_id' => 0
//, 'recurrence_id' => 1223
, 'event_category_id' => 1
, 'event_attributes' => 'a:0:{}'
, 'event_date_created' => $start_date." ".$start_time
, 'event_date_modified' => $start_date." ".$start_time
, 'recurrence' => 0
//, 'recurrence_interval' => 12
//, 'recurrence_freq' => 12
//, 'recurrence_byday' => 1231
//, 'recurrence_byweekno' => 4564
, 'recurrence_days' => 0
//, 'blog_id' => 456465
, 'group_id' => 0
) )){
echo "query execute";
}else{
echo "query not execute";
}
The select callback has changed since. For FullCalender v5 use this...
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
selectable: true,
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
dateClick: function(info) {
alert('clicked ' + info.dateStr);
},
select: function(info) {
alert('selected ' + info.startStr + ' to ' + info.endStr);
}
});
calendar.render();
});
Source: https://fullcalendar.io/docs/select-callback
My autocomplete is not working and I can't spot the error.
Jquery:
<script>
$(function() {
$('#autoComplete').autocomplete({
//source: "/groceries/items/autoComplete", ///This works but response isn't formatted correctly'
//dataType: "json"
minLength: 2,
source: function( request, response ) {
$.ajax({
url: "/groceries/items/autoComplete",
dataType: "jsonp",
data: {
featureClass: "P",
style: "full",
maxRows: 12,
term: request.term
},
success: function( data ) {
response( $.map( data, function( el ) {
return { label: el.id, value: el.name }
}));
}
});
}
});
});
Controller:
public function autoComplete() {
Configure::write('debug', 0);
$this->layout = 'ajax';
$query = $_GET['term'];
$items = $this->Item->find('all', array(
'conditions' => array('Item.name LIKE' => $query . '%'),
'fields' => array('name', 'id', 'category_id'),
'group' => array('name')));
$this->set('items', $items);
}
Form:
<p>
<?php echo $this->Form->create('Item', array('model'=>'item','action' => 'addItem', 'name'=>'AddItem'));?>
<?php echo $this->Form->text('Item.name', array('size'=>'30', 'id'=>'autoComplete', 'autocomplete'=>'off')); ?>
<?php echo $this->Form->text('Item.category_id', array('type'=>'hidden', 'value'=>'0')); ?>
<?php echo $this->Form->text('Groclist.id', array('type'=>'hidden', 'value'=>$groclist['Groclist']['id'])); ?>
<?php echo $this->Form->end('Add'); ?>
</p>
Response:
0: {Item:{name:Cake, id:6, category_id:null}}
1: {Item:{name:Carrot Cake, id:9, category_id:null}}
2: {Item:{name:Carrots, id:8, category_id:null}}
3: {Item:{name:Casserole, id:11, category_id:null}}
4: {Item:{name:Cauliflower, id:10, category_id:null}}
Edited for clarification.
I realize JqueryUI expects label and value and that map should rearrange them, but for some reason it's not. Any ideas?
I found an even better solution. This is done completely in the controller. No view required.
public function autoComplete() {
Configure::write('debug', 0);
*$this->autoRender=false;*
$this->layout = 'ajax';
$query = $_GET['term'];
$items = $this->Item->find('all', array(
'conditions' => array('Item.name LIKE' => $query . '%'),
'fields' => array('name', 'id', 'category_id'),
'group' => array('name')));
*$i=0;
foreach($items as $item){
$response[$i]['value']="'".$item['Item']['id']."'";
$response[$i]['label']="'".$item['Item']['name']."'";
$i++;
}
echo json_encode($response);*
}
What if you reduce the array that is being returned by the Items model. So instead of $this->set('items', $items); you return the json encoded results like so:
foreach($items as $item) {
$data[] = $item['Item'];
}
$data = json_encode($data);
echo $data;
exit;
This is inside the auto_complete method in the controller.
UPDATE:
When querying for Cake for example, it would return a result like so:
[
{"name":"Cake Batter","id":"1","category_id":"3"},
{"name":"Cake Mix","id":"2","category_id":"3"}
]
if you are not wanting to return the json, you could just return $data without the json encoding.
Format Update:
I am not certain if this is to "sloppy", but you could change the foreach loop to:
foreach($items as $item) {
$data[]= array(
'label' => $item['Item']['name'],
'value' => $item['Item']['id']
);
}
Looks like you forgot the Item:
response($.map( data, function( el ) {
return { label: el.Item.id, value: el.Item.name }
});
You can also do it in the server side using Set:
$this->set('items', Set::extract('/Item', $items));