SAPUI5 dynamic binding of table cells - javascript

I have the following Problem:
I want to dynamically create table rows and cells with different settings.
The Solution mentioned here: Dynamic binding of table column and rows
was a good starting point for my problem, but I still could not get it to work.
The table should display each object of the model in a new row with the binding for the given attributes of that object.
The checked attribute should be displayed in/as a checkbox that is either checked or unchecked depending on the value (true or false) of checked.
Now, this works perfectly fine if I define the bindings and columns as they are in the SAPUI5 Table Example
The Problem:
Depending on value (true or false) of the objects existsLocal attribute I want the checkbox of that row to be either enabled or disabled. Further that row should get a new class - called existsLocalClass wich sets its background to grey if existsLocal is true.
I was thinking that this can be solved with a factory function that creates my rows and its cells. Unfortunately my factory does not work as intended.
Here is my code:
Model definition:
var model = [
{name: "name1", description: "description1", checked: true, existsLocal: true},
{name: "name2", description: "description2", checked: false, existsLocal: false}
]
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({modelData: model});
Table plus factory function:
var oTable = new sap.ui.table.Table({
visibleRowCount: 7,
firstVisibleRow: 3,
selectionMode: sap.ui.table.SelectionMode.None
});
var tableRowFactory = function (sId, oContext) {
console.log("row factory");
var exists = oContext.getProperty("existsLocal");
if(exists){
return new sap.ui.table.Row(sId)
.addCell(new sap.ui.commons.TextView()
.bindProperty("text", oContext.sPath + "/name"))
.addCell(new sap.ui.commons.TextView()
.bindProperty("text", oContext.sPath+ "/description"))
.addCell(new sap.ui.commons.CheckBox()
.bindProperty("checked", oContext.sPath+ "/checked").setEnabled(false))
.addStyleClass("existsLocal");
}else{
return new sap.ui.table.Row(sId)
.addCell(new sap.ui.commons.TextView()
.bindProperty("text", oContext.sPath + "/name"))
.addCell(new sap.ui.commons.TextView()
.bindProperty("text", oContext.sPath+ "/description"))
.addCell(new sap.ui.commons.CheckBox()
.bindProperty("checked", oContext.sPath+ "/checked"))
}
};
oTable.setModel(oModel);
oTable.bindRows("/modelData",tableRowFactory); // does not work
oTable.bindAggregation("rows", "/modelData", tableRowFactory); //doesn't work either
The browser does not show any errors and the table stays empty. I think the function does not even get called but I could not manage to fix it.
Maybe my entire approach is wrong - I don't really understand sapui5's binding and context thingy.
I hope that you can help me with this.
Edit:
I found a kinda hacky solution for this:
var model = oTable.getModel();
var rows = oTable.getRows();
var indicesOfRows = [];
$.each(rows, function (index, row){
indicesOfRows.push(oTable.indexOfRow(row));
});
$.each(rows, function(index, row){
var rowIndex = indicesOfRows[index];
var exists = model.getProperty("existsLocal", oTable.getContextByIndex(rowIndex));
var cells = row.getCells();
if(exists){
$.each(cells, function(index, cell){
if(cell instanceof sap.ui.commons.CheckBox){
row.$().toggleClass("existsLocal", true);
cell.setEnabled(false);
}
})
}
})

Instead you could bind to column with template. You have only rows binding and table does not know the columns.
BTW, You can define "enable" property of the checkbox with formatters. You need factory only for addStyleClass when necessary
So something like that: http://jsbin.com/poyetoqa/1/edit

See my edited solution in the original question. If you have a better working solution feel free to answer. Meanwhile I'll mark the question as answered.

Related

DataTable does not re-draw after updating rows

I'm using the DataTables library to create a table with extra functionality. What I'd like to achieve, is that the user can select rows and then press a button. This will call the server, do some stuff and the rows should be updated accordingly.
However, after I'm done iterating over the rows to be changed and setting its values, re-drawing the table does not actually update its values. I update the data object, invalidate the cache and call table.draw(). It's very similar to the last example on this page.
I have created a JSFiddle of this issue. The button updates the date objects of the selected rows and the table is re-drawn, but the data inside the table is not updated. The core JS code:
$('#updateRow').click(function() {
//Get the table
var table = $('#example').DataTable();
//Iterate over selected rows
var rowData = table.rows({
selected: true
}).every(function() {
//For every selected row, update startDate
var d = this.data();
d.startDate = "01/01/2017";
console.log('Set startDate of ' + d.name + ' to ' + d.startDate);
//Invalidate the cache
this.invalidate();
});
//Re-draw the table
table.draw();
});
I forked and did the solution from your JsFiddle. Here's the relevant snippet from fiddle https://jsfiddle.net/k38r9be5/1/
var rowData = table.rows({
selected: true
}).every(function(rowIdx) {
var colIdx = 4; // startDate is the fifth column, or "4" from 0-base (0,1,2,3,4...)
table.cell( rowIdx, colIdx).data('01/01/2017').draw();
});
Basically, via the API you can get the cell object itself, and modify the contents with .data(). In your version you weren't actually getting a particular cell object and instead just copied the data contents of the row to a variable, and modified that.

add a new empty line in ui-grid

I'm trying to add a new empty line in ui-grid. I've tried looking in different tuto and example, but all that I found didn't reply to my spec, and I wasn't able to adapt it to what I'm looking for.
In fact I'm looking how to add a new empty line in an existing ui-grid neither using a button outside the grid nor a button in the rowfooter.
I'm looking to add a abutton in the grid like the + button shown in the screen shot below
or may be render automatically a new empty line when the rendering the ui-grid and a new one when all rows were filled.
I tried doing that using function in cell template but it's not working.
any help is really appreciated
The first option sounds like more of a CSS issue to me. Essentially, the add button would use some sort of font library containing a +, and you would need to position it in the corner of the grid. Perhaps looking at the grid footer would be a starting place. It sounds like you've seen the basics of creating an add row button here: http://ui-grid.info/docs/#/tutorial/112_swapping_data
The second option (render automatically a new empty line when the rendering the ui-grid and a new one when all rows were filled) requires a JavaScript approach.
The basic logic I followed is:
(Assume) Some data loads from somewhere in a backend (in this sample, it's a simulated load returning a promise as $http or $resource would).
After that data is loaded, we append a new row. We wait for the data first; otherwise we'd not be pushing the new row to the correct location.
Upon completion of the edit action, we set a timeout to ensure subsequent edits on other cells do not keep firing a new row. If the timeout is reached, we append a new row. If a subsequent edit action occurs, and a timeout promise exists (for adding a new row), we cancel it. Once no edit actions occur, and the timeout is reached, we push the new row.
To ensure that we are only taking action when our "extra row" is modified, when we create a row, a reference is maintained to the current row such that we can evaluate whether or not a received event is of interest (var newRowTimeoutPromise).
The core logic in code is below, with a sample implementation in Plnkr:
var extraRow = null,
addNewTimeoutMillis = 2000,
newRowTimeoutPromise = null;
loadData().then(function(data) {
$scope.gridOpts.data = data;
}).finally(function() {
// add initial empty row, and set our reference to it
extraRow = addEmptyRow($scope.gridOpts.data);
})
$scope.gridOpts = {
showGridFooter: true,
onRegisterApi: function(gridApi) {
$scope.gridApi = gridApi;
// listen for cell edit completion
gridApi.edit.on.afterCellEdit($scope, function(rowEntity, colDef, newValue, oldValue) {
// test if the edited row was the "extra row"
// otherwise, and edit to any row would fire a new row
// Set a timeout so we don't create a new row if the user has
// not finished their edit(s) on other fields
newRowTimeoutPromise = $timeout(function() {
if (rowEntity == extraRow) {
// add a new empty row, and set our reference to it
extraRow = addEmptyRow($scope.gridOpts.data);
newRowTimeoutPromise = null;
}
}, addNewTimeoutMillis);
})
// Listen for cell edit start, and cancel if we have a pending new
// row add. Otherwise, each time you finish the edit on a cell,
// this will fire.
gridApi.edit.on.beginCellEdit($scope, function(rowEntity, colDef, newValue, oldValue) {
if (newRowTimeoutPromise != null) {
$timeout.cancel(newRowTimeoutPromise);
}
})
}
};
http://plnkr.co/edit/IMisQEHlaZDCmCSpmnMZ?p=preview
I used jQuery to fetch and change style of specific cell elements of the cell template.
Here is a helpful Fiddle
Here is the controller script : -
var app = angular.module('app', ['ngTouch', 'ui.grid']);
app.controller('MainCtrl', ['$scope',
function($scope) {
$scope.gridOptions = {};
$scope.Add = function() {
$scope.gridOptions.data.push( { firstName: ' ',lastName:'',company:'' });
$(".ui-grid-coluiGrid").prevObject["0"].activeElement.style.display="none";
$(".ui-grid-cell")[$scope.gridOptions.data.length-2].style.display="inline";
};
$scope.gridOptions.onRegisterApi = registerGridApi;
function registerGridApi(gridApi) {
$scope.gridApi= gridApi
};
$scope.gridOptions.columnDefs = [{
name: 'firstName',
field: 'firstName',
}, {
name: 'lastNamer',
field: 'firstName'
}, {
name: 'ShowScope',
cellTemplate: '<button id="btb" ng-click="grid.appScope.Add()">+</button>'
}];
$scope.gridOptions.data = [{ yourdata}];
}
]);
To make it work properly 2 more things have to be done
Use cellContentEditable to make the rows editable
In order to disable display style of cell template button that appears on cells corresponding to rows of already existing data,you could use angular foreach or a for loop to iterate through these rows and disable style(I tried using renderContainers but it always returns the length of rendered rows outside Add functions as 0).
I have a working plunker over here.
http://plnkr.co/edit/Vnn4K5DcCdiercc22Vry?p=preview
In columnDefs, I have defined a separate column for add:
{
name: 'add',
displayName: '',
enableCellEdit: false,
enableColumnMenu: false,
width: '3%',
cellTemplate: '<div class="ui-grid-cell-contents" ng-click="grid.appScope.addRow()"><span ng-click="grid.appScope.addRow()">Add</span></div>'
}
And
$scope.addRow= function(){
var newlist = {"remarks":'',"testName":''};
$scope.gridOptions.data.push(newlist);
}
Update: A second plunker with bootstrap icons for add/remove
http://plnkr.co/edit/FjsA2r?p=preview

How to prevent kendo grid reload?

I have two kendo grids (A, B) ...B its loaded by clicking in A and the its data source its loaded by a property of selected row in A ..
A.accounts
dataSource: {
data: this.gridA.dataItem(this.gridA.select()),
},
My problem its that in B I have to change a property using the grid, but when the property its changed the grid its reloaded and if i have 1000 rows when I change the property in the grid (anyrow, 999 i.e), the scroll back to the beginning
...I have read and try to understand it, but a can't fix it... according to the official documentation, the row selected by
var row = this.gridApprovals.select();
var caBean = this.gridApprovals.dataItem(row);
Its a ObservableObject, so, when I try to change a property, some method (of ObservableOject) it's making this behaviour, and the grid its reloaded, its iterating every data source item...
EDITED:
I forgot to mention.. its important keep the relation between the changes of the B grid and its relation in A slection..
Here is a Dojo Project http://dojo.telerik.com/uYemI/2
How about this?
http://dojo.telerik.com/#Stephen/aNije
I removed the k-rebind attribute and instead wire up the dataSource inside your setCarsGrid() method:
$scope.setCarsGrid = function() {
var row = this.brandGrid.select();
var brand = this.brandGrid.dataItem(row);
var brandObj=brand.toJSON();
var dataSource = new kendo.data.DataSource({
data: brand.cars,
schema:{
model:{
fields:{
id:{editable:false},
model:{editable:false},
color:{type:"string"}
}
},
parse: function(response) {
$.each(response, function(idx, elem) {
elem.id = elem.id+10;
//if you put a break point here, every time you try to edit 'color' field.. will be stop because datasourse is iterated again
console.log("Grid has been reloaded and the editable field 'color' can be edited :'( the id property has been added +10")
});
return response;
}
}
});
// This first time this is called, the carsGrid has not been initialized, so it doesn't exist.
// But subsequent times, we just need to set the new datasource.
if (this.carsGrid) {
this.carsGrid.setDataSource(dataSource);
}
$scope.carsGridOptions = {
dataSource: dataSource,
selectable:true,
editable: true,
columns: [{
field: "id",
},{
field: "model",
},{
field: "color",
}]
};
}
Essentially, I am just separating initialization of the GRID from the setting of the grid's DATASOURCE.
The first time through, you set up the grid options AND set the dataSource and then subsequent times through, we just set the grid to the new dataSource.
This avoids the k-rebind behaviour...which I believe is documented: http://docs.telerik.com/kendo-ui/AngularJS/introduction#widget-update-upon-option-changes
This approach is not suitable for dataBound widgets because those widgets are recreated on each change of their data—for example, after paging of the Grid.
Note, I don't really know angular so there may be a better way to organize this code, but it works.

Assigning selected rows as other grid datasource

I am working on setting up a scenario as following:
1) User is shown existing results on first grid
2) User can select multiple results and click an 'Edit' button which will extract the selected items from the first grid
3)Second grid will be populated with the rows the user has selected from the first grid and will allow them to make edits to the content
4)Pressing save will update the results and show the first grid with the rows updated
So far using drips and drabs of various forum threads (here and here), I have managed to accomplish the first two steps.
$("#editButton").kendoButton({
click: function () {
// extract selected results from the grid and send along with transition
var gridResults = $("#resultGrid").data("kendoGrid"); // sourceGrid
var gridConfig = $("#resultConfigGrid").data("kendoGrid"); // destinationGrid
gridResults.select().each(function () {
var dataItem = gridResults.dataItem($(this));
gridConfig.dataSource.add(dataItem);
});
gridConfig.refresh();
transitionToConfigGrid();
}
});
dataItem returns what i am expecting to see with regards to the selected item(s) - attached dataItem.png. I can see the gridConfig populating but with blank rows (gridBlankRows.png).
gridConfig setup:
$(document).ready(function () {
// build the custom column schema based on the number of lots - this can vary
var columnSchema = [];
columnSchema.push({ title: 'Date Time'});
for(var i = 0; i < $("#maxNumLots").data("value"); ++i)
{
columnSchema.push({
title: 'Lot ' + i,
columns: [{
title: 'Count'
}, {
title: 'Mean'
}, {
title: 'SD'
}]
});
}
columnSchema.push({ title: 'Comment'});
columnSchema.push({ title: 'Review Comment' });
// build the datasource with CU operations
var configDataSource = new kendo.data.DataSource({
transport: {
create: function(options) {},
update: function(options) {}
}
});
$("#resultConfigGrid").kendoGrid({
columns: columnSchema,
editable: true
});
});
I have run out of useful reference material to identify what I am doing wrong / what could be outstanding here. Any help/guidance would be greatly appreciated.
Furthermore, I will also need functionality to 'Add New' results. If possible I would like to use the same grid (with a blank datasource) in order to accomplish this. The user can then add rows to the second grid and save with similar functionality to the update functionality. So if there is any way to factor this into the response, I would appreciate it.
The following example...
http://dojo.telerik.com/EkiVO
...is a modified version of...
http://docs.telerik.com/kendo-ui/framework/datasource/crud#examples
A couple of notes:
it matters if you are adding plain objects to the second Grid's dataSource (gridConfig.dataSource.add(dataItem).toJSON();), or Kendo UI Model objects (gridConfig.dataSource.add(dataItem);). In the first case, you will need to pass back the updated values from Grid2 to Grid1, otherwise this will occur automatically;
there is no need to refresh() the second Grid after adding, removing or changing its data items
both Grid dataSources must be configured for CRUD operations, you can follow the CRUD documentation
the Grid does not persist its selection across rebinds, so if you want to preserve the selection in the first Grid after some values have been changed, use the approach described at Persist Row Selection

Create Dynamic checkboxes by iterating JSONArray in DOJO

I am new to DOJO and I am using DOJO 1.5.0 version.
I want to create dynamic checkboxes by iterating JSONArray in DOJO. This dynamic creation will be done on a particular event like when we selecting some combo-box value.
For example consider below JSONArray:
var tempJSONArray = [{role_id = '1', role_name = 'role1'},{role_id =
'2', role_name = 'role2'},{role_id = '3', role_name = 'role3'}]
I want display checkboxes corresponding to Role Name. Lable for checkbox will be role_name and value will be role_id from JSON object.
Also I want to add 'onChange' event to check-boxes, as on this event I want to do a Ajax call to server with role_ids.
Thank you for reading and also please let me know if further clarification is needed.
You can use the dojo.create() method for creating DOM nodes and the dojo.place() method for placing them inside another element, for example:
var checkbox = dojo.create("input", {
type: "checkbox",
value: "1"
});
You will probably have to wrap it inside a <label> to show the text (which you can also create with dojo.create()). Then you can place them inside a list, for example:
dojo.place(label, "checkboxes");
This will wrap the checkbox (or label) inside an element with ID checkboxes. So, if you create your checkboxes now inside a loop, it should work.
After doing some internet surfing and playing around code I able write below code, which working fine for my scenario. So thought it will be helpful for others.
for ( var i = 0; i < roleJSON.length; i++) {
var role = roleJSON[i];
var tr = dojo.create("tr",{id:"rolebasecheckbox"+i,'class':'rolebasecheckbox'});
alert("tr=" + tr);
var td= dojo.create("td",{innerHTML:'<input id="roleBaseCb_'+i+'" value="'+role.role_id+'" dojoType="dijit.form.CheckBox" onclick="alert('onclick event')" type="checkbox" /> <label for="roleBaseCb_'+i+'">'+role.role_name+'</label>',align:'center'},tr);
alert("td=" + td);
if(i==0){
alert("i is 0");
dojo.place(tr, "roleBaseComboTR", "after");
}else{
dojo.place(tr, "rolebasecheckbox"+(i-1), "after");
}
}

Categories

Resources