I'm trying to make the TAB key navigate on my dGrid. I have used as a base the solution found at Dgrid set focus on cell, but there are a couple of issues I'm running into which I couldn't solve so far.
Below you can find the block I'm using now; Not all columns have editors, so for I added a var do the element definition to select the next column instead of doing a right. I also added support for SHIFT+TAB to make backwards navigation possible. MT4.prje.grids[gridId]is the dGrid instance. There might be various on the page.
The grid is created with
MT4.prje.grids[gridId] = new (declare([OnDemandGrid, Keyboard, Selection, CellSelection]))(gridInfo, gridId);
where gridInfo has the column definitions and the store. The store is created as:
new Observable(new Memory({'data': {}, 'idProperty': 'id'}));
The editors are usually TextBox, NumberTextBox and Select dijit widgets, all set to autoSave.
aspect.after(MT4.prje.grids[gridId], "edit", function (promise, cellNode) {
if (promise === null) return;
promise.then(function (widget) {
if (!widget._editorKeypressHandle) {
widget._editorKeypressHandle = on(widget, "keypress", function (e) {
for (var rowId in MT4.prje.grids[gridId].selection) {
break;
}
for (var columnId in MT4.prje.grids[gridId].selection[rowId]) {
break;
}
if (e.charOrCode == keys.TAB) {
e.preventDefault();
var cellToEdit = null,
cellEdited = MT4.prje.grids[gridId].cell(rowId, columnId);
if (e.shiftKey) {
if (cellEdited.column.previousEditor === undefined) {
rowId = parseInt(rowId) - 1;
if (MT4.prje.grids[gridId].row(rowId).element !== null) {
for (var lastColumnId in MT4.prje.grids[gridId].columns) {}
cellToEdit = MT4.prje.grids[gridId].cell(rowId, lastColumnId);
}
} else {
cellToEdit = MT4.prje.grids[gridId].cell(rowId, cellEdited.column.previousEditor);
}
} else {
if (cellEdited.column.nextEditor === undefined) {
var firstColumnId = null;
rowId = parseInt(rowId) + 1;
if (MT4.prje.grids[gridId].row(rowId).element === null) {
var fields = {};
for (var cId in MT4.prje.grids[gridId].columns) {
if ((cId != 'excluir') && (firstColumnId === null)) {
firstColumnId = cId;
}
fields[cId] = '';
}
MT4.prje.addRowToGrid(gridId, fields);
} else {
for (var cId in MT4.prje.grids[gridId].columns) {
if (cId != 'excluir') {
firstColumnId = cId;
break;
}
}
}
cellToEdit = MT4.prje.grids[gridId].cell(rowId, firstColumnId);
} else {
cellToEdit = MT4.prje.grids[gridId].cell(rowId, cellEdited.column.nextEditor);
}
}
if (cellToEdit) {
MT4.prje.grids[gridId].deselect(cellEdited);
MT4.prje.grids[gridId].select(cellToEdit);
MT4.prje.grids[gridId].edit(cellToEdit);
}
}
});
}
});
});
Even ignoring the new line part, there are a couple of errors that happen. First of all, the editor barely pops into existence and them disappears, together with the selection. Sometimes when tabbing to an empty column, the editor will be filled with the values of the previous editor. Is there a way to do it more consistently?
What I'm figuring is that there is a race condition happening on the sharedEditor (they are set to editOn: focus). I tried wrapping the deselect/select on a dojo.on('blur') and emit it. But that doesn't get consistently correct with the dijit/form/Select widgets. Is there a better event that I can call for it?
I also tried changing the final block to:
if (cellToEdit) {
on(cellToEdit.element, 'focus', function(){
MT4.prje.grids[gridId].select(cellToEdit);
});
on(cellEdited.element, 'blur', function(){
MT4.prje.grids[gridId].deselect(cellEdited);
on.emit(cellToEdit.element, 'focus', {'bubble': true, 'cancelable': false});
});
on.emit(cellEdited.element, 'blur', {'bubble': true, 'cancelable': false});
}
But that gives two errors:
If I do make changes to a cell it does not go to the next editor. Does not even select it.
The first time I move from an empty cell to another empty cell it doesn't work either.
Anyone got any ideas?
This fix works on dgrid 0.3.11.
Add to your dgrid's postCreate.
postCreate: function() {
var that = this;
this.inherited(arguments);
this.on('dgrid-datachange', function(evt) {
that._selectedCell = that.cell(evt);
});
aspect.after(this, 'save', function(dfd) {
dfd.then(function() {
var nextCell = that.right(that.cell(that._selectedCell.row.id, that._selectedCell.column.id));
that.edit(nextCell);
// Bonus Fix. Workaround dgrid bug that blocks field text to be selected on focus.
nextCell.element.widget && nextCell.element.widget.textbox && nextCell.element.widget.textbox.select();
});
});
}
Related
I have the JS code below which filters based on checkboxes being checked or not (I don't think you need to see all the HTML because my question is rather simple/general, I think). All this code works fine, but I added a new function at the bottom (I noted it in the code) that simply has an uncheck all button for one of the sets of checkboxes (because there are like 30 checkboxes and I don't want the user to have to uncheck them all manually).
Anyway, the new script works properly too, except that the overall unrelated script that compares all checkboxes needs to run each time the new Uncheck All/Check All button is clicked.
Is there a simple way to make sure all the other JS runs when this new script is run?
I could be wrong, but I think I just need to somehow trigger this function inside the NEW FUNCTION:
$checkboxes.on('change', function() {
but am not sure how to do that.
ALL JS:
<script>
$(window).load(function(){
Array.prototype.indexOfAny = function(array) {
return this.findIndex(function(v) {
return array.indexOf(v) != -1;
});
}
Array.prototype.containsAny = function(array) {
return this.indexOfAny(array) != -1;
}
function getAllChecked() {
// build a multidimensional array of checked values, organized by type
var values = [];
var $checked = $checkboxes.filter(':checked');
$checked.each(function() {
var $check = $(this);
var type = $check.data('type');
var value = $check.data('value');
if (typeof values[type] !== "object") {
values[type] = [];
}
values[type].push(value);
});
return values;
}
function evaluateReseller($reseller, checkedValues) {
// Evaluate a selected reseller against checked values.
// Determine whether at least one of the reseller's attributes for
// each type is found in the checked values.
var data = $reseller.data();
var found = false;
$.each(data, function(prop, values) {
values = values.split(',').map(function(value) {
return value.trim();
});
found = prop in checkedValues && values.containsAny(checkedValues[prop]);
if (!found) {
return false;
}
});
return found;
}
var $checkboxes = $('[type="checkbox"]');
var $resellers = $('.Row');
$checkboxes.on('change', function() {
// get all checked values.
var checkedValues = getAllChecked();
// compare each resellers attributes to the checked values.
$resellers.each(function(k, reseller) {
var $reseller = $(reseller);
var found = evaluateReseller($reseller, checkedValues);
// if at least one value of each type is checked, show this reseller.
// otherwise, hide it.
if (found) {
$reseller.show();
} else {
$reseller.hide();
}
});
});
//NEW FUNCTION for "UNCHECK ALL" Button
$(function() {
$(document).on('click', '#checkAll', function() {
if ($(this).val() == 'Check All') {
$('input.country').prop('checked', true);
$(this).val('Uncheck All');
} else {
$('input.country').prop('checked', false);
$(this).val('Check All');
}
});
});
});
New button HTML for the new UNCHECK portion:
<input id="checkAll" type="button" value="Uncheck All">
I kept researching and discovered the trigger() function to handle this.
http://api.jquery.com/trigger/
In HTML5 I have a dropdown menu . When choosing different options I hide or show different parts of my page. Here is that script:
document
.getElementById('target')
.addEventListener('change', function () {
'use strict';
var vis = document.querySelector('.vis'),
target = document.getElementById(this.value);
if (vis !== null) {
vis.className = 'inv';
}
if (target !== null ) {
target.className = 'vis';
}
});
However what I want to do now, in another script is to preload an option from the dropdown. I can do it easily with this script:
setSelectedIndex(document.getElementById('target'),'content_1');
function setSelectedIndex(s, valsearch)
{
// Loop through all the items in drop down list
for (i = 0; i< s.options.length; i++)
{
if (s.options[i].value == valsearch)
{
// Item is found. Set its property and exit
s.options[i].selected = true;
break;
}
}
return;
}
This is where my problem comes up, my dropdow will get the value I want, but the part that I want to be shown when choosing that option won't come up.
That is because change events need to happen from the browser.
When the user commits the change explicitly (e.g. by selecting a value
from a 's dropdown with a mouse click, by selecting a date
from a date picker for , by selecting a file in the
file picker for , etc.);
If your using Jquery you can:
$("#id").val("value").trigger('change');
or you can use javascript if your not worried about building the event object:
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
element.dispatchEvent(evt);
}
else
element.fireEvent("onchange");
I would recommend moving your anonymous onchange function into a named function that you can call once onload, and again onchange.
Here is the function I wrote:
function setContent(id) {
//get the current visible content
var vis = document.querySelector('.vis');
//get the target element by id
var target = document.getElementById(id);
//make current vis element inv
if (vis) vis.className = "inv";
//make target element vis
if (target) target.className = 'vis';
}
and a fiddle
edited: got rid of querySelectorAll to stick closer to OP original code and updated fiddle. clarified and commented code.
The problem you have is changing a vale or the selected value of an input with JavaScript does not trigger any change event. So you would need to manually trigger the event.
function setSelectedIndex(s, valsearch)
{
// Loop through all the items in drop down list
for (i = 0; i< s.options.length; i++)
{
if (s.options[i].value == valsearch)
{
// Item is found. Set its property and exit
s.options[i].selected = true;
break;
}
}
//Setting the selected value with JavaScript does not trigger the change event so you need to manually trigger the change event
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
s.dispatchEvent(evt);
} else {
s.fireEvent("onchange");
}
return;
}
I have a problem with ckeditor widgets. I have inline non-editable text widget which I can drag drop enywhere in editor (using its default functionality). So I need to check where I'm dropping my widget and if this place is undroppable (according my rules it us TABLE) do cancel events propagations and widget should stay on previous place.
editor.widgets.add('customWidgetAdd', {
inline: true,
template: '<span class="simplebox">' +
'<span class="simplebox-title" ></span>' +
'</span>',
init: function(){
var that = this;
that.widgetData = ko.observable(self.activeWidgetData);
var subscription = that.widgetData.subscribe(function (value) {
$(that.element.$).find('.simplebox-title').text(value.name);
if (that.isSelected) {
self.activeWidgetData = value;
}
});
var destroyListener = function(ev){
subscription.dispose();
};
that.once('destroy', destroyListener);
that.on('doubleclick', function (evt) {
editor.execCommand(editAction.command);
});
that.on('select', function (evt){
that.isSelected = true;
self.activeWidgetData = that.widgetData();
});
that.on('deselect', function (evt){
try {
var endContainer = editor.getSelection().getRanges()[0].endContainer.getName();
} catch (e) {
}
that.isSelected = false;
if (endContainer == 'td' || endContainer == 'th') {
//SO here comes the problem. My rule is executed and
//I want CKEDITOR do nothing from here... but stil widget is getting cutted from DOM and inserted to place where I have dropped it...
//that.removeListener('destroy', destroyListener);
//that.removeAllListeners();
evt.cancel();
evt.stop();
return false;
}
});
}
});
Unfortunately there is no easy solution in this situation.
The only one way you can do it is to subscribe to editor's drop event, and cancel it if needed, like:
editor.on('contentDom', function() {
var editable = editor.editable(),
// #11123 Firefox needs to listen on document, because otherwise event won't be fired.
// #11086 IE8 cannot listen on document.
dropTarget = (CKEDITOR.env.ie && CKEDITOR.env.version < 9) || editable.isInline() ? editable : editor.document;
editable.attachListener(dropTarget, 'drop', function(evt) {
//do all checks here
});
});
You can find how it works in CKEditor (See code of function setupDragAndDrop)
Within jQgrid is there a way that a row can be clicked on and then highlighted without the multiselect row checkbox being checked?
I have tried with Multiboxonly = true which was recommended by Oleg https://stackoverflow.com/a/3719637/853607
Any help would be great as this is stopping progress on a critical project.
Maybe you have typed it wrong. You speak Multiboxonly : true, but actuually it is multiboxonly : true in the implementation. This should work if you use JavaScript only version. If you use some wrapper like ASP.NEt or PHP you should consult the docs for these.
Kind Regards
Here is my answer for clicking on a row and not checking the checkbox but the row getting highlighted - returning false ensures onSelectRow is not fired. ps #tony I had multiboxonly in lower case.
beforeSelectRow: function (rowid, e) {
//debugger;
rowId = rowid;
if ($("#jqg__tradesGrid_" + rowid).attr("disabled") || CommentsClicked) {
CommentsClicked = false;
return false;
}
if (e.target.type == "checkbox") {
var blnChecked = $(e.target).is(":checked");
var trElement = jQuery("#" + rowid, jQuery('#_tradesGrid'));
if (!firstLoadWithRecords) {
updateIdsOfSelectedRows(rowid, blnChecked);
}
if (blnChecked || $.inArray(rowid, idsOfSelectedRows) >= 0) {
trElement.removeClass('ui-widget-content');
trElement.addClass('ui-state-highlight');
} else {
trElement.removeClass('ui-widget-content');
trElement.removeClass('ui-state-highlight');
}
} else {
var blnChecked = $(e.target).is(":checked");
var trElement = jQuery("#" + rowid, jQuery('#_tradesGrid'));
trElement.removeClass('ui-widget-content');
trElement.removeClass('ui-state-highlight');
}
//TODO: put in the style to chagne to silver when the checkbox is checked
CommentsClicked = false;
return false;
},
I need to detect changes on page change in page input field of pagingtoolbar in extjs 4.2.
I am going through docs, but can't find any method for it. I have successfully overridden next, previous, &c., buttons, but can't find anything to override page input field. How would you go about this?
I have added afterrender listener on ExtJS Combobox component. You can add accordingly to override input field of paging toolbar. Here is the working code :
'afterrender' : function(thisCombo){
thisCombo.getPicker().pagingToolbar.addListener('change', function() {
var me = this;
thisCombo.getPicker().pagingToolbar.child("#inputItem").addListener('specialkey', function(field, e) {
if (e.getKey() == e.ENTER) {
///// Do your modifications here
var inputItem = thisCombo.getPicker().pagingToolbar.child('#inputItem').getValue();
total = me.getPageData().pageCount;
if (inputItem <= total) {
if (me.fireEvent('beforechange', me, inputItem) !== false) {
me.store.inputItemPage({
// Enter params
});
}
}
}
});
});
}
}
this example my help
As in the code you can all of the texts given that they have setter etc
http://jsfiddle.net/acteon/sZ3y6/1/
Ext.toolbar.Paging.override({
onLoad: function () {
var me = this,
pageData, currPage, pageCount, afterText, count, isEmpty;
count = me.store.getCount();
isEmpty = count === 0;
if (!isEmpty) {
pageData = me.getPageData();
currPage = pageData.currentPage;
pageCount = pageData.pageCount;
afterText = Ext.String.format(me.afterPageText, (isNaN(pageCount) || (pageCount === 0)) ? 1 : pageCount);
} else {
currPage = 0;
pageCount = 0;
afterText = Ext.String.format(me.afterPageText, 1);
}
Ext.suspendLayouts();
me.child('#afterTextItem').setText("my precious text");
// this one is the input field
me.child('#inputItem').setDisabled(isEmpty).setValue(currPage);
me.child('#first').setDisabled(currPage === 1 || isEmpty);
me.child('#prev').setDisabled(currPage === 1 || isEmpty);
me.child('#next').setDisabled(currPage === pageCount || isEmpty);
me.child('#last').setDisabled(currPage === pageCount || isEmpty);
me.child('#refresh').enable();
me.updateInfo();
Ext.resumeLayouts(false);
if (me.rendered) {
me.fireEvent('change', me, pageData);
}
}
});
thanks for your help.
I have another issue, #inputItem field. What event handles the enter/return key? I need to override this function, because I have a disable/enable button.