Dojo - How to set "label" of togglebutton inside a grid from row.data.SOMEFIELD value? - dojo

i'm new to Dojo.
I have a "FullEditable" grid, with many columns, and one of them is a widget, it's a ToggleButton. This grid is used to show details of a purchase, so every line represents a product sold. When i click a button inside one of the columns it launches the product (dialog) browser and then the data of that product is passed to the grid. After some work i managed to display the button and being able to set it's label accordingly to the row.data.FIELD value received from that dialog.
But now i am editing that purchase and i need to be able to do the same, to be able to load the data from the preloaded array with data ("detalle") (as it's label) into the togglebutton, but nothing seems to work, not even the direct assignment of the widget or the row data or anything.
Here's a fragment of my code:
var grid = this;
var selectedRow = grid.row(i);
var cell = grid.cell(selectedRow, 'Moneda');
selectedRow.Moneda = detalle.MonedaItem; //didn't work
if(cell.row !== null && cell.row !== undefined && cell.row.data !== null && cell.row.data !== undefined)
cell.row.data.Moneda = detalle.MonedaItem; //didn't work
if(cell.element !== null && cell.element !== undefined){
cell.element.innerText = detalle.MonedaItem; //didn't work
cell.element.textContent = detalle.MonedaItem; //didn't work
if(detalle.MonedaItem === 'UF')
cell.element.widget.set('checked', true);
if(detalle.MonedaItem === 'CLP')
cell.element.widget.set('checked', false);
cell.element.widget.set('label', detalle.MonedaItem); //didn't work
cell.element.widget.set('value', detalle.MonedaItem); //didn't work
}
}
Whenever i set the value through the change event with "cell.element.widget.set('checked', boolean);" i get an error message from the editor, when it tries to get a cell but the variable is null, so it crashes.
The values of "label" and "value" of the widget, row.data en cell are perfectly set, BUT the grid DISPLAYS the "emptyValue" value of the widget and not the one that is actually set.

AFAIK, best way to change the grid data is to update the store/ collection that is associated with the grid and just invoke grid.refresh(). The new data will be loaded automatically.
The statement cell.row.data.Moneda = detalle.MonedaItem is actually changing the data in your store. Just refresh the grid after this and you are done!
Never try to set something in grid DOM element directly. Always work with store, renderCell and renderColumn. This will ensure consistency in store and grid attributes that reference the store.

I finally managed to get what i need through renderCell like this:
,{
id: 'Moneda',
field: 'Moneda',
label: "Moneda",
renderCell: function (object, value, node, options) {
try {
var button = new ToggleButton({
node: node,
isWidgetInGrid: true,
parameterName: 'codigoMoneda',
label: object.Moneda,
showLabel: true,
onChange: function(newValue){
var grid = this.getParent();
var row = grid.row(this.node);
var data = row.data;
if(this.get('label') === 'CLP')
{
data.Moneda = 'UF';
this.set('label','UF');
this.set('value', 'UF');
}
else
{
data.Moneda = 'CLP';
this.set('label','CLP');
this.set('value', 'CLP');
}
grid.getParent().getParent()._calcularTotales();
}
});
node.appendChild(button.domNode);
}
catch (ex) {
Debug.log(this.declaredClass, arguments, ex);
}
},
emptyValue: 'CLP',
autoSave: true
}

Related

Datatable: when using 3000 records my datatable is getting loading slowly, and when click on buttton on change action not responding immediately

When I click on search button, my page getting freeze, alerts & masking are not working but when page loads completing alerts are popup.
Pleas help me on this
I have 3000 rows in a Datatable:
I Have two radio buttons
Show only Difference
All
When I click any one of button applyFitler function will get execute and executeFilter is filter function that executes when table is getting Draw,
What I expected is, when I click on any of Radio button, Mask with Please wait should show immediately, but it shows before one second (9th second) of returning results to Datatable. Total its taking 10 seconds to apply filter and return Datatable.
During these 10 seconds by browser page getting freeze
var applyFitler = function() {
alertBlock('Please wait..');
// clear the global search box
__DATATABLE.search('');
// set columnIndex to match data attribute of each column (used in our custom search while allowing moving of columns)
$('#devresult th').each(function(index) {
var headerText = $(this).data('col-header');
__TABLEHEADERS[headerText].columnIndex = $(this).data('column-index');
});
if (__DATATABLE != undefined) {
__DATATABLE.draw();
}
alertUnblock();
};
var executeFilter = function(settings, data, dataIndex, rowData, counter) {
var rows = __DATATABLE.rows({ 'search': 'applied' }).nodes();
var showRow= $("input[name=showRow]:checked").val();
if(showRow == "notshow") {
return false;
}
}
return true;
}

How to prevent closing of cell edit mode on validation errors with custom vue components in ag-grid

I have succesfully rendered my own component as the cellEditor and would like and on-leave I would like it to try to validate the value and prevent the closing if it fails.
If I look at this then https://www.ag-grid.com/javascript-grid-cell-editing/#editing-api there's cancelable callback functions for editing. But in this callback function is there a way to access the current instantiated component? I would think that would be the easiest way to handle this.
I'm using vee-validate so the validation function is async, just to keep in mind.
Use Full row editing.
Create a global variable like
var problemRow = -1;
Then Subscribe to this events:
onRowEditingStarted: function (event) {
if (problemRow!=-1 && event.rowIndex!=problemRow) {
gridOptions.api.stopEditing();
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
},
onRowEditingStopped: function (event) {
if (problemRow==-1) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
}
if (problemRow == event.rowIndex) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
else{
problemRow=-1;
}
}
},
I had a similar issue - albeit in AngularJS and the non-Angular mode for ag-grid - I needed to prevent the navigation when the cell editor didn't pass validation.
The documentation is not very detailed, so in the end I added a custom cell editor with a form wrapped around the input field (to handle the niceties such as red highlighting etc), and then used Angular JS validation. That got me so far, but the crucial part was trying to prevent the user tabbing out or away when the value was invalid so the user could at least fix the issue.
I did this by adding a value parser when adding the cell, and then within that if the value was invalid according to various rules, throw an exception. Not ideal, I know - but it does prevent ag-grid from trying to move away from the cell.
I tried loads of approaches to solving this - using the tabToNextCell events, suppressKeyboardEvent, navigateToNextCell, onCellEditingStopped - to name a few - this was the only thing that got it working correctly.
Here's my value parser, for what it's worth:
var codeParser = function (args) {
var cellEditor = _controller.currentCellEditor.children['codeValue'];
var paycodeId = +args.colDef.field;
var paycodeInfo = _controller.paycodes.filter(function (f) { return f.id === paycodeId; })[0];
// Check against any mask
if (paycodeInfo && paycodeInfo.mask) {
var reg = new RegExp("^" + paycodeInfo.mask + '$');
var match = args.newValue.match(reg);
if (!match) {
$mdToast.show($mdToast.simple().textContent('Invalid value - does not match paycode format.').position('top right').toastClass('errorToast'))
.then(function(r) {
_controller.currentCellEditor.children['codeValue'].focus();
});
throw 'Invalid value - does not match paycode format.';
}
}
return true;
};
The _controller.currentCellEditor value is set during the init of the cell editor component. I do this so I can then refocus the control after the error has been shown in the toast:
CodeValueEditor.prototype.init = function (params) {
var form = document.createElement('form');
form.setAttribute('id', 'mainForm');
form.setAttribute('name', 'mainForm');
var input = document.createElement('input');
input.classList.add('ag-cell-edit-input');
input.classList.add('paycode-editor');
input.setAttribute('name', 'codeValue');
input.setAttribute('id', 'codeValue');
input.tabIndex = "0";
input.value = params.value;
if (params.mask) {
input.setAttribute('data-mask', params.mask);
input.setAttribute('ng-pattern','/^' + params.mask + '$/');
input.setAttribute('ng-class',"{'pattern-error': mainForm.codeValue.$error.pattern}");
input.setAttribute('ng-model', 'ctl.currentValue');
}
form.appendChild(input);
this.container = form;
$compile(this.container)($scope);
_controller.currentValue = null;
// This is crucial - we can then reference the container in
// the parser later on to refocus the control
_controller.currentCellEditor = this.container;
$scope.$digest();
};
And then cleared in the grid options onCellEditingStopped event:
onCellEditingStopped: function (event) {
$scope.$apply(function() {
_controller.currentCellEditor = null;
});
},
I realise it's not specifically for your components (Vue.js) but hopefully it'll help someone else. If anyone has done it a better way, I'm all ears as I don't like throwing the unnecessary exception!

How to access or get value of specific graph on chart plot by click event?

I use vue-chartjs to draw some chart like line, bar, etc.
In my project, there are many cases using specific value or lable of data in chart.
Using tooltip option of vue-chartjs, I can check that value or label of data item when hovered.
I want to know how to access or get information of specific data matched with point on graph when clicked(not hovered).
Here is my code about chart options.
chartOptions: {
responsive: false,
onClick: function(evt){
//Some logic to get value of label of specific data...(type, label, value, ...)
}
In my case, I use 'onclick' option to access specific data on point triggered 'click' event. In 'onClick' callback, I checked all of chart elements and dataset, etc.
How can I get value of label specific dataItem on point of graph(like line) or bar of graph(like bar) when triggered click event?
I was not able to find a solution that worked for me, but I dug a little bit and this is what I came up with.
onClick: function(evt, array) {
if (array.length != 0) {
var position = array[0]._index;
var activeElement = this.tooltip._data.datasets[0].data[position]
console.log(activeElement);
} else {
console.log("You selected the background!");
}
}
This will get the position in the array that you clicked and grab the data from what position you clicked. This may not be the prettiest or best example, but it worked for me.
This solution use the getElementAtEvent method of chartjs, but to use that you need reference to the Chart itself, not the Vue component. We can get that from the $data._chart property. To use this in a parent Vue component, we use the $refs as seen below`.
So parent defines the chart options
{
...
options: {
onClick: this.handleChartClick
}
...
}
and then parent method, using $refs with $data._chart to get the chart. We get the datasetIndex and value and also the tooltip
handleChartClick(evt, elements) {
var chart = this.$refs.periodChart.$data._chart;
const chartIndex = chart.getElementAtEvent(evt);
if (chartIndex.length !== 0) {
const datasetIndex = chartIndex[0]._datasetIndex;
const position = chartIndex[0]._index;
const info = {
datasetIndex: datasetIndex,
valueIndex: position,
label: chart.tooltip._data.labels[position],
value: chart.tooltip._data.datasets[datasetIndex].data[position]
};
console.log(info);
} else {
console.log("Background clicked");
}

Dojo dijit tree with checkbox is not keyboard accessible

I have created a dijit.Tree object where every node is a checkbox. When you select/deselect the parent node, the child nodes get selected/deselected;
when one of the children is deselected, the parent gets deselected; when all the children are selected, the parent gets selected. It works perfectly fine.
However I need it to be keyboard accessible. When I navigate to the tree nodes and press spacebar or Enter, nothing happens.
I tried adding tabindex and aria-role to the checkbox (programmatically), but it did not work.
Here is the fiddle - http://jsfiddle.net/pdabade/pyz9Lcpv/65/
require([
"dojo/_base/window", "dojo/store/Memory",
"dijit/tree/ObjectStoreModel",
"dijit/Tree", "dijit/form/CheckBox", "dojo/dom",
"dojo/domReady!"
], function(win, Memory, ObjectStoreModel, Tree, checkBox, dom) {
// Create test store, adding the getChildren() method required by ObjectStoreModel
var myStore = new Memory({
data: [{
id: 'allDocuments',
name: 'All Documents'
}, {
id: 'inboxDocuments',
name: 'Inbox Documents',
parent: 'allDocuments'
}, {
id: 'outboxDocuments',
name: 'Outbox Documents',
parent: 'allDocuments'
}, {
id: 'draftDocuments',
name: 'Draft Documents',
parent: 'allDocuments'
}, {
id: 'finalDocuments',
name: 'Final Documents',
parent: 'allDocuments'
}],
getChildren: function(object) {
return this.query({
parent: object.id
});
}
});
// Create the model
var myModel = new ObjectStoreModel({
store: myStore,
query: {
id: 'allDocuments'
}
});
// Create the Tree.
var tree = new Tree({
model: myModel,
autoExpand: true,
getIconClass: function(item, opened) {
// console.log('tree getIconClass', item, opened);
// console.log('tree item type', item.id);
},
onClick: function(item, node, event) {
//node._iconClass= "dijitFolderClosed";
//node.iconNode.className = "dijitFolderClosed";
var _this = this;
console.log(item.id);
var id = node.domNode.id,
isNodeSelected = node.checkBox.get('checked');
dojo.query('#' + id + ' .dijitCheckBox').forEach(function(node) {
dijit.getEnclosingWidget(node).set('checked', isNodeSelected);
});
if (item.id != 'allComments') {
if (!isNodeSelected) {
var parent = node.tree.rootNode; // parent node id
//console.log(node);
parent.checkBox.set('checked', false);
} else {
var parent = node.tree.rootNode;
var selected = true;
var i = 0;
dojo.query('#' + parent.id + '.dijitCheckBox').forEach(function(node) {
if (i > 0) {
var isSet = dijit.getEnclosingWidget(node).get('checked');
console.log(isSet);
if (isSet == false) {
selected = false;
}
}
i++;
});
if (selected) {
parent.checkBox.set('checked', true);
}
}
}
//console.log(node.id);
},
_createTreeNode: function(args) {
var tnode = new dijit._TreeNode(args);
tnode.labelNode.innerHTML = args.label;
console.log(args);
var cb = new dijit.form.CheckBox({
"aria-checked": "false",
"aria-describedby": args.label
});
cb.placeAt(tnode.labelNode, "first");
tnode.checkBox = cb;
return tnode;
}
});
tree.placeAt(contentHere);
tree.startup();
tree.checkedItems();
//tree.expandAll();
});
}
Any ideas as to how to make it keyboard accessible?
Thanks!
Looking into the dijit/Tree source I see that it sets the function _onNodePress() as an event handler for keyboard events. You can override it (or add an aspect after it) and handle the key presses you want manually. It takes as argument the tree node and an event object that you can use to check specifically for the space and the enter key.
I forked your jsfiddle with an example: https://jsfiddle.net/pgianna/jjore5sm/1/
_onNodePress: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// This is the original implementation of _onNodePress:
this.focusNode(nodeWidget);
// This requires "dojo/keys"
if (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE)
{
var cb = nodeWidget.checkBox;
cb.set('checked', !cb.get('checked'));
}
}
Do not add role, aria-checked, nor tabindex to the checkbox. Those are already built into the control, so you are adding risk of breaking it down the road. You can probably also get rid of every role="presentation" as those are on <div>s and <span>s which are presentational by nature. Finally, you need <label> on each block of text that is associated with a checkbox if you want this to be accessible. The aria-describedby is incorrect and is the less good option anyway.
I am getting the error: Uncaught TypeError: tree.checkedItems is not a function (line 159)
You also have a big focus management problem. Put the following in your CSS and you will see that it takes two presses of the Tab key for each single control (if starting at a focused checkbox): :focus {outline:2px solid #f00;}
It looks like you have the containing elements stealing any clicks, meaning the correct element never gets selected. The <span> with classes dijit dijitReset dijitInline dijitCheckBox keeps stealing focus as it toggles its tabindex from -1 to 0, taking itself in and out of the tab order. That may be a factor.
I suggest addressing the script error and then looking at focus management.
With dijit, there's all kinds of stuff going on in the background that might be out of your control. As aardrian said, there's lots of role=presentation and all the aria tags on the <input type='checkbox> are superfluous. dijit is probably (incorrectly) setting all that. An <input type='checkbox> already handles selections and it's role is inherently a checkbox. Those aria properties are for when you're making a custom checkbox out of div/span tags.
There is a native checkbox buried down in the code but it has opacity set to 0 so you can't see it. dijit is probably using it for the checkbox events.
The native checkbox also has data-dojo-attach-event="ondijitclick:_onClick". I'm not sure what that means but anytime I see "click" in an event name, I get suspicious that it might not work with a keyboard.
I tried the example on https://dojotoolkit.org/reference-guide/1.10/dijit/form/CheckBox.html and it works with the keyboard. Hit space will check and uncheck the box. Whether you can see the focus on the checkbox is another issue.
As a side note, it might be nice if your checkbox tree used aria-checked="mixed" for the parent branch. Anytime you have child checkboxes where some are selected and some are not, you can use "mixed" for the parent checkbox to indicate a mixture of selections. Not sure if dijit supports that.

Change styling of dojo treegrid cell if value changes

What I have is a treegrid populated with values from ajax. Every 30 seconds the store is refreshed and new data is displayed.
I need to change the styling (color or background-color) of a treegrid cell when it's value differs from the old one. The requirement is to make the comparison and styling from javascript.
Any ideas on how this could be done ?
You could use dijit.Tree's getRowStyle method to modify the style dynamically. It will be called whenever a row needs to be rendered.
Something like this might get you started:
(function(){ // closure for private variables
var previousValues = {};
var myTree = ... // lookup dijit.Tree via dijit.byId, or create
myTree.getRowStyle = function(item){
var style = {};
var itemId = myTree.store.getIdentity(item);
var newValue = myTree.store.getValue(item, "MY_ITEM_VALUE");
if(newValue !== null &&
previousValues[itemId] !== null &&
previousValues[itemId] !== newValue) {
style.backgroundColor = "#0000FF";
previousValues[itemId] = newValue;
}
return style;
};
})();
There may be better ways to keep track of the previous values, but since your store is getting changed, I really can't think of one.