IG - how to register changes to cell being edited - eg. to action shortcut key (Oracle Apex Interactive Grid) - keyboard-shortcuts

We have common functionality across multiple pages: Cancel/Save/Back/Next (buttons).
I added shortcut keys via app-level static JavaScript file: Alt-C/S/B/N (using common CSS on the buttons & jQuery binds).
Problem: when using shortcut key whilst editing a cell, the edits to the cell were not saved with Alt-S, Alt-N. (For same reason, no confirmation requested on Cancel/Back, since the grid did not see any changes.)
Much searching and reading of documentation did not reveal the answer...
After a huge amount of effort: inspecting of widget/ interactiveGrid/ model/ getCurrentView via the console & inspect-panes (partly because I didn't find/inspect the .interactiveGrid('getViews','grid).getColumns() for a long time), I found a solution, but I feel there must be a more elegant one. Advice sought! TIA.

Here is my solution. Note the namespace is lps, and I have aliased apex.jQuery as lps.$ prior to the code below. I include comments for my Oracle colleagues less experienced with JS, Apex, jQuery, etc.
The solution works for all 4 shortcut keys, and should work for any IG (possibly even without a static id on the containing region, although my test case has a static id). There is some supporting code after the main solution. All happening on page load via the static JS file.
The jQuery selectors must only bind to a single button on each page!
lps.$(window).on('keydown',function($evt) // Capture Alt + shortcut key in keydown event
{
// $evt is the jQuery simplified event
if( $evt.altKey ) // the alt-key is being pressed-and-held
{
var key = $evt.key.toUpperCase();
if( lps.shortcuts[key] )
{
if( lps.$($evt.target).is('input,textarea') ) // effect the onchange() JavaScript (more specifically, the IG version thereof)
{
// We've pressed a shortcut key combo, whilst editing a cell, so the IG-onchange-y event hasn't yet fired
var tgtEle = $evt.target,
newVal = tgtEle.value, // the value of the on-screen element, regardless of change-status
tgtId = tgtEle.id, // this tallies with an elementId exposed via the igCols below
activeFld;
// IG stuff - igId - get the IG overall id via an IG ancestor div of the target field, then losing the trailing _ig suffix
var igId = lps.$(tgtEle).parents('div.a-IG')[0].id.replace(/_ig$/,'')
igWidget= apex.region(igId).widget(),
igGrid = igWidget.interactiveGrid('getViews','grid'),
igModel = igGrid.model,
igView = igWidget.interactiveGrid('getCurrentView'),
igRecId = igView.getActiveRecordId(),
igCols = igGrid.getColumns(); // this provides meta information about the columns in the IG
for( var i=-1; ++i<igCols.length; ) // find the field name of this IG-column from target element id
{
if( igCols[i].elementId === tgtId )
{
activeFld = igCols[i].property;
break;
}
}
// Phew. Eventually setValue on the IG-cell so Apex knows the cell has changed without an onchange firing
igModel.setValue(igModel.getRecord(igRecId),activeFld,newVal);
}
$evt.originalEvent.preventDefault(); // we're consuming the event, so stop it doing some native browser trick we don't want
lps.$(lps.shortcuts[key].selector).select(); // select the to-be-clicked-element
lps.$(lps.shortcuts[key].selector).click(); // synthesise a click on the selector'd element
}
}
});
Supporting code to pre-establish what our 4 shortcut keys are to be:
lps.shortcuts = { // keys and their jQuery selectors
B : { selector:'button.nav-back' },
C : { selector:'button.nav-cancel' },
N : { selector:'button.nav-next' },
S : { selector:'button.nav-save' },
};
Code to add hover-title to the buttons:
// Advertise the shortcut with a 'title' attribute (appears as popup tip when hover over the element)
for( var key in lps.shortcuts ){
lps.$(lps.shortcuts[key].selector).attr('title','Alt+'+key);
}

Related

extjs 4.1 how to reset the itemselector

I am using extjs 4.1.1a for developing some application.
I had a form consisting of two combo-boxes and an item-selector.
Based on the value selected in first combo-box , the itemselector will load its data from database. This is working fine.
My problem is, if i reselect the first combo-box the new data will be displayed in itemselector along with previous data displayed in itemseletor .That is previous data displayed in itemselector will remain there itself.
for example: name "test1" consists of ids 801,2088,5000. on selecting test1 in firstcombobox itemselector must show output as below.
and if "test2" consists of ids 6090,5040. on selecting test2 in firstcombobox itemselector must show output as below.
problem is. for first time if i select "test1" from firstcombobox , output will come as expected. if i reselect "test2" from firstcombobox , output will come as below.
as you can see, previous data displayed (marked in red rectagle) remains there itself with new data displayed (marked with green rectangle).
I want for every reselection of first combobox, previously displayed data in itemselector to be erased before printing new data on itemselector.
How can I reset the itemselector for every reselection of first combobox?
You should remove all items from the store of the itemselector by the removeAll command. After that you should load the store of the itemselector.
itemselector.store.removeAll();
itemselector.store.load();
Any solutions above solve my problem.
i found solution from Sencha Forum.
https://www.sencha.com/forum/showthread.php?142276-closed-Ext-JS-4.0.2a-itemselector-not-reloading-the-store
in the itemselector.js file, change the line marked below.
populateFromStore: function(store) {
var fromStore = this.fromField.store;
// Flag set when the fromStore has been loaded
this.fromStorePopulated = true;
// THIS LINE BELOW MUST BE CHANGED!!!!!!!!!!!!
fromStore.loadData(store.getRange()); //fromStore.add(store.getRange());
// setValue waits for the from Store to be loaded
fromStore.fireEvent('load', fromStore);
},
You need to insert...
this.reset();
at the head of the function that is inserting the data.
As an example...
Ext.override( Ext.ux.ItemSelector, {
setValue: function(val) {
this.reset();
if (!val) return;
val = val instanceof Array ? val : val.split(this.delimiter);
var rec, i, id;
for (i = 0; i < val.length; i++) {
var vf = this.fromMultiselect.valueField;
id = val[i];
idx = this.toMultiselect.view.store.findBy(function(record){
return record.data[vf] == id;
});
if (idx != -1) continue;
idx = this.fromMultiselect.view.store.findBy(function(record){
return record.data[vf] == id;
});
rec = this.fromMultiselect.view.store.getAt(idx);
if (rec) {
this.toMultiselect.view.store.add(rec);
this.fromMultiselect.view.store.remove(rec);
}
}
}
});
are u got it?
when u select that combobox frist stoe of item selector is null after store load with ur pass the para meters
for example
store.load(null),
store.proxey.url='jso.php?id='+combobox.getrawvalue(),
store.load();
like that so when ur select a value in ur combobox that time ur used a listeners to ur combobox in that listners ur used above code , select ur some value in combobox that time frist store is get null after ur pass some values to json.php then store load with responce so that time old data is remove and new data load in that store
if u post ur code i will give correct code
I ran into the same issue with ExtJS 4.2.1. I got it to work by calling reload() on the data store and then setValue() with an empty string on the item selector in the data store's reload() callback.
Ext.create("Ext.form.field.ComboBox", {
// Other properties removed for brevity
listeners: {
change: function(field, newValue, oldValue, eOpts) {
Ext.getStore("ExampleStore").reload({
callback: function() {
Ext.getCmp("ExampleItemSelector").setValue("");
}
});
}
}
});
Ext.create("Ext.data.Store", {
storeId: "ExampleStore",
// Other properties removed for brevity
});
Ext.create("Ext.form.FormPanel", {
// Other properties removed for brevity
items:[{
xtype: "itemselector",
id: "ExampleItemSelector",
// Other properties removed for brevity
}]
});
For any folks that are curious, I'm fairly convinced there's a bug in the item selector's populateFromStore() function. When the function is called, it blindly adds all of the values from the bound store (store) to the internal store (fromStore). I suspect there should be a call to fromStore.removeAll() prior to the call to fromStore.add(). Here's the relevant code from ItemSelector.js.
populateFromStore: function(store) {
var fromStore = this.fromField.store;
// Flag set when the fromStore has been loaded
this.fromStorePopulated = true;
fromStore.add(store.getRange());
// setValue waits for the from Store to be loaded
fromStore.fireEvent('load', fromStore);
},
EDIT 12/18/2013
If you've configured any callback events on the item selector (e.g. change), you may want to disable the events temporarily when you call setValue(""). For example:
var selector = Ext.getCmp("ExampleItemSelector");
selector.suspendEvents();
selector.setValue("");
selector.resumeEvents();
I had the same problem and finally I decided to modify the extjs source code, not considering it a big issue as extjs itself its saying in the start of the file
Note that this control will most likely remain as an example, and not as a core Ext form
control. However, the API will be changing in a future release and so should not yet be
treated as a final, stable API at this time.
Based on that, as jstricker guessed (and sadly I didn't read and took me a while to arrive to the same conclusion), adding fromStore.removeAll() before fromStore.add() solves the problem.
Outside of the problem (but I think it can be interesting as well), additionally, I also added listConfig: me.listConfig in the MultiSelect configuration (inside createList), that way it's possible to format each item additional options (such as images, etc.) setting in the 'itemselector' the option listConfig as it's explained in the (irrealistic) documentation.
Need to reset the store used in ItemSelector that can be done by setting Empty object like below. Also need to call clearValue() method of ItemSelector component.
store.setData({});
ItemSelectorComponent.clearValue();

OpenTok - How to publish/unpublish manually?

I looked at these links
http://www.tokbox.com/opentok/api/tools/js/documentation/overview/publish.html
http://www.tokbox.com/opentok/api/tools/js/tutorials/overview
but their are no examples for publishingunpublishing manually, that is, publishing/unpublishing without using 'streamCreated'/'streamDestroyed' event handler respectively.
The reason I want to do this is that I have a button to publish/unpublish so that the user can do it at will.
Is there a way to do this?
Yes and it is very simple. Check out the prepublish source code to see how. There are 2 functions, startPublishing() and stopPublishing() which achieve this.
Primarily they use session.publish(publisher);to publish and session.unpublish(publisher); to unpublish.
Here is code I have used to work off:
// Called by a button to start publishing to the session
function startPublishing() {
if (!publisher) {
var parentDiv = document.getElementById("myCamera");
var publisherDiv = document.createElement('div'); // Create a div for the publisher to replace
publisherDiv.setAttribute('id', 'opentok_publisher');
parentDiv.appendChild(publisherDiv);
var publisherProps = {
width : VIDEO_WIDTH,
height : VIDEO_HEIGHT
};
publisher = TB.initPublisher(apiKey, publisherDiv.id, publisherProps); // Pass the replacement div id and properties
session.publish(publisher);
show('unpublishLink');
hide('publishLink');
}
}
//Called by a button to stop publishing to the session
function stopPublishing() {
if (publisher) {
session.unpublish(publisher);
}
publisher = null;
show('publishLink');
hide('unpublishLink');
}

Calling member functions on click/tap within sencha touch 2 templates

I am rather new to sencha touch, I've done a lot of research and tutorials to learn the basics but now that I am experimenting I have run into a problem that I can't figure out.
I have a basic DataList which gets its data from a store which displays in a xtemplate.
Within this template I have created a member function which requires store field data to be parsed as a parameter.
I would like to make a thumbnail image (that's source is pulled from the store) execute the member function on click/tap.
I can't find any information on this within the docs, does anyone know the best way to go about this?
Here is a code example (pulled from docs as I can't access my actual code right now).
var tpl = new Ext.XTemplate(
'<p>Name: {name}</p>'
{
tapFunction: function(name){
alert(name);
}
}
);
tpl.overwrite(panel.body, data);
I want to make the paragraph clickable which will then execute the tapFunction() member function and pass the {name} variable.
Doing something like onclick="{[this.tapFunction(values.name)]} " does not seem to work.
I think functions in template are executed as soon as the view is rendered so I don't think this is the proper solution.
What I would do in your case is :
Add a unique class to your < p > tag
tpl : '<p class="my-p-tag">{name}</p>'
Detect the itemtap event on the list
In your dataview controller, you add an tap event listener on your list.
refs: {
myList: 'WHATEVER_REFERENCE_MATCHES_YOUR_LIST'
},
control: {
myList: {
itemtap: 'listItemTap'
}
}
Check if the target of the tap is the < p > tag
To do so, implement your listItemTap function like so :
listItemTap: function(list,index,target,record,e){
var node = e.target;
if (node.className && node.className.indexOf('my-p-tag') > -1) {
console.log(record.get('name'));
}
}
Hope this helps

Which events are attached to an element?

How can I receive all events attached to an element with dojo?
dojo.query('#mydiv') // which events does #mydiv has?
To get all events on a DOM element:
// Get my div
myDiv = dojo.byId("myDiv");
// Obtain all event-related attributes
var events = dojo.filter(
myDiv.attributes,
function(item) {
return item.name.substr(0, 2) == 'on';
}
);
// Execute first found event, just for fun
eval(events[0].value);
If you get myDiv using dojo.query, remember that dojo.query returns an array, so your element would be in myDiv[0].
This solution does not work with events attached with dojo.connect. There probably is a way to extract this info from Dojo inner workings, but you would have to delve into the source code to understand how.
Another option is that you explicitly manage all dojo.connect events with a global registry. You could use dojox.collections to make this easier. For example, creating a global registry whose keys will be the dom nodes, and values will be the handles returned by dojo.connect (these handles contain the dom node, the type of event and the function to execute):
// On startup
dojo.require(dojox.collections.Dictionary);
eventRegistry = new dojox.collections.Dictionary();
...
// Registering an event for dom node with id=myDiv
var handle1 = dojo.connect(dojo.byId("myDiv"), "onclick", null, "clickHandler");
// Check if event container (e.g. an array) for this dom node is already created
var domNode = handle1[0];
if (!eventRegistry.containsKey(domNode))
eventRegistry.add(domNode, new Array());
eventRegistry.item(domNode).push(handle1);
...
// Add another event later to myDiv, assume container (array) is already created
var handle2 = dojo.connect(dojo.byId("myDiv"), "onmouseover", null, "mouseHandler");
eventRegistry.item(domNode).push(handle2);
...
// Later get all events attached to myDiv, and print event names
allEvents = eventRegistry.item(domNode);
dojo.forEach(
allEvents,
function(item) {
console.log(item[1]);
// Item is the handler returned by dojo.connect, item[1] is the name of the event!
}
);
You can hide the annoying check to see if event container is already created by creating a subclass of dojox.collections.Dictionary with this check already incorporated. Create a js file with this path fakenmc/EventRegistry.js, and put it beside dojo, dojox, etc:
dojo.provide('fakenmc.EventRegistry');
dojo.require('dojox.collections.Dictionary');
dojo.declare('fakenmc.EventRegistry', dojox.collections.Dictionary, {
addEventToNode : function(djConnHandle) {
domNode = djConnHandle[0];
if (!this.containsKey(domNode))
this.add(domNode, new Array());
this.item(domNode).push(djConnHandle);
}
});
Using the above class you would have to dojo.require('fakenmc.EventRegistry') instead of 'dojox.collections.Dictionary', and would simply directly add the dojo connect handle without other checks:
dojo.provide('fakenmc.EventRegistry');
eventRegistry = new fakenmc.EventRegistry();
var handle = dojo.connect(dojo.byId("myDiv"), "onclick", null, "clickHandler");
eventRegistry.addEventToNode(handle);
...
// Get all events attached to node
var allEvents = eventRegistry.item(dojo.byId("myDiv"));
...
This code is not tested, but I think you get the idea.
If its only for debugging purpose. You can try dijit.byId("myId").onClick.toString(); in your firebug console and you can see the entire onclick code this works even if the function is anonymous you can view the content of anonymous content.

Dojo DnD Move Node Programmatically

I would like to know if there is a way to move the node programmatically in dojo Dnd? The reason is I would like to revert the changes to the drag and drop when the web service call triggered a failed save on the database. Here is what I have so far.
In my code, the node Id seems to be unrecognized by the dojo.dnd.Container.DelItem. I cannot just use the selected item on the target because this is a asynchronous webservice function callback. So the user may be selecting another node on the container when this is called.
function OnMoveWSComplete(strResult) {
var resultObj = eval('(' + strResult + ')');
var sourceContainer = eval('(' + objResult.sourceContainerId + ')');
var targetContainer = eval('(' + objResult.targetContainerId + ')');
var targetNodes = targetContainer.getAllNodes();
for (var iNode = 0; iNode < targetNodes.length; iNode++) {
var currId = getLastIdFromStr(targetNodes[iNode].id);
if (currId == resultObj.Id) {
var targetN = targetNodes[iNode];
var Name = targetNodes[iNode].childNodes[0].nodeValue;
targetContainer.delItem(targetNodes[iNode].id);
var origData = { Id: resultObj.Id, Name: Name };
sourceContainer.insertNodes(true, origData);
break;
}
}
}
EDIT: Solution (Thanks Eugene Lazutkin) [2009/11/30]:
/**
* Move one node from one container to the other
*/
function moveNode(nodeId, sourceContainer, targetContainer) {
var node = dojo.byId(nodeId);
// Save the data
var saveData = sourceContainer.map[nodeId].data;
// Do the move
sourceContainer.parent.removeChild(node);
targetContainer.parent.appendChild(node);
// Sync the DOM object → map
sourceContainer.sync();
targetContainer.sync();
// Restore data for recreation of data
targetContainer.map[nodeId].data = saveData;
}
It looks like you assume that delItem removes physical nodes. Take a look at the documentation — probably you want to move nodes between containers instead of deleting them from the map. One simple way to do that just to move DOM nodes between containers, and call sync() on both containers.
Addition: Here is a super-simple pseudocode-like example:
function move(node, source, target){
// normalize node and/or node id
node = dojo.byId(node);
// move it physically from one parent to another
// (from target to source) adding to the end
target.parent.appenChild(node);
// now it is moved from source to target
// let's synchronize both dojo.dnd.Source's
source.sync();
target.sync();
}
Or something along these lines should work. The important pieces:
Move node from one parent to another using any DOM operations you deem appropriate. I used appendChild(), but you can use insertBefore(), or anything else.
Synchronize both sources involved after the move.
Obviously it works if both sources use nodes of the same type and structure. If not, you should do something more complex, e.g., move everything you need emulating a real DnD move by publishing topics described in the documentation.
I have this function which moves selected nodes by button click:
source.forInItems(dojo.hitch(this, function(item, id, map) {
if (dojo.hasClass(id, "dojoDndItemAnchor")) {
target.onDrop(source, [ dojo.byId(id) ], false);
dojo.removeClass(id, "dojoDndItemAnchor");
}
}));
onDrop() is an overridable method, which is called upon item drop, and by default calls to method onDropExternal(source, nodes, copy).
I am doing the same thing right now. I was able to solve by doing the following.
Set the dnd/Source autoSync property to true
<div data-dojo-type="dojo.dnd.Source" accept="widget" class="source" data-dojo-props="autoSync: true">
After dragging it looses the dndtype so I had to re-add it using client side code. Also I remove the dojoDndItemAnchor class after drop.
$(node).removeClass('dojoDndItemAnchor').attr('dndtype', 'widget');