x-editable typeahead ajax source - ajax

I don't want to print all the cities in my code local: ['LONDON', 'BERLIN', 'NEW YORK', 'MADRID']. i would like to make query each time user type a letter.
Can I use ajax in order to use Auto complete with "x-editable typeahead"?
HTML:
<li><label class="control-label">CITY:</label> <span id="userCity" data-
type="typeaheadjs" data-pk="cityID" data-name="users">PARIS</span></li>
JS:
$('#userCity').editable({
url: 'ajax/editUser.php',
typeahead: {
local: ['LONDON', 'BERLIN', 'NEW YORK', 'MADRID']
}
});

Just in case this may help someone, looks like the current 1.5.1 release of the X-Editable does not support passing the dataset parameter to the typeahead constructor. There is a pull request which implements support for this documented here https://github.com/vitalets/x-editable/pull/664.
To quickly get around this issue, you will need to update the inputs-ext/typeaheadjs/typeaheadjs.js at around line 45 to be:
this.$input.typeahead.apply(this.$input, this.options.typeahead);
Then you can instantiate the X-Editor as:
var bloodhound = new Bloodhound({
remote: '/url/to/json/output'
// any other bloodhound options here
});
bloodhound.initialize();
$('#editable-field').editable({
type: 'typeaheadjs',
typeahead: [{
hint: true,
highlight: true
// any other typeahead options
}, {
source: bloodhound.ttAdapter()
// any other dataset options
}]
});

Related

Ideal way to ajax load a view in Drupal 8 with contextual filters

I have a taxonomy called category.
I have a menu with links to each of these taxonomy items.
The Taxonomy page for each of these items has that menu and also contains a view which uses a contextual filter from the URL to filter the content of the view to content with that taxonomy term.
I wanted to Ajax load the view content when one of these menu items is clicked.
I've been able to achieve the desired result by enabling ajax on the view and using the following JavaScript.
(function ($) {
Drupal.behaviors.course_browser = {
attach: function (context, settings) {
// should only be one menu, but guard against 0, and avoid the if statement.
context.querySelectorAll(".menu--categories").forEach((menu) => {
// for each link in the menu
menu.querySelectorAll(".nav-link").forEach((link) => {
// on click
link.addEventListener("click", (event) => {
event.preventDefault();
// fetch the taxonomy term id from menu link
let tid = event.target.dataset.drupalLinkSystemPath.replace(
"taxonomy/term/",
""
);
// make the ajax call
$.ajax({
url: "/views/ajax",
type: "post",
data: {
view_name: "course_browser",
view_display_id: "block_1",
view_args: tid,
},
success: (response) => {
response.forEach((action) => {
// the response contains a number of commands; I'm not sure
if (
action.command === "insert" &&
action.method === "replaceWith"
) {
let viewElement = document.querySelector(VIEW_SELECTOR);
// update the html of the course browser
viewElement.innerHTML = action.data;
// update the url in the browser
window.history.pushState("", "", event.target.href);
// seperate function to adjust my page title
updatePageTitle(event.target.textContent);
// call drupal behaviours passing context to ensure all the other js code gets a chance to manipulate the new content
Drupal.attachBehaviors(viewElement);
}
});
},
error: function (data) {
console("An error occured fetching the course browser");
},
});
});
});
});
},
};
})(jQuery);
I'm looking for feedback on my approach here; my main concern at the moment is the way I handle the response. When I look at the response I receive something like that shown below:
0: {command: "settings", settings: {…}, merge: true}
1: {command: "add_css", data: "<link rel="stylesheet" media="all" href="/core/modules/views/css/views.module.css?qcog4i" />↵"}
2: {command: "insert", method: "append", selector: "body", data: "<script src="/core/assets/vendor/jquery/jquery.min…/js/modules/views/ajax_view.js?qcog4i"></script>↵", settings: null}
3: {command: "insert", method: "replaceWith", selector: ".js-view-dom-id-", data: "The HTML"}
As you can see, I'm manually handling the response by cherry picking the part I want and replacing the HTML of view. Based on what I've seen around Drupal, I think there should be something I can pass this response to that handles it automatically. When I look at the window object of the browser, I can see Drupal.AjaxCommands which looks like it was designed to handle this, but I'm not sure how I should be using this.
I also note that in the case I can simply pass this response to something to have those AjaxCommands executed, the selector ".js-view-dom-id-" isn't right. So I could tweak the response before I pass it, or if someone knows a way to adjust the ajax request to perhaps get the right selector, that would be ideal.
Sorry if this info is readily available somewhere...there are quiet a few resources around related to Drupal and Ajax but I haven't been able to find examples of exactly what I'm doing here, the circumstances always seem to differ enough that I can't use them.
Thanks for any help.

Kendo UI: How to get the text input from the Multiselect

I'm trying to use Kendo UI MultiSelect to select some stuff from an API. The API won't return all items because they are too much. It will only return those that contains the searchTerm.
I'm trying to figure out how to send the input text in a Kendo UI Multiselect. When I say the input text, I mean what the user typed in the input before selecting anything from the list. That text has to be passed on to the DataSource transport.read option.
See this Codepen to understand
https://codepen.io/emzero/pen/NYPQWx?editors=1011
Note: The example above won't do any filtering. But if you type "bre", the console should log searching bre.
Use the data property in the read transport options, this allows you to modify the query being sent by returning an object that will later on be serialized in the request.
by default read are GET requests so it will be added to the queryString of your url specified.
If it were to be a POST it would be added to the POST values.
<div id="multiselect"></div>
<script>
$('#multiselect').kendoMultiSelect({
dataTextField: 'first_name',
dataValueField: 'id',
filter: "startswith",
dataSource: {
serverFiltering: true, // <-- this is important to enable server filtering
schema: {
data: 'data'
},
transport: {
read: {
url: 'https://reqres.in/api/users',
// this callback allows you to add to the request.
data: function(e) {
// get your widget.
let widget = $('#multiselect').data('kendoMultiSelect');
// get the text input
let text = widget.input.val();
// what you return here will be in the query string
return {
text: text
};
}
}
}
}
});
</script>

ExtJS: 2 ajax store, 1 with an extra row

I have this code:
Ext.define('storeBusiness',{
extend: 'Ext.data.Store',
autoLoad: true,
autoSync: true,
model: 'Business',
proxy: {
type: 'ajax',
api: {
read: '../crud/ler_filter.php?db='+database+'&tbl=mercados',
create: '../crud/criar.php?db='+database+'&tbl=mercados',
update: '../crud/update.php?db='+database+'&tbl=mercados',
destroy: '../crud/apagar.php?db='+database+'&tbl=mercados'
},
reader: {
type: 'json',
root: 'rows'
},
writer: {
type:'json'
}
}
});
var storeBusinessCombo = Ext.create('storeBusiness');
var storeBusiness = Ext.create('storeBusiness');
storeBusiness.add({id: 0, business: "All"});
I have 2 grids. One has storeBusiness and the other has storeProducts.
The way the work is when I click on the business grid it filters the produts grid so that it shows the products of that business.
On the business grid it has the storeBusiness that it fetches the records from a database. I want to fetch all business from the database and add one more record (named 'All') without writing it to the database.
I dont want to add 'All' to the database because in the Product's grid I want to have a combobox that has all the business (storeBusinessCombo) without the 'All' record.
Does anyone has any idea how I can do this?
(the code above isn't doing what I want, storeBusiness shows all business without the 'All' in the grid)
Important: This works if the Ext.define('storeBusiness', has a proxy that is of type: 'memory'
Two approaches come to mind:
Add the "all" record to the store after load and don't sync so the
record isn't sent to the server.
Use memory proxy, retrieve the
businesses records via Ajax request and assign them to the store via its data config.
To resolve this I have done:
Put the Ext.define('storeBusiness',{ to autoLoad: false,
Put this code:
var storeBusinessCombo = Ext.create('storeBusiness', {autoLoad: true});
var storeBusiness = Ext.create('storeBusiness');
storeBusiness.on('beforeload', function(){
storeBusiness.loadData([{id: 0, business: "All"}]);
}
And you should put
storeBusiness.load({addRecords: true});
when you want to load storeBusiness.

Filter param value(name value) is not sending in Ajax call

Filter parameter value i.e name value is not included when i was performing filter operation in my API call as written below
_dc:1427270031651
counrtyId:2
custId:1
id:
name:
page:1
start:0
limit:10
sort:[{"property" : "id", "direction" : "desc"}]
This is my store
Ext.define('MyDesktop.store.DirectoriesStore', {
extend: 'Ext.data.Store',
requires:'MyDesktop.model.DirectoriesNumberModel',
model: 'MyDesktop.model.DirectoriesModel',
storeId:'DirectoriesStore',
autoLoad : {
params : {
start : 0,
limit : '10'
}
},
pageSize : 10,
remoteSort : true,
sorters : [{
property : 'id',
direction : 'desc'
}],
remoteFilter : true,
proxy:{
type:'ajax',
url:'./configuration/directory/get',
reader:{
type: 'json',
root: 'data',
totalProperty: 'total'
},
extraParams: {
'countryId': '',
'custId': ''
}
},
autoLoad:false
});
and this is my view
Ext.require([
'Ext.ux.grid.FiltersFeature'
]);
var filters = {
ftype: 'filters',
local: true,
features: [{type: 'integer',dataIndex: 'name'},
{type: 'string',dataIndex: 'description'},
{type: 'string',dataIndex: 'fileName'}]
};
and I added it grid as follows:
features: [filters],
Filters are working but In API call filters are not calling
Please anyone help me.
You have remote filtering disabled in your store.
Change remoteFilter: false to remoteFilter: true and try again.
From the docs:
remoteFilter : Boolean
true to defer any filtering operation to the server. If false,
filtering is done locally on the client.
It also appears that your specifically telling your application to apply the filters locally with the local:true config.
The problem is, the params are not being sent to the ajax call. But hey, try changing the page after you loaded your store. Then try sorting again. You will see that the params are now being sent along in the Ajax request.
My current workaround is to imitate a changepage via gridStore.loadPage(page, [options]). You should also change it back to first page again after that.
After you change the page, sort will be working for all following requests.
This of course is just a workaround and we may have different requirements. My grid is calling store.load() after render so I attached the change page there.
Hope you have some hints.
NOTE: THIS IS ONLY A WORKAROUND!

Extjs create dynamic accordion using store

I'm beginning development of an app in extjs. I'm using the MVC approach, as provided in the extjs documentation.
I have some dynamic data which needs to present the user with a set of accordion controls. I've got the data in a store, but I do not know how to dynamically create the accordion items (unlike grid panels, there doesn't appear to be a store data method).
Here is my current accordion view code - with static items:
Ext.define('BP.view.induction.LeftPage', {
extend: 'Ext.Panel',
alias : 'widget.leftpage',
title: "Left Page",
layout: {
type: 'accordion',
align: 'stretch'
},
layoutConfig: {
// layout-specific configs go here
titleCollapse: true,
animate: true,
activeOnTop: true
},
items: [{
xtype: 'panel', // fake hidden panel, so all appear collapsed
hidden: true,
collapsed: false
},{
xtype: 'panel',
title: 'Panel 1',
html: 'Panel content!'
},{
xtype: 'panel',
title: 'Panel 2',
html: 'Panel content!'
},{
xtype: 'panel',
title: 'Panel 3',
html: 'Panel content!'
}]
});
Any guidance on how to achieve the above would be appreciated, thank you.
[Edit] In response to sra's request, here is my controller:
Ext.define('BP.controller.Induction', {
extend: 'Ext.app.Controller',
views: [
'induction.Binder'
],
stores: [
'Sections',
'Categories',
'Tasks'
],
init: function() {
console.log('Initialized Induction!');
}
});
I should note here that this controller loads a parent view, which in turn loads the LeftPage view - I'm not sure if this creates any scoping issues. Furthermore, as you can see, more than one store is loaded.
You can do like this (untested example with some tweaks)
Ext.define('BP.view.induction.LeftPage', {
extend: 'Ext.Panel',
alias : 'widget.leftpage',
title: "Left Page",
layout: null,
layoutConfig: null,
store: null,
attentive: true,
initComponent: function() {
var me = this;
// begin edit
// only set the store if is is not already defined
me.store = me.store ? me.store : Ext.StoreMgr.lookup('Sections'); // you may change this to any storename you want
// end edit
me.layout = { // don't set objects directly in class definitions
type: 'accordion',
align: 'stretch'
};
me.layoutConfig = { // dont set objects directly in class definitions
titleCollapse: true,
animate: true,
activeOnTop: true
};
me.callParent(arguments);
if (me.attentive) {
me.store('load', me.onRebuildContent, me);
if (me.store.count() == 0)
me.store.load();
} else {
me.buildContent();
}
},
buildContent: function() {
var me = this;
function addItem(rec) {
me.add({
xtype: 'panel',
title: rec.get('titleProperty'),
html: rec.get('bodyProprty')
});
};
me.store.each(addItem);
},
onRebuildContent: function() {
var me = this;
me.removeAll();
me.buildContent();
}
});
Your store will need at least two properties; one for the title and one for the content. And the store need to be loaded before you should instantiate this. But that can easily be done within your controller.
Edit based on comment and new OP info:
Well your view is a bit out of control of the controller. So I recommend you to simply use the StoreManager to receive a valid instance (I've edited the code, I just didn'T know the correct store to use). The StoreManager will know about the store as long as you list him within a controller (StoreManager is capable of much more, but that is all you need to know at the moment). For a further release you could also use the mixin bindable which would manage a storebinding more clean and enables you to update your accordion after the store receives new data (get updated)
Edit for readability
I've just cleaned it up a bit and included a switch param attentive which would allow you to use this component as directly bound to a store, reacting on all load events or as a sort of static one where the store should already be loaded. All in all this should give you a start without making it to complex.

Resources