Kendo Grid Child -> using CRUD toolbar - javascript

My problem is that I Have Hierarchical grid (Master and Child) let say I Have a Department Grid it contains List of Employee Grid, and they both use same datasource.
Here's my GridChild Code:
function detailInit (e){
var msterRow = e.sender.items().index(e.masterRow).toString();
var grid = $("<div id='childGrid"+msterRow+"'
class=childGrid'/>").appendTo(e.detailCell).kendoGrid({
data: e.data.DeptEmployees,
schema: {
model: { fields: { foo: {--skip--}, bar: {--skip--} } }
},
toolbar: ["create", "cancel", "save"],
editable: "popup",
columns: [ --skip--]
save: function(e){
ajaxUpdateDepartment(msterRow, this.dataSource.data());
}
})
As you can see i use data: e.data.DeptEmployees, as child data source to fetch data.
Now I'm stacked in how can I update the child data source?
What I have Tried:
I add child's dataSource.transport for updates, but my child grid keeps on loading.
So I end up configuring the save: function (e) and simply send all data source of the current child but popup editor didn't close at all. And I'm having difficulty to refresh the child data source.
I also attempt to convert my Master and Child Grid to ASP Razor but there was no definite example if how could I handle it in back end, and also my child grid contains drop down grid, so that would be a big re-do. And I also don't know if how can I pass customize parameter through it
I am desperate, I can't find any working reference except this one. but it's using odata, and I dont have child id to use as reference, since I am only using list which I retrieve in a user event.
Please help :'( I'm taking too much time for this one.

The solution is to define a transport properties, in order to fetch data from master, I only need to define the data and convert that to Jason.
take a look of these code:
function detailInit (e){
var msterRow = e.sender.items().index(e.masterRow).toString();
var grid = $("<div id='childGrid"+msterRow+"'
class=childGrid'/>").appendTo(e.detailCell).kendoGrid({
//data: e.data.ChildDetails,
transport: {
read: function (o) {
console.log("child read");
var data = e.data.ChildDetails.toJSON();
o.success(data);
},
update: function (o) {
console.log("child update");
var data = o.data,
arentItem = findByID(data.id);
for (var field in data) {
if(!(field.indexOf("_") === 0)){
arentItem[field] = data[field];
}
}
e.data.dirty = true;
saveChild(record, "#suffix", msterRow, "update");
o.success();
},
destroy: function (o) {
var parentItem = findByID(o.data.id);
preventBinding = true;
e.data.ChildDetails.results.remove(parentItem);
o.success();
saveChild(record, "#suffix", msterRow, "destroy");
},
create: function (o) {
console.log("child create");
var record = o.data;
record.id = index;
index++;
saveChild(record, "#suffix", msterRow, "create");
o.success(record);
}
},
schema: {
model: { fields: { foo: {--skip--}, bar: {--skip--} } }
},
toolbar: ["create", "cancel", "save"],
editable: "popup",
columns: [ --skip--]
}
Here's the working dojo snippet

Related

ag-grid: Using Javascript, find the row id for a given data

I have a list of employees and I want to find the “id” of the row where firstName=Bob which is unique. In other words, I know the firstName supplied as “Bob” and now, I just need to find the row id using Javascript for ag-grid.
According to the documentation https://www.ag-grid.com/javascript-grid/accessing-data/ and I see that the rowNode can be got by using
getRowNodeId: data => data.id,
But I am just unable to to send the parameters (Bob), so that ag-grid finds and puts the id in some assigned variable. I think foreachloop is unnecessary to iterate each and every row, in case if the dataset is too big, then it will be an overhead.
I have the index.html file and a cellRenderer script as shown below. When the circle icon is clicked, I get the row Id correctly, but I am unable to retrieve the firstName, lastName etc.
I tried in two ways (i) writing the evenlistener inside the cellRenderer class, but most convenient for is to take this event listener as a function out of the cell Renderer file. (ii) So, I added a function called getAlert() which you can see as commented. Nothing works for me.
index.html
<div id="myGrid" style="height: 800px; width:100%;" class="ag-theme-alpine"></div>
<script>
var colSpan = function (params) {
return params.data === 2 ? 3 : 1;
};
const columnDefs = [
{ headerName:'FIRST NAME',field: "firstName", width:100, cellClass: 'my-class', colSpan: colSpan,},
{ headerName:'LAST NAME',field: "lastName", cellClass: 'my-class', flex: 6,},
{ headerName:'DESIGNATION (%)',field: "designation", cellClass: 'my-class', flex: 1,},
{ headerName:'INFO', field: "row_id",flex: 2, cellRenderer: 'infoCellRenderer'},
{ headerName:'COMMAND',field: "action",flex: 2,},
];
// let the grid know which columns and what data to use
const gridOptions = {
defaultColDef: {
resizable: true,
},
columnDefs: columnDefs,
getRowNodeId: data => data.id,
onGridReady: event => console.log('The grid is now ready'),
components:{
infoCellRenderer: InfoCellRenderer,
},
};
/*function getAlert(params)
{
console.log(params.node.firstName+","+params.firstName+","+params.value);
}*/
document.addEventListener('DOMContentLoaded', function () {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
//gridOptions.api.refreshView();
agGrid
.simpleHttpRequest({
url: 'XXXXXXX',
})
.then((data) => {
gridOptions.api.setRowData(data);
});
});
</script>
cellRenderer class
class InfoCellRenderer {
constructor() {}
// gets called once before the renderer is used
init(params) {
// create the cell
this.eGui = document.createElement('div');
this.eGui.innerHTML = '<i class="fas fa-info-circle"></i>';
// get references to the elements we want
this.circleValue = this.eGui.querySelector('.circle');
// add event listener to button
this.cellValue = this.getValueToDisplay(params);
//this.eventListener = () => getAlert('${params}');
this.eventListener = () => console.log(`${params.node.id},${params.node.firstName},${params.node.row_id}`);
this.circleValue.addEventListener('click', this.eventListener);
}
getGui() {
return this.eGui;
}
// gets called whenever the cell refreshes
refresh() {
// set value into cell again
this.cellValue = this.getValueToDisplay(params);
//this.eValue.innerHTML = this.cellValue;
// return true to tell the grid we refreshed successfully
return true;
}
// gets called when the cell is removed from the grid
destroy() {
// do cleanup, remove event listener from button
if(this.circleValue) {
// check that the button element exists as destroy() can be called before getGui()
this.circleValue.removeEventListener('click', this.eventListener);
}
}
getValueToDisplay(params) {
return params.valueFormatted ? params.valueFormatted : params.value;
}
}
if firstName is unique you can do as followed:
this.gridOptions.getRowNodeId = data => {
return data.firstName;
};
this code tells ag-grid to use firstName as its internal row Id. so you can easily get the row by:
const node = this.gridApi.getRowNode('Bob');
see link (note // ********* comments ) for full example
gridOptions.api.getModel().forEachNode(
function(rowNode, index){
if (rowNode.data.firstName == "Bob") {
row_index = index;
row = gridOptions.api.getModel().rowsToDisplay[row_index];
data_dict = row.data;
// do something with your row data, data_dict
}
}
);

Problem with variable scope in nested Backbone views

I have a parent view which contains a Backgrid view. In the parent view's initialize section I define a variable isWellSelected. The variable is toggled in the Backgrid column logic when a tickbox is checked. I am able to watch the variable toggled when a box is ticked and unticked.
However, once an event fires the variable is no longer in scope for the event to see. I suspect I may need to pass the variable to the Backrgrid view but I am unsure how to do that correctly. Please advise.
app.wellCollectionView = Backbone.View.extend({
template: _.template($('#wellTemplate').html()),
initialize: function() {
this.isWellSelected = false;
// isWellSelected toggled to true when a tickbox is checked in the columns block.
var columns = [...];
// instantiate collection
var wellCollection = new app.wellCollection;
// Set up a grid view to use the pageable collection
var wellGrid = new Backgrid.Grid({
columns: columns,
collection: wellCollection
});
// Initialize the paginator
var paginator = new Backgrid.Extension.Paginator({
collection: wellCollection
});
// Render the template
this.$el.html(this.template());
// Render the grid
this.$el.append(wellGrid.render().el);
this.$el.append(paginator.render().$el);
wellCollection.fetch({reset: true}).then(function () {...});
},
events: {
'click #EvaluateWell': function(){
this.evalWell(event, this.isWellSelected);
console.log("In events - isWellSelected: " + this.isWellSelected);}
},
// More stuff
}
Constructive feedback welcome.
Thanks!
Adding a snippet for "columns" as per JT's request:
var columns = [
{
name: '',
label: 'Select',
cell: Backgrid.BooleanCell.extend({
events : {
'change': function(ev){
var $checkbox = $(ev.target);
var $checkboxes = $('.backgrid input[type=checkbox]');
if($checkbox.is(':checked')) {
$checkboxes.attr("disabled", true);
this.isWellSelected = true;
// Disable all checkboxes but this one
$checkbox.removeAttr("disabled");
} else {
// Enable all checkboxes again
$checkboxes.removeAttr("disabled");
this.isWellSelected = false;
}
}
}
})
}, {
name: "api",
label: "API",
editable: false, // Display only!
cell: "string"
}, {
name: "company",
label: "Operator",
editable: false, // Display only!
cell: "string"
}];

Trigger function in moment column is sorted in GridX

I use SingleSort module with Gridx. I can't find (I already checked SingleSort documentation and found nothing) a way to react on sort event. I need info about which (and how) column is sorted. I know how get info about sorting (getSortData method) but i don't know how make react in moment sort is made. I can't made onRender-event function because after sorting i will send that info to webapi get new data and again render Grid so event will be triggered again.
define([
"dojo/Evented",
"dojo/_base/declare",
"dojo/store/Memory",
"gridx/core/model/cache/Sync",
"gridx/Grid",
"gridx/modules/ColumnResizer",
'gridx/modules/Focus',
'gridx/modules/RowHeader',
'gridx/modules/select/Row',
'gridx/modules/select/Column',
'gridx/modules/select/Cell',
"gridx/modules/SingleSort"
], function (Evented, declare, Memory, Cache, Grid, ColumnResizer, Focus, RowHeader, SelectRow, SelectColumn, SelectCell, Sort) {
return declare([Evented], {
_counter: 1,
_topOffset: 100,
constructor: function () {
},
initialize: function () {
this._initGrid();
this._resizeGridContainer();
},
clear: function () {
var store = new Memory({ data: [] });
this._grid.setStore(store);
this._counter = 1;
},
_initGrid: function () {
var _this = this;
var store = new Memory({
data: []
});
var structure = [
{
id: 'operationType', field: 'operationType', name: dojo.byId(this._operationTypeId).value
},
{
id: 'transportationType', field: 'transportationType', name: dojo.byId(this._transportationUnitTypeId).value
},
{
id: 'transportationTypeLength', field: 'transportationTypeLength', name: dojo.byId(this._transportationLengthId).value
}
]
this._grid = Grid({
id: this._gridId,
cacheClass: Cache,
store: store,
structure: structure,
modules: [
ColumnResizer,
Sort
],
selectRowTriggerOnCell: true
});
this._grid.placeAt(this._gridPlaceholderId);
this._grid.startup();
},
_resizeGridContainer: function () {
var _this = this;
var container = dojo.byId(this._gridContainerId);
var height = container.parentElement.clientHeight - this._topOffset;
require(["dojo/dom-style"], function (domStyle) {
domStyle.set(_this._gridContainerId, "height", height + "px");
})
},
});
});
you need to tap on to the gridx SingleSort module sort method like this
this._grid.connect( this._grid.Sort, 'sort', function(colId, isDescending, skipUpdateBody){
alert(colId+" "+isDescending+" "+skipUpdateBody);
});
this will trigger the event whenever a grid column is sorted. Hope this helps.

Saving Only the changed record on a BackGrid grid?

I am in the process of learning Backbone.js and using BackGrid to render data and provide the end user a way to edit records on an Microsoft MVC website. For the purposes of this test grid I am using a Vendor model. The BackGrid makes the data editable by default (which is good for my purpose). I have added the following JavaScript to my view.
var Vendor = Backbone.Model.extend({
initialize: function () {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function (model, options) {
if (options && options.save === false) return;
model.url = "/Vendor/BackGridSave";
model.save();
});
}
});
var PageableVendors = Backbone.PageableCollection.extend(
{
model: Vendor,
url: "/Vendor/IndexJson",
state: {
pageSize: 3
},
mode: "client" // page entirely on the client side.
});
var pageableVendors = new PageableVendors();
//{ data: "ID" },
//{ data: "ClientID" },
//{ data: "CarrierID" },
//{ data: "Number" },
//{ data: "Name" },
//{ data: "IsActive" }
var columns = [
{
name: "ID", // The key of the model attribute
label: "ID", // The name to display in the header
editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
// Defines a cell type, and ID is displayed as an integer without the ',' separating 1000s.
cell: Backgrid.IntegerCell.extend({
orderSeparator: ''
})
}, {
name: "ClientID",
label: "ClientID",
cell: "integer" // An integer cell is a number cell that displays humanized integers
}, {
name: "CarrierID",
label: "CarrierID",
cell: "number" // A cell type for floating point value, defaults to have a precision 2 decimal numbers
}, {
name: "Number",
label: "Number",
cell: "string"
}, {
name: "Name",
label: "Name",
cell: "string"
},
{
name: "IsActive",
label: "IsActive",
cell: "boolean"
}
];
// initialize a new grid instance.
var pageableGrid = new Backgrid.Grid({
columns: [
{
name:"",
cell: "select-row",
headercell: "select-all"
}].concat(columns),
collection: pageableVendors
});
// render the grid.
var $p = $("#vendor-grid").append(pageableGrid.render().el);
// Initialize the paginator
var paginator = new Backgrid.Extension.Paginator({
collection: pageableVendors
});
// Render the paginator
$p.after(paginator.render().el);
// Initialize a client-side filter to filter on the client
// mode pageable collection's cache.
var filter = new Backgrid.Extension.ClientSideFilter({
collection: pageableVendors,
fields: ['Name']
});
// REnder the filter.
$p.before(filter.render().el);
//Add some space to the filter and move it to teh right.
$(filter.el).css({ float: "right", margin: "20px" });
// Fetch some data
pageableVendors.fetch({ reset: true });
#{
ViewBag.Title = "BackGridIndex";
}
<h2>BackGridIndex</h2>
<div id="vendor-grid"></div>
#section styles {
#Styles.Render("~/Scripts/backgrid.css")
#Styles.Render("~/Scripts/backgrid-select-all.min.css")
#Styles.Render("~/Scripts/backgrid-filter.min.css")
#Styles.Render("~/Scripts/backgrid-paginator.min.css")
}
#section scripts {
#Scripts.Render("~/Scripts/underscore.min.js")
#Scripts.Render("~/Scripts/backbone.min.js")
#Scripts.Render("~/Scripts/backgrid.js")
#Scripts.Render("~/Scripts/backgrid-select-all.min.js")
#Scripts.Render("~/Scripts/backbone.paginator.min.js")
#Scripts.Render("~/Scripts/backgrid-paginator.min.js")
#Scripts.Render("~/Scripts/backgrid-filter.min.js")
#Scripts.Render("~/Scripts/Robbys/BackGridIndex.js")
}
When the user edits a row, it successfully fires the hits the model.Save() method and passes the model to the save Action, in this case BackGridSave and it successfully saves the record that changed, but seems to save all of the vendors in model when only one of the vendors changed. Is there a way from the JavaScript/Backbone.js/BackGrid to only pass one Vendor - the vendor that changed?
Update: I realized that it is not sending every vendor, but it is sending the same vendor multiple times as though the change event was firing multiple times.
I guess I answered my own question. Well, at least I am getting the desired result. I just added a call to off after the first on. Seems like this would not be necessary though.
var Vendor = Backbone.Model.extend({
initialize: function () {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function (model, options) {
if (options && options.save === false) return;
model.url = "/Robbys/BackGridSave";
model.save();
model.off("change", null, this); // prevent the change event from being triggered many times.
});
}
});

Add an "All" item to kendo ui listview populated by a remote datasource

I am building a website using MVC 4, Web API, and Kendo UI controls.
On my page I am using a Kendo UI Listview to filter my grid. I'm trying to add an "ALL" option as the first item in my listview.
Here is the listview:
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: filterDataSource,
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Here is my datasource:
var filterDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "api/Filter"
}
},
schema: {
model: { id: "FilterId" }
}
});
I have tried a few different methods to make this happen:
I can make it work if I attach it to a button - but I need it there
when the data loads.
If I add it to the dataBound event of the listview, it causes the
databound event to go into a loop and adds the item a bunch (IE) or kills the browser (firefox). Adding preventDefault did nothing.
I've read up on adding a function to the Read paramter of the
datasource, but I think that is simply not the correct place to do
it.
Based on what I've read, I think that I should be able to do it in the dataBound event of the listview and that my implementation is incorrect. Here is the listview with dataBound event added that crashes my browser (Firefox) - or adds about 50 "All" items to the listview (IE).
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: {
transport: {
read: {
url: "api/Filter"
}
}
},
dataBound: function (e) {
var dsource = $("#jobfilter").data("kendoListView").dataSource;
dsource.insert(0, { FilterId: 0, FilterName: "All" });
e.preventDefault();
},
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Any help would be greatly appreciated.
Why don't you add it server-side?
Anyway, if you want to do it in dataBound, just check whether it exists and only add if it doesn't:
dataBound: function (e) {
var dsource = this.dataSource;
if (dsource.at(0).FilterName !== "All") {
dsource.insert(0, {
FilterId: 0,
FilterName: "All"
});
}
},
As an explanation to the problem you're seeing: you're creating an infinite loop since inserting an element in the data source will trigger the change event and the list view will refresh and bind again (and thus trigger dataBound).
You could also encapsulate this in a custom widget:
(function ($, kendo) {
var ui = kendo.ui,
ListView = ui.ListView;
var CustomListView = ListView.extend({
init: function (element, options) {
// base call to widget initialization
ListView.fn.init.call(this, element, options);
this.dataSource.insert(0, {
FilterId: 0,
FilterName: "All"
});
},
options: {
name: "CustomListView"
}
});
ui.plugin(CustomListView);
})(window.jQuery, window.kendo);

Categories

Resources