I have the following issue:
Article Page contains -> Multiple ArticleUnit Blocks contains -> Multiple Media Blocks
(Edit: See in the comments below for a summary of what I indented to do.)
I built the forms separately using buttom-up approach and what I currently have in a working state is:
Individually each of the 3 Documents works correctly
ArticleUnit Block embeds Media Block works correctly
Article Page embeds standlone ArticleUnit Block (without embedding Media Block) works correctly
But when Article Page embeds ArticleUnit Block which embeds Media Block it is NOT working correctly. After clicking on 'Add New' Button in Article Page the ajax response that I get is a hyperlink 'Add New' that is not displayed on the Article Page form.
Ajax response
<div id="field_container_s559a3c68b30e7_children" class="field-container">
<span id="field_widget_s559a3c68b30e7_children" >
<div>
</div>
</span>
<span id="field_actions_s559a3c68b30e7_children" >
<a
href="/Proximity_Projects/xx/refonte_2015/www/trunk/web/admin/xx/block/articleunitblock/create?uniqid=s559a3c8bf24d1&code=xx.block.admin.article_unit_block_admin&pcode=xx.article.admin.article.news&puniqid=s559a3c68b30e7"
onclick="return start_field_retrieve_s559a3c68b30e7_children(this);"
class="btn btn-success btn-sm btn-outline sonata-ba-action"
title="Add new"
>
<i class="icon-plus fa fa-plus-circle"></i>
Add new
</a>
</span>
<!-- edit one association -->
<script type="text/javascript">
// handle the add link
var field_add_s559a3c68b30e7_children = function(event) {
event.preventDefault();
event.stopPropagation();
var form = jQuery(this).closest('form');
// the ajax post
jQuery(form).ajaxSubmit({
url: 'http://localhost:8080/Proximity_Projects/xx/refonte_2015/www/trunk/web/admin/core/append-form-field-element?code=xx.article.admin.article.news&elementId=s559a3c68b30e7_children&uniqid=s559a3c68b30e7',
type: "POST",
dataType: 'html',
data: { _xml_http_request: true },
success: function(html) {
jQuery('#field_container_s559a3c68b30e7_children').replaceWith(html); // replace the html
if(jQuery('input[type="file"]', form).length > 0) {
jQuery(form).attr('enctype', 'multipart/form-data');
jQuery(form).attr('encoding', 'multipart/form-data');
}
jQuery('#sonata-ba-field-container-s559a3c68b30e7_children').trigger('sonata.add_element');
jQuery('#field_container_s559a3c68b30e7_children').trigger('sonata.add_element');
}
});
return false;
};
var field_widget_s559a3c68b30e7_children = false;
// this function initialize the popup
// this can be only done this way has popup can be cascaded
function start_field_retrieve_s559a3c68b30e7_children(link) {
link.onclick = null;
// initialize component
field_widget_s559a3c68b30e7_children = jQuery("#field_widget_s559a3c68b30e7_children");
// add the jQuery event to the a element
jQuery(link)
.click(field_add_s559a3c68b30e7_children)
.trigger('click')
;
return false;
}
</script>
<!-- / edit one association -->
</div>
My code:
Article Page/News Admin (Admin of a StaticContent)
/**
* Service name of the sonata_type_collection service to embed
* #var string
*/
protected $embeddedArticleUnit;
/**
* Configure the service name (admin_code) of the admin service for the embedded units
*
* #param string $adminCode
*/
public function setEmbeddedArticleUnitAdmin($adminCode)
{
$this->embeddedArticleUnit = $adminCode;
}
protected function configureFormFields(FormMapper $formMapper)
{
parent::configureFormFields($formMapper);
$formMapper
->with('form.group_general', ['name' => 'Details'])
->add('body', 'hidden', ['data' => 'default'])
->add('colour', 'choice', [
'choices' => [
'red' => 'Red',
'blue' => 'Blue',
'yellow' => 'Yellow',
],
])
->end()
->with('Items')
->add('children', 'sonata_type_collection',
['label' => 'Text'],
[
'edit' => 'inline',
'inline' => 'standard',
'admin_code' => $this->embeddedArticleUnit,
])
->end();
}
Article News service
xx.article.admin.article.news:
class: xx\ArticleBundle\Admin\NewsAdmin
arguments:
- ''
- xx\ArticleBundle\Document\News
- 'SonataAdminBundle:CRUD'
tags:
- { name: sonata.admin, manager_type: doctrine_phpcr, group: 'xx Dashboard', label: 'News' }
calls:
- [setRouteBuilder, ['#sonata.admin.route.path_info_slashes']]
- [setRootPath, ['%cmf_base_page_article_news%']]
- [setContainer,[#service_container]]
- [setEmbeddedArticleUnitAdmin, ['xx.block.admin.article_unit_block_admin']]
- [setEmbeddedMediaBlockAdmin, ['xx.block.admin.media_block_admin_header_image']]
ArticleUnit Block Admin (Admin of a Container Block)
/**
* {#inheritdoc}
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('form.group_general', ['name' => 'General'])
->add('body', 'textarea', ['required' => false])
->add('mediaPosition', 'choice', [
'choices' => [
'bottom' => 'Bottom',
'right' => 'Right',
'left' => 'Left',
],
])
->add('parentDocument', 'doctrine_phpcr_odm_tree', ['root_node' => $this->getRootPath(), 'choice_list' => [], 'select_root_node' => true])
->add('position', 'hidden', array('mapped' => false))
->end()
->with('Items')
->add('children', 'sonata_type_collection',
['label' => 'Image(s)'],
[
'edit' => 'inline',
'inline' => 'table',
'admin_code' => $this->embeddedAdminCode,
'sortable' => 'position',
])
->end()
;
}
ArticleUnit Block service
xx.block.admin.article_unit_block_admin:
class: xx\BlockBundle\Admin\ArticleUnitBlockAdmin
arguments:
- ''
- xx\BlockBundle\Document\ArticleUnitBlock
- 'SonataAdminBundle:CRUD'
tags:
- { name: sonata.admin, manager_type: doctrine_phpcr, group: 'xx Dashboard', label: 'Article Unit Block' }
calls:
- [setRouteBuilder, ['#sonata.admin.route.path_info_slashes']]
- [setEmbeddedMediaAdmin, ['xx.block.admin.media_block_admin']]
- [setRootPath, ['%cmf_base_block%']]
Media Block Admin (Admin of an Imagine block)
/**
* {#inheritdoc}
*/
protected function configureFormFields(FormMapper $formMapper)
{
parent::configureFormFields($formMapper);
$formMapper
->with('form.group_general')
->remove('filter')
->remove('name')
->remove('position')
->add('imageMobile', 'cmf_media_image', ['required' => false])
->add('imageTablet', 'cmf_media_image', ['required' => false])
->add('label', 'text', ['label' => 'Label (Alt)'])
->add('position', 'hidden', array('mapped' => false))
->end();
}
Media Block service
xx.block.admin.media_block_admin:
class: xx\BlockBundle\Admin\MediaBlockAdmin
arguments:
- ''
- xx\BlockBundle\Document\MediaBlock
- 'SonataAdminBundle:CRUD'
tags:
- { name: sonata.admin, manager_type: doctrine_phpcr, group: 'xx Dashboard', label: 'Media Block' }
calls:
- [setRouteBuilder, ['#sonata.admin.route.path_info_slashes']]
- [setRootPath, ['%cmf_base_block%']]
Thanks for reading. Any ideas/suggestions? :)
Related
I have an antd Modal, i am trying to validate a field and provided validation to it. How can i enable/disable the Ok button based on the validation. If the validation is successful then button should be enabled else disabled.
<Form>
<Modal
title={modalHeader}
okText="ADD FIELD"
cancelText="CANCEL"
visible={visible}
onCancel={onCancelHandler}
onOk={() => onAddFieldHandler(fieldName)}
width={600}
okButtonProps={{disabled:true}}
>
<p>Please provide name</p>
<Form.Item
name="fieldName"
rules={[{ required: true, message: 'Please enter name' }]}
>
<FieldNameInput
placeholder="Field name..."
value={fieldName}
onChange={(event) => setFieldName(event.target.value)}
/>
</Form.Item>
</Modal>
</Form>
You can use onFieldsChange from Antd Forms API togehter with geFieldsError and the okButtonProps from Antd Modal API.
const [form] = Form.useForm();
const [buttonDisabled, setButtonDisabled] = useState(true);
return (
<Modal
...
okButtonProps={{ disabled: buttonDisabled }}
>
<Form
form={form}
...
onFieldsChange={() =>
setButtonDisabled(
form.getFieldsError().some((field) => field.errors.length > 0)
)
}
>
Here is a working Stackblitz.
In my case I had Form inside modal and there is onFieldChange prop when you can pass function to perform some operations due to changes on from so you can sth like that:
const SomeModal = ({ visible }) => {
const [form] = Form.useForm();
const [buttonDisabled, setButtonDisabled] = useState(true);
const handleOk = () => form.submit();
const handleAfterClose = () => {
setButtonDisabled(true);
form.resetFields();
}
const handleCancel = () => ...some action to hide modal;
const handleFormFinish = (values) => {
... some logic here
}
return (
<Modal
title={"Some title..."}
visible={visibile}
onOk={handleOk}
onCancel={handleCancel}
afterClose={handleAfterClose}
okButtonProps={{ disabled: buttonDisabled }}
>
<Form
form={form}
layout="vertical"
name="acceptform"
onFinish={handleFormFinish}
initialValues={{
...initial values here
}}
onFieldsChange={() => {
const actualFieldValues = form.getFieldsValue();
const anyError = form.getFieldsError().some((field) => field.errors.length > 0);
.. some logic if error etc..
if (anyError) {
setButtonDisabled(true);
}
else {
setButtonDisabled(false);
}
}}
>
and of course there is need to have some validators on fields
<Form.Item
label={"someLabel"}
id="field"
name="field"
hasFeedback
rules={[
{
type: "string",
validator: async (rule, value) => inputFieldValidate(value, "custom message")
},
]}
>
and validator looks alike:
const inputFieldValidate = async (value, message) => {
if (someCondition)) {
return Promise.reject(message);
}
return Promise.resolve();
};
here is some nice to know that validator isasync and to make it work without any warnings just handle promises
Having the Form inside the Modal, a way to update modal button status would be just running form instance's validateFields, but there are two things to take into account:
This function is a Promise, so the state must update after an await with the validation results.
I've experienced some looping issues when using onFieldsChange (maybe the validation triggers some kind of field update). Instead, onValuesChange has worked good enough for me.
Running the validation into a setTimeout callback seems to be mandatory. Doing it without the setTimeout returns a validation error even when all the fields are valid because of an outOfDate: true. It seems to be because of how the Antd Form update lifecycle works, and waiting until this process has ended (what we can easily achieve with the setTimeout) solves that problem.
A succesful validation returns the form values object, a failed one returns an errorInfo object with the errors list, the outOfDate status and the current form values. You can use the errors list in the latter to get the validation messages returned by Antd to display more descriptive and specific feedback.
In the end, my approach has this structure:
const MyModal = ({onFinish, ...otherProps}) => {
const [canSubmit, setCanSubmit] = useState(false);
const [form] = Form.useForm();
return (
<Modal
{...otherProps}
okButtonProps={{
disabled: !canSubmit
}}
>
<MyFormComponent
form={form}
onFinish={onFinish}
onValuesChange={() => {
setTimeout(() => {
form
.validateFields()
.then(() => {
/*
values:
{
username: 'username',
password: 'password',
}
*/
setCanSubmit(true);
})
.catch((err) => {
/*
errorInfo:
{
values: {
username: 'username',
password: 'password',
},
errorFields: [
{ name: ['password'], errors: ['Please input your Password!'] },
],
outOfDate: false,
}
*/
setCanSubmit(false);
});
});
}}
/>
</Modal>
);
};
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 am currently using braintree hosted fields, to embed the credit cards into my app. i am looking at how i pass the payment nonce from the view to the controller. the javascript i have seems to be triggering the braintree api and returning a nonce to my alert but i just need to now push this through to the controller to execute the final piece of the code
within the controller create method i have
def create
result = Braintree::PaymentMethod.create(
:customer_id => current_user.customer_cim_id,
:payment_method_nonce => nonce_from_the_client,
:cardholder_name => "#{current_user.first_name} #{current_user.last_name}",
:options => {
:make_default => true,
:fail_on_duplicate_payment_method => true
}
)
new view
- title t('.title')
= form_for(#payment_method, :url => myaccount_payment_methods_path(#payment_method), :html => {:id => 'cardForm'}) do |f|
= render :partial => 'form', :locals => {:f => f}
/ Load Hosted Fields component.
%script{:src => '//js.braintreegateway.com/web/3.0.0-beta.10/js/hosted-fields.min.js'}
form view
.mdl-grid
.panel
%header.panel__header
%h1 Card Payment
.panel__content
.textfield--float-label
%label.hosted-field--label{:for => "card-number"}
%i.material-icons credit_card
Card Number
#card-number.hosted-field
.textfield--float-label
%label.hosted-field--label{:for => "expiration-date"}
%i.material-icons date_range
Expiration Date
#expiration-date.hosted-field
.textfield--float-label
%label.hosted-field--label{:for => "cvv"}
%i.material-icons lock
CVV
#cvv.hosted-field
%footer.panel__footer
= f.submit class: 'pay-button', id: 'button-pay', disabled: true
application.js
var form = document.querySelector('#cardForm');
var submit = document.querySelector('input[type="submit"]');
braintree.client.create({
authorization: 'sandbox_92dswc7q_mbmb637xwpzgxbrd'
}, function (err, clientInstance) {
if (err) {
console.error(err);
return;
}
// Create input fields and add text styles
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'color': '#282c37',
'font-size': '16px',
'transition': 'color 0.1s',
'line-height': '3'
},
// Style the text of an invalid input
'input.invalid': {
'color': '#E53A40'
},
// placeholder styles need to be individually adjusted
'::-webkit-input-placeholder': {
'color': 'rgba(0,0,0,0.6)'
},
':-moz-placeholder': {
'color': 'rgba(0,0,0,0.6)'
},
'::-moz-placeholder': {
'color': 'rgba(0,0,0,0.6)'
},
':-ms-input-placeholder ': {
'color': 'rgba(0,0,0,0.6)'
}
},
// Add information for individual fields
fields: {
number: {
selector: '#card-number',
placeholder: '1111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: '10 / 2019'
}
}
}, function (err, hostedFieldsInstance) {
if (err) {
console.error(err);
return;
}
hostedFieldsInstance.on('validityChange', function (event) {
// Check if all fields are valid, then show submit button
var formValid = Object.keys(event.fields).every(function (key) {
return event.fields[key].isValid;
});
if (formValid) {
$('.pay-button').prop("disabled", false);
} else {
$('.pay-button').prop("disabled", true);
}
});
hostedFieldsInstance.on('empty', function (event) {
$('header').removeClass('header-slide');
$('#card-image').removeClass();
$(form).removeClass();
});
hostedFieldsInstance.on('cardTypeChange', function (event) {
// Change card bg depending on card type
if (event.cards.length === 1) {
$(form).removeClass().addClass(event.cards[0].type);
$('#card-image').removeClass().addClass(event.cards[0].type);
$('header').addClass('header-slide');
// Change the CVV length for AmericanExpress cards
if (event.cards[0].code.size === 4) {
hostedFieldsInstance.setPlaceholder('cvv', '1234');
}
} else {
hostedFieldsInstance.setPlaceholder('cvv', '123');
}
});
submit.addEventListener('click', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
console.error(err);
return;
}
// This is where you would submit payload.nonce to your server
alert('Got a nonce: ' + payload.nonce);
// If this was a real integration, this is where you would
// send the nonce to your server.
console.log('Got a nonce: ' + payload.nonce);
});
}, false);
});
});
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
Right after your alert line in application.js, you will want to send a request to your server that contains the payment method nonce. For example you can do this with Ajax:
$.ajax({
type: "POST",
url: your_payment_url,
data: {"payment_method_nonce":payload.nonce}
});
Then within your Ruby on Rails controller, you can call Transaction.sale using the payment method nonce in the request to complete the transaction.
For more information on hosted fields, please check out this link.
Edit on Vault question:
If you're using Vault, you can charge users without needing a payment nonce each time. After creating the customer (either through the control panel or through Customer.create, you can retrieve a payment_method_token directly through the payment_methods attribute of the Customer object. To charge the user later, retrieve their payment_method_token on your server and call Transaction.sale, passing in the payment_method_token.
result = Braintree::Transaction.sale(
:amount => "your_amount",
:payment_method_token => "payment_method_token"
)
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');
Can't seem to figure out what the problem is here. This worked perfectly fine when using 3.5, but does not work with 4.0.
I am using select2.full.js as well which supports using input in this manner.
html:
<input id="vcreate-filter" type="text" name="settings[filter]" class="form-control" style="width:100%;"/>
js:
$("#vcreate-filter").select2({
placeholder: "Select or enter application...",
allowClear: true,
multiple: false,
ajax: {
dataType: 'json',
delay: 1000,
type: 'post',
url: '/process/get_application_list.php',
data: function (term, page) {
return {
term: term, // search term
page_limit: 25, // page size
page: page // page number
};
},
results: function (data, page) {
var more = (page * 25) < data.total; // whether or not there are more results available
return {
results: data.results,
more: more
};
}
},
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term)===0; }).length===0) {
return {id:term, text:term};
}
}
}).on('change', function() {
$(this).valid();
});
get_application_list.php:
.......
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// make sure there are some results else a null query will be returned
if( count($results) > 0 )
{
foreach( $results as $row )
{
$ajax_result['results'][] = array(
'id' => htmlspecialchars($row['event_target'], ENT_QUOTES, 'UTF-8'),
'text' => htmlspecialchars($row['event_target'], ENT_QUOTES, 'UTF-8')
);
}
}
else
{
// 0 results send a message back to say so.
$ajax_result['results'][] = array(
'id' => 0,
'text' => 'No results found...'
);
}
// return result array to ajax
echo json_encode($ajax_result);
html: use select element instead of input.
<select id="vcreate-filter" type="text" name="settings[filter]" class="form-control" style="width:100%;"> </select>`
js: use processResults instead of 'results' as callback property.
processResults: function (data, page) {
var more = (page * 25) < data.total; // whether or not there are more results available
return {
results: data.results,
more: more
};
}
assuming the json is in the correct format [{"id": "1", "text": "One"}, {"id": "2", "text": "Two"}]
Breaking changes seems to be documented here.