I used this code for APIs that were surrounded with square brackets and it worked. However, it doesn't work for an API that looks like this:
{"name":"Summer Tire","description":"Premium Summer Tires.","notes":"Good price","supplier":"Tire Systems","price":79,"currency":"EUR","ID":2}
Here is my code:
function loadMaterialDetails(id) {
$("#materialDetails").jqGrid("GridUnload");
$("#materialDetails").jqGrid({
url: url,
mtype: "GET",
datatype: "json",
colModel: [
{ label: 'Material ID', name: 'ID', key: true, width: 1 },
{ label: 'Material Name', name: 'name', width: 2 },
{ label: 'Supplier', name: 'supplier', width: 2 },
{ label: 'Description', name: 'description', width: 3 },
{ label: 'Notes', name: 'notes', width: 1 },
{ label: 'Price', name: 'price', width: 1 },
{ label: 'Currency', name: 'currency', width: 1 }
],
viewrecords: true,
width: 780,
height: 250,
rowNum: 20,
onSelectRow: function(id){
alert(id);
}
});
UPDATE
I gave up on it. If I know that only one row could ever be acquired I fetch it with getJSON and manipulate it manually. Thanks for you input guys!
While Oleg's answer works, it is incomplete.
Try this:
jsonReader: {
root: function (obj) {
return obj instanceof Array ? obj : [obj];
}
}
This will ensure that the grid will continue to work when the returned data is an array of rows, while also allowing it to render the single row / object.
Everything is possible. You need to add jsonReader with root defined as function:
jsonReader: {
root: function (obj) {
return [obj];
}
}
See the demo https://jsfiddle.net/OlegKi/kLsqk8gc/ which displays the results like
UPDATED: I forget to add small changes posted before in the discussion, which is deleted now. If the response in not always object, but could be correct (array too) then one can just use:
jsonReader: {
root: function (obj) {
return $.isArray(obj) ? obj : [obj];
}
}
Related
I try to create sidebar component in React and I have a data structure like bellow
const links = [
{
label: 'Item-1',
url: '/item-1',
},
{
label: 'Item-2',
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
},
{
label: 'Item-2-2',
url: '/item-2-2',
},
{
label: 'Item-2-3',
url: '/item-2-3',
},
],
},
{
label: 'Item-3',
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
},
{
label: 'Item-3-2',
url: '/item-3-2',
},
],
},
];
So the problem is let's say the user changed URL and URL something like that http://localhost:90/item-2-3.
And I need to activate this sidebar item from the sidebar and it can be nested. So firstly I need to deactivate all other sidebar items (I don't want multiple selected items in the sidebar) and after that activate the selected sidebar item.
Because that (if I'm correct) I need update all tree items let's say I add active:false field to all JSON and after that I need to find the correct tree item from tree (URL === item-2-3) add active:true and also update all parent json to active:true (for there are look selected/opened)
So my question is am I correct if I correct how can write this code optimal way? :/
Actually, I want to create a function and when calling this function like that selectItemFromTree(links, '/item-2-2') I get results like in bellow.
const links = [
{
label: 'Item-1',
url: '/item-1',
active: false
},
{
label: 'Item-2',
active: true,
children: [
{
label: 'Item-2-1',
url: '/item-2-1',
active: false
},
{
label: 'Item-2-2',
url: '/item-2-2',
active: true,
},
{
label: 'Item-2-3',
url: '/item-2-3',
active: false
},
],
},
{
label: 'Item-3',
active: false,
children: [
{
label: 'Item-3-1',
url: '/item-3-1',
active: false
},
{
label: 'Item-3-2',
url: '/item-3-2',
active: false
},
],
},
];
You have to traverse the tree recursively and update all the active statuses.
const links=[{label:"Item-1",url:"/item-1"},{label:"Item-2",children:[{label:"Item-2-1",url:"/item-2-1"},{label:"Item-2-2",url:"/item-2-2"},{label:"Item-2-3",url:"/item-2-3"}]},{label:"Item-3",children:[{label:"Item-3-1",url:"/item-3-1"},{label:"Item-3-2",url:"/item-3-2"}]}];
const updateActiveLink = (links, url, parent = null) => {
for (link of links) {
link.active = false;
if (link?.children) {
updateActiveLink(link.children, url, link)
}
if (link.url === url) {
link.active = true;
if (!!parent) parent.active = true;
}
}
}
updateActiveLink(links, '/item-2-3');
console.log(links);
.as-console-wrapper {min-height: 100%!important; top: 0}
Note that this method will mutate the links array.
I am a new Software Developer. In my new company, I use their framework to code. And its using Kendo. I tried to make a search field for Kendo Grid so I can find a specific information in that grid. I tried this method but it doesn't work. Honestly, I don't understand how to use 'transport' things. I call an API to get datas for my grid and I call it in my grid's line of code.
{
type: 'panel',
fields: [{
type: 'panel',
text: 'Payment List',
name: 'payment',
fields: [{
type: 'grid',
name: 'paymentGrid',
data: [],
toolbar: function () {
return `<div class="toolbar" style="width:370px">
<label class="search-label" for="search-reservation" style="color:white"> Cari berdasarkan No. Pesanan: </label>
<input type="search" id="search-reservation" class="search-class">
</div>`
},
sourceOptions: {
pageSize: 10
},
options: {
selectable: true,
autoheight: true,
allowCopy: true,
altrows: true,
pageable: {
refresh: true,
buttonCount: 5,
pageSizes: [10, 20, 50, 100]
},
dataBinding: function () {
record = (this.dataSource.page() - 1) * this.dataSource.pageSize();
}
},
url: function (option) {
var arg = option.data
$.ajax({
method: 'POST',
url: APILink,
data: JSON.stringify(arg),
dataType: 'json',
contentType: 'application/json',
}).done(function (resp) {
if (resp.data != null) {
var nameMap = [];
$.each(resp.data, function (key, val) {
nameMap.push({
id: val.id,
supplier: val.supplier,
reservation_id: val.reservation_id,
currentPayment: val.state
});
});
option.success({
data: nameArray,
total: resp.total
});
}
}).fail(function (jqXHR, status, err) {
option.error(err);
});
},
fields: [{
name: 'number',
text: 'No. ',
template: "#= ++record #",
width: 70,
}, {
name: 'supplier',
text: 'Supplier',
}, {
name: 'reservation_id',
text: 'No. Reservation',
}, {
name: 'currentPayment',
text: 'status',
}, {
name: 'checked',
text: 'choose',
width: 100,
template: function (item) {
return !!item.checked
? `<input id="${item.id}" name='ceklis-boks[]' class="check" checked value="${item.id}" type=\'checkbox\' />`
: `<input id="${item.id}" name='ceklis-boks[]' class="check" value="${item.id}" type=\'checkbox\' />`
}
}],
onDataBound: 'dataBound',
}]
}
Then I used the same code as I mention before in previous link, and replace the ID (#) in that code with mine. But, it won't work. I come to his fiddle and I thought it was because his PlainDs variable and $("#category").kendoAutoComplete({...}) or serverPaging, serverSorting, or serverFiltering. So, I comment all of it here and still working properly. So basically, I can just write the code from line 49 - 81 like in his post. But why it doesn't work? For your information, I call the grid with its name or sometimes I give it a class. But it won't work. Is it a problem if I use class or name instead of ID?
The term "Not working" is too broad here, if you can be more specific on what is not working, we may be able to pinpoint better. However I assume you know how to get the grid to display and so on. so basically to get the search to work I usually have this in the click event of my "Search" button:
var grid = $("#myGrid").data("kendoGrid");
var ds = grid.dataSource;
var searchVal = $("#search-reservation").val();
if ( searchVal ) {
ds.filter({
field: "reservation_id", operator: "eq", value: searchVal
});
}
else {
ds.filter({});
}
I use https://github.com/backbone-paginator/backbone.paginator to display data at a table whose columns are sortable. But when clicking on any header of a column, the sorting is done at the client instead of doing a new server request that should contain the attribute (e.g. name) that should be used to sort the results.
Base class
module.exports = Backbone.PageableCollection.extend({
initialize: function (items, options) {
options || (options = {});
this.url = options.url || "/";
},
state: {
pageSize: 15,
firstPage: 0,
currentPage: 0
},
queryParams: {
sortKey: 'sort',
pageSize: 'size',
currentPage: 'page'
},
parseState: function (resp) {
return {totalRecords: resp && resp.length > 0 ? resp[0]['total_entries'] : 0};
},
parseRecords: function (resp) {
return resp && resp.length > 0 ? resp[1] : [];
},
model: Backbone.NestedModel
});
Example Instantiation
collections.myTasks = new collections.PagingCollection([], {
model: models.SyncModel.extend({
url: URLs.TASKS
}),
url: URLs.MY_TASKS,
state: {
pageSize: 30,
firstPage: 0,
currentPage: 0,
}
});
Columns
columns: [
{
name: "dueDate",
label: "Due Date",
cell: "date",
filterCell: FilterCell,
editable: false,
width: "80px"
},
{
name: "reminder",
label: "Reminder",
filterCell: FilterCell,
cell: Backgrid.StringCell.extend({
formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
fromRaw: function (rawValue, model) {
return DateHelper.format(
IntervalHelper.calculateBefore(model.attributes['dueDate'], rawValue)
);
}
})
}),
editable: false,
width: "80px"
},
{
name: "name",
label: "Subject",
cell: "string",
filterCell: FilterCell,
editable: false,
width: "auto"
},
{
name: "taskStatusCtlg.taskStatus",
label: "State",
filterCell: SelectFilterCell.extend({
filterField: 'taskStatus',
addAllOption: true
}),
cell: "string",
width: "75px"
},
{
name: "assignedTo.alfrescoUserName",
label: "Assigned To",
cell: "string",
filterCell: SelectFilterCell.extend({
filterField: 'assignee',
addAllOption: true
}),
editable: false,
width: "120px"
},
{
name: "taskTypeCtlg.taskType",
label: "Type",
cell: "string",
filterCell: SelectFilterCell.extend({
filterField: 'taskType',
addAllOption: true
}),
editable: false,
width: "70px"
},
{
name: "mainDocument.name",
label: "Case / Document",
link: "mainDocument.id",
cell: LinkCell,
filterCell: FilterCell,
editable: false,
width: '160px'
}
],
Fetching the data etc. is done without problems. But when clicking on a caret sorting is done on the client. But I need that attributes "sort" and "order" get append to the request URL when clicking on a column header (sorting on the server).
Current request:
http://localhost/tasks/user?page=0&size=30
Needed request:
http://localhost/tasks/user?page=0&size=30&sort=name&order=asc
Paginator offers 3 modes for the fetching and sorting:
client: all on the client. Feed the data yourself.
server: Fetch the data from an API (e.g.: collection.getFirstPage()) and receive the total number of pages.
infinite: like the server mode, but best used with unknown number of pages. Like a search result from an external API.
Ensure that you're using the server value for the mode property on the PageableCollection.
var books = new Books([
{ name: "A Tale of Two Cities" },
{ name: "Lord of the Rings" },
// ...
], {
// Paginate and sort on the server side, this is the default.
mode: "server",
});
or within your collection class definition:
module.exports = Backbone.PageableCollection.extend({
mode: "server", // this is the default
initialize: /*...*/
Other than that, this is likely solvable within Backgrid.
The default of Backgrid is to sort on the client. For a custom behavior, you can
provide a Backbone.PageableCollection
or override the HeaderCell view with your own implementation
Using a Backbone.PageableCollection
Set the collection property of the Grid.
var taskGrid = new Backgrid.Grid({
collection: collections.myTasks,
columns: [/*...*/],
});
Overriding the HeaderCell view
You are allow to use a different header cell class on columns. There is no restriction on what a header cell must do. In fact, any Backbone.View class can be used. However, if you wish to modify how the sorter behaves, you must
implement the sorting protocol. See the JSDoc for details.
var SelectAllHeaderCell = Backgrid.HeaderCell.extend({
// Implement your "select all" logic here
});
var grid = new Backgrid.Grid({
columns: [{
name: "selected",
label: "",
sortable: false,
cell: "boolean",
headerCell: SelectAllHeaderCell
}],
collection: col
});
Note (2017/01/30): Links to the API documentation within the documentation are not up to date, this is being discussed in issue #664.
I am using ListFilter plugin to filter results on a Grid panel. The column definition is.
{
header: 'Provider',
filter: {
type: 'list',
store: Ext.getStore('MyApp.store.Provider'),
dataIndex: 'provider_id',
labelField: 'name'
}
}
MyApp.store.Provider is created as
Ext.create('Ext.data.Store', {
storeId: 'MyApp.store.Provider',
autoDestroy: true,
autoLoad: {start: 0, limit: 50},
autoSync: true,
model: 'MyApp.model.Provider',
pageSize: 50,
proxy: {
type: 'ajax',
api: {
create: 'proxy/provider/create',
read: 'proxy/provider/read',
update: 'proxy/provider/update',
destroy: 'proxy/provider/destroy'
},
reader: {
type: 'json',
root: 'data',
successProperty: 'success',
messageProperty: 'message',
totalProperty: 'total'
},
writer: {
allowSingle: false,
type: 'json',
writeAllFields: false,
root: 'data'
}
}
});
And lastly model MyApp.model.Provider is defined as
Ext.define('MyApp.model.Provider', {
extend: 'Ext.data.Model',
fields: [
{ name: 'provider_id', type: 'int'},
'name',
{ name: 'create_time', type: 'date', dateFormat: appDateFormat },
{ name: 'status', type: 'int'}
],
idProperty: 'provider_id',
displayProperty: 'name' // A custom property used for dynamically use the property to display
})
Now this code does not show any sub-menu in the filter menu. It just shows loading. See the image.
Update
I have solved it using following filter config. This actually populates options config manually. So no store is used here.
{
type: 'list',
labelField: 'name',
options: (function () {
var opts = [];
fS.load(function (records, operation, success) {
for (var i = 0; i < records.length; i++) {
var ar = {
id: records[i].get('provider_id'),
name: records[i].get('name')
};
opts.push(ar);
}
});
return opts;
})(),
single: true
}
It seems 'id' is hard-coded. id: records[i].get('provider_id'), does not look good. Though it works.
But I am still looking for a proper way to do it.
Note: The expected behavior can be found on ExtJS 4.1.1. See this jsfiddle. I have reproduced it. But this very same thing does not work on ExtJS 4.0.7
I didn't tried this myself but you need to set the ID manually with the idField property [new to ExtJS4.1.3] which is per default set to id. So I guess this will work:
{
header: 'Provider',
filter: {
type: 'list',
idField: 'provider_id',
store: Ext.getStore('MyApp.store.Provider'),
dataIndex: 'provider_id',
labelField: 'name'
}
}
Update
OK, I looked at the source and I can now tell you that this is the answer. So will have to either live with your workarround until 4.2 is out or you can apply the following changes to your Ext.ux.grid.menu.ListMenu to make it run:
add the idField with a default value.
look within the constructor for this lines
case 'object': options.push([value.id, value[this.labelField]]); break;
// some more lines
fields: ['id', this.labelField],
and replace it with
case 'object': options.push([value[me.idField], value[me.labelField]]); break;
// some more lines
fields: [me.idField, me.labelField],
and within the onLoad function look for
itemValue = records[i].get('id');
and replace it with
itemValue = records[i].get(me.idField);
and that pretty much is it.
I'm teaching myself ExtJS by building a really simple 'scrum' development tracking application. I'm currently displaying the "Backlog" as a grid panel that displays the properties of the card(user story).
Card.js (Card model)
Ext.define('AM.model.Card', {
extend: 'Ext.data.Model',
fields: [
'id',
'name',
'priority_id',
...
]
});
Priority.js (Priority model)
Ext.define('AM.model.Priority', {
extend: 'Ext.data.Model',
fields: [
'id',
'name',
'short_name'
]
});
So the data for the card will look something like this:
backlogcards.json (data)
{
success: true,
backlogcards: [
{
id: 1,
name: 'ONEs',
priority_id: 2,
...
},
{
id: 2,
name: 'TWOs',
priority_id: 3,
...
}
]
}
And the priorities looks like this:
priorities.json (data)
{
success: true,
priorities: [
{
id : 1,
name : "High",
short_name : "H"
},
{
id : 2,
name : "Medium",
short_name : "M"
},
{
id : 3,
name : "Low",
short_name : "L"
}
]
}
So ideally what I would like to do is have the grid panel display the short_name for the corresponding priority_id. When the item is clicked on to be edited inline, a combo box will be displayed that shows the name property. I'm half way there already.
BacklogList.js (view)
Ext.define('AM.view.card.BacklogList', {
extend: 'Ext.grid.Panel',
alias: 'widget.backlogcardlist',
title: 'Backlog',
store: 'BacklogCards',
selType: 'cellmodel',
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
columns: [
{ header: 'ID', dataIndex: 'id' },
{ header: 'Name', dataIndex: 'name', field: 'textfield' },
{
header: 'Priority',
dataIndex: 'priority_id',
width: 130,
field: {
xtype: 'combobox',
typeAhead: true,
store: 'Priorities',
displayField: 'name',
valueField: 'id',
listClass: 'x-combo-list-small'
}
}
]
});
So I know the 'dataIndex' property is what I need to modify in order to change the display, but I'm not sure how to tie those two stores together.
As you can see above, priority is being displayed as a number instead of the short_name.
Is this a situation where I would need to use associations? (I only know OF them) Sencha Docs
Thank you!
EDIT1: Oh I realize I could 'hard code' a renderer property that does this change, but I would like to avoid that and instead use values from the priorities store.
renderer: function(value){
if (value==3)
{
return "L";
}
else if (value==2)
{
return "M";
}
else
{
return "H";
}
},
EDIT2 for Evan:
Priorities store
Ext.define('AM.store.Priorities', {
extend: 'Ext.data.Store',
model: 'AM.model.Priority',
autoLoad: true,
proxy: {
type: 'ajax',
api: {
read: 'app/data/priorities.json',
update: 'app/data/updateUsers.json'
},
reader: {
type: 'json',
root: 'priorities',
successProperty: 'success'
}
}
});
The store.each refers to this store, right? If so, how do I perform the each operation on it?
I tried changing the declaration line to:
var test = Ext.define('AM.store.Priorities', {
And then tried changing your code to test.each but was unsuccessful.
Thanks again!
You need to use a renderer, however there's nothing stopping you from looping over the values in the priorities store and checking, something like:
renderer: function(value) {
var display = '';
store.each(function(rec){
if (rec.get('id') === value) {
display = rec.get('name');
return false;
}
});
return display;
}