How to combine two columns in one in Yajra datatable - javascript

I want to merge restaurant name and location to be in one column, Im uisng laravel and I don't want to merge it using the editcolumn from the controller because this will not allow search to work and I will get in trouble as Im using eloquent as explain here https://github.com/yajra/laravel-datatables/issues/2293
so I have to return the same name as it comes from the elquent, but I need in the same time to combine tow columns in one
Please note, I cant use combination in controller and return it in one column
->editColumn('restaurant_name', function ($data) {
return $data->restaurant_name.' - '.$data->restaurant_city;
}) //this one cant be work because it will give me error message when do search
Javascript:
columns: [
{data: 'id', name: 'id', sClass: 'text-center'},
{data: 'user_name', name: 'users.name', sClass: 'text-center'},
{data: 'restaurant_name', name: 'restaurants.name', sClass: 'text-center'},
{data: 'restaurant_city', name: 'locations.city', sClass: 'text-center'},
{data: 'action', name: 'action', sClass: 'text-center',orderable: false, searchable: false},
],
Controller
return $this->select('orders.*', 'users.name')
->join("food_orders", "orders.id", "=", "food_orders.order_id")
->join("foods", "foods.id", "=", "food_orders.food_id")
->join("restaurant_locations", "restaurant_locations.id", "=", "orders.location_id")
->join("restaurants", "restaurants.id", "=", "foods.restaurant_id")
->join('users', 'orders.user_id', '=', 'users.id')
->select('orders.*',"users.name as user_name","restaurants.name as restaurant_name","restaurant_locations.city as restaurant_city")
->groupBy('orders.id');
return DataTables::of(Order::GetAllOrders())
->addIndexColumn()
->editColumn('created_at', function ($data) {
return date('d-m-Y', strtotime($data->updated_at));
})
->addColumn('action', function($row){
return view('admin.orders.all_orders.action-buttons',compact('row'))->render();
})
->rawColumns(['action'])
->make(true);

I think you should use addColumn instead of editColumn, to display merged name:
->addColumn('full_restaurant_name', function ($data) {
$data->restaurant_name.' - '.$data->restaurant_city
});
By doing this, you are not changing the existing column restaurant_name, but adding another field. Now, if you want to keep this column searchable, you could do two things:
Add custom filter
Define the new full_restaurant_name column like this:
{data: 'full_restaurant_name', name: 'restaurant_name'}
This way, search will work on the full_restaurant_name, because it's referred as restaurant_name column in your database.
PS: I haven't tested this, so if you occur any problems, let me know.

try to use custom filter https://datatables.yajrabox.com/collection/custom-filter
this may help you:
DataTables::of(Order::GetAllOrders())
->addIndexColumn()
->filterColumn('restaurant_name', function($query, $keyword) {
$query->where('restaurant_name', 'like', "%{$keyword}%")
->orWhere('restaurant_city', 'like', "%{$keyword}%"); // you can implement any logic you want here
})
->editColumn('restaurant_name', function ($data) {
return $data->restaurant_name.' - '.$data->restaurant_city;
})
...;

Related

How to update a different column/cell's value based on current column selected value - editable grid Syncfusion(Javascript es5)

I am trying to create an editable grid where I can add TransactionItems. Each TransactionItem will have a product (combobox), rate (textbox), quantity(textbox), total(textbox) and IsTaxable(checkbox) field. When I select Product, I want to update Rate and IsTaxable field on the same row for that product as well. How?
var productElem;
var productDDL;
ej.grids.Grid.Inject(ej.grids.Edit, ej.grids.Toolbar);
var grid = new ej.grids.Grid({
dataSource: [],
toolbar: ['Add', 'Cancel'],
editSettings: { showConfirmDialog: true, showDeleteConfirmDialog: true,allowEditing: true, allowAdding: true, allowDeleting: true, mode: 'Batch' },
columns: [
{
field: 'Product', headerText: 'Product',
type: 'string',
foreignKeyField: "ProductId",
foreignKeyValue: "Product",
edit: {
create: function () {
productElem = document.createElement('input');
return productElem;
},
read: function () {
return productDDL.text;
},
destroy: function (e) {
productDDL.destroy();
},
write: function (args) {
productDDL = new ej.dropdowns.ComboBox({
fields: { text: 'product', value: 'product' },
placeholder: "Select a Product",
allowFiltering: true,
filtering: (e) => {
if (!e.text || e.text.length < 3) { return; }
else {
var query = new ej.data.Query().addParams("searchText",e.text.trim());
e.updateData(productDDLSource, query);
}
},
change: (item) => {
// HERE I want to update value of Rate and IsTaxable based on Product selected
}
});
productDDL.appendTo(productElem);
},
}
},
{ field: 'Rate', headerText: 'Rate', type: 'number' },
{ field: 'Quantity', headerText: 'Quantity', type: 'number' },
{ field: 'Total', headerText: 'Total', type: 'number' },
{ field: 'IsTaxable', headerText: 'Is Taxable', type: 'checkbox' }
],
height: 315
});
grid.appendTo('#grid');
We checked attached sample and we suspect that you have performed the Batch editing with ForeignKey column.
In your code example we found that, you have not defined the column.isPrimaryKey property in the unique Grid column which is required to perform the CRUD action.
Please refer the below documentation for more information
Help Documentation: https://ej2.syncfusion.com/documentation/api/grid/column/#isprimarykey
https://ej2.syncfusion.com/documentation/grid/edit/#editing
Query: When I select Product, I want to update Rate and IsTaxable field on the same row for that product as well. How?
Based on your query we can update Rate, IsTaxable column value when select Product using updateCell method in the change event dropdown editing. Please refer the below syntax and documentation for more information.
Syntax:
gridInstance.updateCell(rowIndex, field, value);
Help Documentation: https://ej2.syncfusion.com/documentation/api/grid/#updatecell
From your code example, we need more details about your query to validate further so, please ensure the following details us.
In your sample, we found that the Grid’s dataSource has been empty and you do not define the dataSource of foreignKeyColumn, dropdownlist editing which are required dataSource to perform the Grid’s CRUD action.
In ForeignKeyColumn, you do not define the column.dataSource property in the foreignKeyColumn(Product) and you have tried to map the different columns value in field and foreignKeyField. By default, in ForeignKey column, bind the external table/JSON data to Grid column and it will display value from foreignKeyValue which is depending on unique values of column.field and column.foreignKeyField.
We shared the Demo sample and documentation about the ForeignKeyColumn Feature.
Demo Sample: https://ej2.syncfusion.com/javascript/demos/#/material/grid/foreign-key.html
Documentation: https://ej2.syncfusion.com/documentation/grid/columns/#foreign-key-column
Note: By default, we are unable update the ForeignKeyField(ProductId) and we should define that foreignKeyColumn’s dataSource since we can map the ForeignKeyValue to Grid column using that columns dataSource.
Also you have enabled the dropdown editing in Product column but dataSource is undefined. Since please ensure that you want to add the dataSource to dropdown editing while perform Grid’s Add action or we misunderstood please share exact requirement to us that will help to validate further.

how to call query call back data to data attribute select2

$('#placeSelect').select2({
width: '100%',
allowClear: true,
multiple: true,
maximumSelectionSize: 1,
placeholder: "Click here and start typing to search.",
data: function(query) {
queryData{!randomJsIden}(query);
},
matcher: function (term, text) {if (text.toUpperCase().indexOf(term.toUpperCase()) == 0){return true;}return false;}
});
function queryData{!randomJsIden}(query){
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.AutoCompleteV2_Con.getData}','{!sObjVal}','{!labelFieldVar}','{!valueFieldVar}',query.term,
function(result, event){
//if success
if(event.status){
var data = {results:[{ id: 1, text: "Ford" },
{ id: 2, text: "Dodge" },
{ id: 3, text: "Mercedes" },
{ id: 4, text: "Jaguar" }]}
query.callback( data);
}
},
{escape: true}
);
}
</script>
How to initialize query call back data to data attribute in select2 .
I cannot use data directly .In above example i am using sample data .
query : function(query){
queryData{!randomJsIden}(query);
}
I want to replace this by data attribute like this : the below one is not working
data : function(query){
queryData{!randomJsIden}(query);
}
here is the fiddle :
http://jsfiddle.net/Lnf8j/303/
Let me know any ideas from you
There are couple of issues in your code
you cannot name a function using flower braces as it is reserved notation symbol in javascript function queryData{!randomJsIden}(query), instead name it as function queryData(query){ ... }
if your adding a callback for data of your select2 then you need to return that data constructed from queryData() function.
data: function(query) {
return queryData(query);
},
function queryData(query){
...
return data;
}
If i'am not wrong,data is sourced to select2 via asynchronous call inside queryData(), which means the data is returned at unpredictable time,so you need to restrict the user from accessing the select2 component until data is feeded to it.
which means you either need to prefetch the data or disable the select2 field until data is returned from remote call.
Working Demo # JSFiddle

Where to programmatically set column filters when using a spreadsheet selModel?

This is a follow up question that I got answered here: How can I programmatically set column filters?
I have a 188 line Ext.js view. In this view I extend Ext.grid.Panel and in this grid I have set the selModel like so ...
selModel: {
cellSelect: false, // Only support row selection.
type: 'spreadsheet' // Use the new "spreadsheet" style grid selection model.
},
On one of the columns, the Status column, I am programmatically setting the filter so that only rows that have the Status of Ready will appear when the page firsts renders. I have been doing this here in the code:
columns: [
...
{
text: 'Status',
dataIndex: 'status',
itemId: 'status',
renderer: function(value, metaData) {
var filter = this.up('panel').down('#status').filter;
if (!filter.menu) {
filter.createMenu();
filter.menu
.down('menuitem[value="Ready"]')
.setChecked(true);
}
metaData.tdStyle = (value == 'Ready') ?
'color:green;font-weight: bold' :
'color:red;font-style: italic'
return(value)
},
filter: 'list',
flex: 1,
},
... more columns ...
A helpful SO member pointed out that is not the most efficient place for the code that sets the filter as the code will be executed for each row in the grid.
I have tried adding an afterrender function like so ...
{
text: 'Status',
dataIndex: 'status',
itemId: 'status',
renderer: function(value, metaData) {
metaData.tdStyle = (value == 'Ready') ?
'color:green;font-weight: bold' :
'color:red;font-style: italic'
return(value)
},
filter: 'list',
flex: 1,
listeners: {
afterrender: function(value) {
Ext.Msg.alert('We have been rendered value is ' + value );
var filter = this.up('panel').down('#status').filter;
if (!filter.menu) {
filter.createMenu();
filter.menu
.down('menuitem[value="Ready"]')
.setChecked(true); //Uncaught TypeError: Cannot read property 'setChecked' of null
}
}},
... but that results in this error message, //Uncaught TypeError: Cannot read property 'setChecked' of null.
What am I doing wrong here? Do I need the listeners:? Am I not getting passed the data I think I am getting passed to my afterrender function? Should I defining a initComponent function?
UPDATE:
I changed my code to what DrakeES suggested, ...
{
text: 'Status',
dataIndex: 'status',
itemId: 'status',
renderer: function(value, metaData) {
metaData.tdStyle = (value == 'Ready') ?
'color:green;font-weight: bold' :
'color:red;font-style: italic'
return(value)
},
flex: 1,
filter: {
type: 'list',
value: 'Ready'
}
... but the result is this:
Where the animated loading image just sits there and spins. This prevents the user from be able to change the filter interactively. I wonder what it is I am doing wrong here?
I am programmatically setting the filter so that only rows that have
the Status of Ready will appear when the page firsts renders
What checking the filter's checkbox effectively does is setting filter on the store. Because you want the filter to be applied initially, it would be better to have it in the store config right away:
filters: [
{
id: 'x-gridfilter-status',
property: 'status',
value: 'Ready'
}
]
That way the grid view appear filtered in the first place — instead of initially showing all rows and only then filtering them out once the column menu renders and applies the filter. Note that having id: 'x-gridfilter-status' on the store's filter is required so that the column's filter picks it up instead of creating a duplicate.
Setting filter on the store, however, will not send feedback to the column filter menu, so the latter will remain unchecked unless you explicitly check it. Therefore, you still need an afterrender handler on either the grid or the column to make things look in sync.
A simple and elegant solution without listeners and stuff:
filter: {
type: 'list',
value: 'Ready'
}
Full working example: https://fiddle.sencha.com/#fiddle/prp

Get ID from selectize.js to form

I'm trying to set up a form on my site, and want to use some dynamic dropdown.
I found Selectize.js, which seems like a good solution, however I'm struggling to find out how to get the ID's from the selected option when I post the form.
As in user selects "Banana" and selectize should return 2 as value for the post
The obvious answer would of course be to change valueField to 'id' however that messes up the createFilter so that's a no go..
I've made a jsfiddle with what I have so far: http://jsfiddle.net/imfpa/Lh3anheq/16/
HTML:
<form>
<select id="item-type" placeholder="Choose type...">
</select>
</form>
javascript:
function hasOwnPropertyCaseInsensitive(obj, property) {
var props = [];
for (var i in obj) if (obj.hasOwnProperty(i)) props.push(i);
var prop;
while (prop = props.pop()) if (prop.toLowerCase() === property.toLowerCase()) return true;
return false;
}
var REGEX = '[a-zA-ZæøåÆØÅ][a-zA-ZæøåÆØÅ ]*[a-zA-ZæøåÆØÅ]';
$('#item-type').selectize({
persist: true,
valueField: 'text',
labelField: 'text',
searchField: ['text'],
options: [
{id: '1', text: 'Apple'},
{id: '2', text: 'Banana'},
{id: '3', text: 'Orange'},
{id: '4', text: 'Cherry'},
],
createFilter: function(input) {
var match, regex;
regex = new RegExp('^' + REGEX + '$', 'i');
match = input.match(regex);
if (match) {
console.log(match[0]);
return !hasOwnPropertyCaseInsensitive(this.options, match[0]);
}
return false;
},
create: true
});
jsFiddle demo (http://jsfiddle.net/json/Lh3anheq/35/)
Okay, based on our discussion in the comments above, you want the selectize.js to return the id of the selected item, and also let users create unique items.
You are right about the id: you just need to replace the valueField: 'text' with valueField: 'id'.
Now we need to fix the decision making in your function hasOwnPropertyCaseInsensitive.
The first argument in this function is an object of objects. If you see the console output, for this.options of your selectize element, you will see roughly the following structure (valueField is already replaced with id here):
{
idOfItem1: {
id: idOfItem1,
text: textOfItem1
},
idOfItem2: ...
}
Here is what the web inspector prints out for console.log(this.options):
So, we can iterate over all objects and still have the display value in the field text, and this is exactly the string that we need to compare against the user input for uniqueness.
function hasOwnPropertyCaseInsensitive(options, userValue) {
var exists = false;
for (var option in options) {
if (options.hasOwnProperty(option)) {
if (options[option].text.toLowerCase() === userValue.toLowerCase()) {
exists = true;
break; // break from the loop when the match is found. return true works as well.
}
}
}
return exists;
}
Note! The id of an element created by a user will be the same as the display value. I.e. if I add a new element to the list, e.g. Test, it will look like this:
{
Test: {
id: "Test",
text: "Test"
}
}
Please see the jsFiddle demo (http://jsfiddle.net/json/Lh3anheq/35/) and let me know if I missed something.

Ext JS 4: Grid List Filter is NOT updated

I am running a weird problem when I try to set Grid Filter list dynamically.
Let me explain by my code snippets
I have a column with filter list is defined as
{
text : 'Client',
dataIndex : 'topAccount',
itemId : 'exTopAccount',
filter: {
type: 'list',
options:[]
}
}
I initialize list from store in 'viewready'
viewready: function(cmp,eOpts){
cmp.getHeaderCt().child('#exTopAccount').initialConfig.filter.options = clientsStore.collect('topAccount');
}
===> WORKS GOOD
Now, I have to build the new client store based on the records when user moves to next page. Therefore I build the store in the 'change' event of paging
listeners: {
'change' :function( toolbar, pageData, eOpts ) {
var store = Ext.StoreManager.get('ExceptionRecords');
clientsStore.removeAll(true);
store.each(function(record){
if(clientsStore.findRecord('topAccount',record.data.topAccount.trim()) == null ) {
clientsStore.add({topAccount: record.data.topAccount.trim()})
}
})
Ext.getCmp('exceptionGridContainer').view.refresh;
Ext.getCmp('exceptionGridContainer').view.getHeaderCt().doLayout;
console.log(clientsStore);
Ext.getCmp('exceptionGridContainer').view.getHeaderCt().child('#exTopAccount').initialConfig.filter.options = clientsStore.collect('topAccount');
}
}
I can now see the new data in clientsStore . But Grid filter list is not updated. still showing old data. I tried refresh,layout etc. Nothing helps
Any help will be appreciated
Thanks
Tharahan
Just changing the value of a property does not affect the component rendered or computed state. The menu is created when the list is first initialized. The first time you do that, it works because that's before the initialization, but the second time, that's too late.
If you can grab a reference to the instantiated ListFilter, I think you could force the recreation of the menu this way:
listFilter.menu = listFilter.createMenu({
options: [ ... ] // new options
// rest of the filter config
});
So, supposing you have a reference to your target grid, you could change the options for the column with dataIndex of "topAccount" by a call similar to this:
var listFilter = grid
.findFeature('filters') // access filters feature of the grid
.get('topAccount'); // access the filter for column
listFilter.menu = listFilter.createMenu({
options: [ ... ] // new options
// rest of the filter config
});
--- Edit ---
OK, complete example. Tested, working.
Ext.widget('grid', {
renderTo: Ext.getBody()
,height: 400
,features: [{
ftype: 'filters'
,local: true
}]
,columns: [{
dataIndex: 'a'
,text: 'Column A'
,filter: {
type: 'list'
,options: ['Foo', 'Bar']
}
},{
dataIndex: 'b'
,text: 'Column B'
},{
dataIndex: 'c'
,text: 'Column C'
}]
,store: {
fields: ['a', 'b', 'c']
,autoLoad: true
,proxy: {
type: 'memory'
,reader: 'array'
,data: [
['Foo', 1, 'Bar']
,['Bar', 2, 'Baz']
,['Baz', 1, 'Bar']
,['Bat', 2, 'Baz']
]
}
}
,tbar: [{
text: 'Change list options'
,handler: function() {
var grid = this.up('grid'),
// forget about getFeature, I read the doc and found something!
filterFeature = grid.filters,
colAFilter = filterFeature.getFilter('a');
// If the filter has never been used, it won't be available
if (!colAFilter) {
// someone commented that this is the way to initialize filter
filterFeature.view.headerCt.getMenu();
colAFilter = filterFeature.getFilter('a');
}
// ok, we've got the ref, now let's try to recreate the menu
colAFilter.menu = colAFilter.createMenu({
options: ['Baz', 'Bat']
});
}
}]
});
I was solving similar problem and answers to this question helped me a lot. Local List filter menu is in fact lazy loaded (only created when clicked) and I needed to set filter menu to be reloaded if the grid store has been reloaded with different data. Solved it by destroying of menu after each reload, so on next click menu is recreated:
var on_load = function() {
var grid_header = me.gridPanel.filters.view.headerCt
if (grid_header.menu) {
grid_header.menu.destroy();
grid_header.menu = null;
}
}

Categories