Are there other events that can be registered with dojo/form/Select, except onChange?
I'd need to execute a callback function every time user selects an option, even though he selects the same option as it was selected last time. The options I have tried: onSelect, onClick did not work.
var spatialSelectionStore = new Memory({
data: [
{ label: "Rectangle", id: "RECT" },
{ label: "Polygon", id: "POLY" },
{ label: "Circle", id: "CIRC" },
{ label: "Freehand", id: "FREE" }
]
});
var os = new ObjectStore({ objectStore: spatialSelectionStore });
spatialQuerySelect = new Select({
id: "selectionType",
style: { width: "100px" },
store: os,
onChange: activateDrawTool
}, "cp_selectByShapeId");
spatialQuerySelect.startup();
I found a way to do this, and while it may not be the best way to do it, it seems to work.
I set up an aspect to fire a function after the Select._setValueAttr function executes, which is fired by the widget every time you click on either the menu drop-down or a drop-down item. Because of this, I added a check to make sure the function callback only fires when you click on a menu item (i.e. after the menu has closed). I also had to delete the onChange callback you added to Select manually, as this interfered with the aspect.
HTML
<div id="foo"></div>
JavaScript
require(["dojo/aspect", "dojo/store/Memory", "dijit/form/Select", "dojo/data/ObjectStore", "dojo/dom-construct", "dojo/dom", "dojo/aspect"], function(aspect, Memory, Select, ObjectStore, domConstruct, dom, aspect) {
var spatialSelectionStore = new Memory({
data: [
{ label: "Rectangle", id: "RECT" },
{ label: "Polygon", id: "POLY" },
{ label: "Circle", id: "CIRC" },
{ label: "Freehand", id: "FREE" }
]
});
var os = new ObjectStore({ objectStore: spatialSelectionStore });
spatialQuerySelect = new Select({
id: "selectionType",
style: { width: "100px" },
store: os
}, "cp_selectByShapeId");
spatialQuerySelect.startup();
aspect.after(spatialQuerySelect, "_setValueAttr", function() {
if(spatialQuerySelect.dropDown.isShowingNow === false) {
alert(spatialQuerySelect.get('value'));
}
});
domConstruct.place(spatialQuerySelect.domNode, dom.byId("foo"), "first");
});
Fiddle
Aspects can be very powerful, but if you use too many and rely on them too heavily, you can end up with a horrible mess of spaghetti code, so I recommend you use them sparingly, and only when necessary.
In case you're not familiar with what they do, you can tell an aspect to fire before, after, or around another method, and the aspect will "listen" to that method being fired and behave appropriately with your function callback. Further documentation.
spatialQuerySelect.dropDown.on("execute",function() {
alert(spatialQuerySelect.get('value'));
});
this would also work for all option.
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
Related
I have an ExtJS 4 dataView and i would like to catch the id of a selected node.
It is the first time that i'm using the dataView, then, there are some troubles.
The store is loaded correctly and i see the datas into the view very well. The problem which i'm having, concern the "classic" actions of update and delete, particularly getting the id of a selected item.
For example into a grid i click, then select a record and through a button's pressing i open a window (or other actions) with a loaded form (by sending in AJAX to the store, the id of the selected row) and i update the datas.
I'm not still able to do it with the ExtJS 4 dataView.
Below my dataView:
dataView_player = Ext.create('Ext.Panel', {
id: 'images-view',
frame: true,
collapsible: false,
autoWidth: true,
title: 'Giocatori (0 items selected)',
items: [ Ext.create('Ext.view.View', {
id:'players-view',
store: store_player,
multiSelect: true,
height: 310,
trackOver: true,
overItemCls: 'x-item-over',
itemSelector: 'div.thumb-wrap',
emptyText: 'Nessun giocatore visualizzato',
tpl: [
'<tpl for=".">',
'<div class="thumb-wrap" id="{id}-{name}">',
'<div class="thumb">',
'<img src="/img/players/{picture}" title="{name} {surname}" alt="{name} {surname}" style="">',
'</div>',
'<span class="" style="height:30px;">{general_description}{name} {surname}</span>',
'</div>',
'</tpl>',
'<div class="x-clear"></div>'
],
plugins: [
Ext.create('Ext.ux.DataView.DragSelector', {}),
Ext.create('Ext.ux.DataView.LabelEditor', {dataIndex: 'name'})
],
prepareData: function(data) {
Ext.apply(data, {
name: data.name,
surname: data.surname,
general_description: Ext.util.Format.ellipsis(data.general_description, 15)
});
return data;
},
listeners: {
'selectionchange': function(record, item, index, e) {
var node = this.getNode(record); //this.getNode(record);
console.log(node.get('id'));
}
}
}) ],
dockedItems: [{
xtype: 'toolbar',
items: [{
iconCls: 'delete',
text: 'Cancella Selezionati',
scale: 'medium',
tooltip: 'Per <b>cancellare</b> i giocatori selezionati',
tooltipType: 'qtip',
id: 'delete-player',
disabled: true,
handler: delete_news
}, '-', {
iconCls: 'edit',
text: 'Aggiorna Selezionata',
scale: 'medium',
tooltip: 'Per <b>aggiornare</b> un giocatore selezionato',
tooltipType: 'qtip',
disabled: false,
id: 'update-player',
handler: function(nodes) {
var l = nodes.get('id');
console.log(l);
}
}
}
]
}]
});
Of course, this is a wrong example (because the listeners don't work) but it's just to make an idea.
There are two main things what i would like to do:
1) Catch the id (and other store's fields) of the selected item on the action "selectionchange". Obviously, now it doesn't work because of this: node.get('id'). Of course it's a wrong syntax but make up the idea of my will.
2) Catch the id of the selected item on the handler event of the "update-player" button. As above, the issue is the nodes.get('id'). Further trouble, is how to pass the selected item's features. in handler: function(nodes) { the nodes variable does not assume any value and i don't know how to pass the params from the dataview to the handler function.
I hope that somebody will able to help me.
According to the docs the selectionchange event provides the selection model as well as the array of selected records, so you are probably assuming the wrong parameters in your listener.
Without doing further testing, I think it should be something like this:
listeners: {
'selectionchange': function(selModel, selection, eOpts) {
var node = selection[0];
console.log(node.get('id'));
}
}
Note that you're using multiSelect: true, so it could be more than one record in the selection array.
Answer for second part of the question:
In button handler, you need to get selection model of the view and from it get information about selected records:
handler: function(nodes) {
// find view component
var view = dataView_player.down('dataview');
// get all selected records
var records = view.getSelectionModel().getSelection();
// process selected records
for(var i = 0; i < records.length; i++) {
console.log(records[i].getId());
}
}
I am working in extjs4. i have extjs view as-
Ext.define('Balaee.view.kp.Word.SearchWord', {
extend:'Ext.form.Panel',
id:'WordId',
alias:'widget.SearchWord',
bodyPadding: 30,
defaults:{
margin:'0 20 0 70'
},
layout: {
type: 'hbox'
},
items:[
{
xtype:'textfield',
fieldLabel:'Enter the Word:',
name:'Word',
// height:30,
border:false,
allowBlank:false,
emptyText: 'Enter the word',
id:'wordtext'
},
{
xtype: 'image',
width: 30,
height: 22,
src: 'http://www.asien-news.de/wp-content/uploads/new-york.jpg',
}
],});
I want to call controller's function on click of above image. So how to catch image click event in controller?
You can use listeners propetry from Observable class;
A config object containing one or more event handlers to be added to
this object during initialization. This should be a valid listeners
config object as specified in the addListener example for attaching
multiple handlers at once.
Simply bind it on the underlying element, like this:
listeners: {
el: {
click: function() {
Ext.Msg.alert("Image clicked");
}
}
}
here is a example.
I have created a View by extending Ext.container.Container and have given it an alias widget.myCustomView. Since I'm using it in different places.
The view as usual Ext form components like textfield, dataview, etc. Now I adding this view into other view using xtype as follows:
{
xtype: 'myCustomView',
itemId: 'myCustomView'
}
Now, I want to add change event handler such that if any component's change is fired, I can fire the change event of myCustom view. In short, do something like this.
{
xtype: 'myCustomView',
itemId: 'myCustomView',
listeners: {
'change' : function(viewObj, eOpts) {
//do something
}
}
}
How to do it?
Use the relayEvents() method to... well, relay the change event from child fields.
Here's some basic code that does that:
Ext.define('My.Container', {
extend: 'Ext.Container'
,layout: 'form'
,initComponent: function() {
this.callParent(arguments);
// i want to support nested containers
this.parseContainerItems(this);
}
,onItemAdded: function(item) {
if (item instanceof Ext.Container) {
this.parseContainerItems(item);
} else if (item instanceof Ext.form.field.Base) {
this.relayEvents(item, ['change']);
}
}
,parseContainerItems: function(ct) {
if (ct.items) {
ct.items.each(this.onItemAdded, this);
}
}
});
Example usage:
Ext.create('My.Container', {
renderTo: 'ct' // render to a test div
,height: 200
,width: 200
,items: [{
xtype: 'textfield', name: 'foo', fieldLabel: 'Foo'
},{
xtype: 'container'
,items: [{
xtype: 'checkbox', name: 'bar', fieldLabel: 'Bar'
}]
}]
,listeners: {
change: function(item, newValue, oldValue) {
console.log(Ext.String.format('Value of item {0} changed from {1} to {2}', item.name, oldValue, newValue));
}
}
});
Going further...
As I've said my implementation is quite rudimentary since it only supports fields that are added to the container by configuration. If you want to make that component flexible, you'll have to handle fields that are added after the component creation.
For that you'll need to watch the add event of the container to relay from fields that are added after its creation. The doc says that this event bubbles from child containers, but from my tests it does not :-( So (until that's fixed?) you'll also have to watch the add event of child containers.
Here's the updated code for the parseContainerItems() method:
parseContainerItems: function(ct) {
ct.on('add', function(me, item) {
this.onItemAdded(item);
}, this);
if (ct.items) {
ct.items.each(this.onItemAdded, this);
}
}
If you also want to support the possibility of removing fields dynamically, that's when things will go awry... You'd have to implement your own version of relayEvents() because, as far as I know, it is not possible to stop relaying events with the one provided by Ext. Then you'd have to watch the remove event to remove the listeners you've added to child fields and containers.
I'm beginning development of an app in extjs. I'm using the MVC approach, as provided in the extjs documentation.
I have some dynamic data which needs to present the user with a set of accordion controls. I've got the data in a store, but I do not know how to dynamically create the accordion items (unlike grid panels, there doesn't appear to be a store data method).
Here is my current accordion view code - with static items:
Ext.define('BP.view.induction.LeftPage', {
extend: 'Ext.Panel',
alias : 'widget.leftpage',
title: "Left Page",
layout: {
type: 'accordion',
align: 'stretch'
},
layoutConfig: {
// layout-specific configs go here
titleCollapse: true,
animate: true,
activeOnTop: true
},
items: [{
xtype: 'panel', // fake hidden panel, so all appear collapsed
hidden: true,
collapsed: false
},{
xtype: 'panel',
title: 'Panel 1',
html: 'Panel content!'
},{
xtype: 'panel',
title: 'Panel 2',
html: 'Panel content!'
},{
xtype: 'panel',
title: 'Panel 3',
html: 'Panel content!'
}]
});
Any guidance on how to achieve the above would be appreciated, thank you.
[Edit] In response to sra's request, here is my controller:
Ext.define('BP.controller.Induction', {
extend: 'Ext.app.Controller',
views: [
'induction.Binder'
],
stores: [
'Sections',
'Categories',
'Tasks'
],
init: function() {
console.log('Initialized Induction!');
}
});
I should note here that this controller loads a parent view, which in turn loads the LeftPage view - I'm not sure if this creates any scoping issues. Furthermore, as you can see, more than one store is loaded.
You can do like this (untested example with some tweaks)
Ext.define('BP.view.induction.LeftPage', {
extend: 'Ext.Panel',
alias : 'widget.leftpage',
title: "Left Page",
layout: null,
layoutConfig: null,
store: null,
attentive: true,
initComponent: function() {
var me = this;
// begin edit
// only set the store if is is not already defined
me.store = me.store ? me.store : Ext.StoreMgr.lookup('Sections'); // you may change this to any storename you want
// end edit
me.layout = { // don't set objects directly in class definitions
type: 'accordion',
align: 'stretch'
};
me.layoutConfig = { // dont set objects directly in class definitions
titleCollapse: true,
animate: true,
activeOnTop: true
};
me.callParent(arguments);
if (me.attentive) {
me.store('load', me.onRebuildContent, me);
if (me.store.count() == 0)
me.store.load();
} else {
me.buildContent();
}
},
buildContent: function() {
var me = this;
function addItem(rec) {
me.add({
xtype: 'panel',
title: rec.get('titleProperty'),
html: rec.get('bodyProprty')
});
};
me.store.each(addItem);
},
onRebuildContent: function() {
var me = this;
me.removeAll();
me.buildContent();
}
});
Your store will need at least two properties; one for the title and one for the content. And the store need to be loaded before you should instantiate this. But that can easily be done within your controller.
Edit based on comment and new OP info:
Well your view is a bit out of control of the controller. So I recommend you to simply use the StoreManager to receive a valid instance (I've edited the code, I just didn'T know the correct store to use). The StoreManager will know about the store as long as you list him within a controller (StoreManager is capable of much more, but that is all you need to know at the moment). For a further release you could also use the mixin bindable which would manage a storebinding more clean and enables you to update your accordion after the store receives new data (get updated)
Edit for readability
I've just cleaned it up a bit and included a switch param attentive which would allow you to use this component as directly bound to a store, reacting on all load events or as a sort of static one where the store should already be loaded. All in all this should give you a start without making it to complex.
Developing a Sencha Touch MVC app that pulls data from json store (thats set up to a DB pulling out content from a Wordpress Blog).
Everything works up until my "detail" panel. Instead of it listening to the TPL, its just dumping some data. The data looks similar to my blog post, but is filled with other code and doesn't make much sense.
Here is a lean version of my list:
myApp.views.PostListView = Ext.extend(Ext.Panel, {
postStore: Ext.emptyFn,
postList: Ext.emptyFn,
id:'postlistview',
layout: 'card',
initComponent: function () {
/* this.newButton = new Ext.Button({
text: 'New',
ui: 'action',
handler: this.onNewNote,
scope: this
});*/
this.topToolbar = new Ext.Toolbar({
title: 'All Posts',
/* items: [
{ xtype: 'spacer' },
this.newButton
],*/
});
this.dockedItems = [ this.topToolbar ];
this.postList = new Ext.List({
store: myApp.stores.postStore,
grouped: true,
emptyText: '<div style="margin:5px;">No notes cached.</div>',
onItemDisclosure: true,
itemTpl: '<div class="list-item-title">{title}</div>' +
'<div class="list-item-narrative"><small>{body}</small></div>',
});
this.postList.on('disclose', function (record) {
this.onViewPost(record);
}, this),
this.items = [this.postList];
myApp.views.PostListView.superclass.initComponent.call(this);
},
onViewPost: function (record) {
Ext.dispatch({
controller: myApp.controllers.masterController,
action: 'viewpost',
post: record
});
},
});
And here is the "detail" view that is called on disclosure:
myApp.views.PostSingleView = Ext.extend(Ext.Panel, {
title:'Single Post',
id:'postsingleview',
layout:'card',
style:'background:grey;',
initComponent: function () {
this.new1Button = new Ext.Button({
text: 'Back',
ui: 'back',
handler: this.onViewList,
scope: this,
dock:"left"
});
this.top1Toolbar = new Ext.Toolbar({
items: [
this.new1Button
],
title: 'Single Posts',
});
this.postSinglePanel = new Ext.Panel({
layout:'fit',
flex:1,
scroll: 'vertical',
style:'padding:10px;background:yellow;',
itemTpl: '<tpl for=".">' +
'<div class="list-item-narrative">{body}</div>' +
'</tpl>',
});
this.dockedItems = [ this.top1Toolbar, this.postSinglePanel ];
myApp.views.PostSingleView.superclass.initComponent.call(this);
},
onViewList: function () {
Ext.dispatch({
controller: myApp.controllers.masterController,
action: 'viewlist',
});
},
});
And here is the controller that its talking to:
Ext.regController('masterController', {
'index': function (options) {
if (!myApp.views.mainView) {
myApp.views.mainView = new myApp.views.MainView();
}
myApp.views.mainView.setActiveItem(
myApp.views.postView
);
},
'viewpost': function (options) {
myApp.views.postSingleView.postSinglePanel.update(options.post);
myApp.views.postView.setActiveItem(
myApp.views.postSingleView,
{ type: 'slide', direction: 'left' }
);
},
});
myApp.controllers.masterController = Ext.ControllerManager.get('masterController');
When the data comes out, it looks similar to this:
http://i.imgur.com/QlQG8.png
(the black boxes are "redacted" content, no error code there).
In closing, I believe that the controller is "dumping" the data into "MyApp.views.PostSingleView" rather than formatting it as I request in the TPL, though I'm not sure how to fix it. Any and all help MUCH appreciated!
UPDATE: As requested, here is the RegModel:
Ext.regModel("CategoryModel", {
fields: [
{name: "id", type: "int"},
{name: "title", type: "string"},
{name: "body", type: "string"},
],
hasMany: {
model: 'Post',
name: 'posts'
}
});
And here is a sample of the json:
{
"status":"ok",
"post":{
"id":1037,
"type":"post",
"slug":"post-title",
"url":"http:\/\/localhost:8888\/jsontest\/PostTitle\/",
"status":"publish",
"title":"Post Title",
"title_plain":"Post Title",
"content":"<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br \/>\nLorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<\/p>\n<!-- PHP 5.x -->",
"excerpt":"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat [...]",
"date":"2011-07-29 14:17:31",
"modified":"2011-08-30 01:33:20",
"categories":[
{
"id":87,
"slug":"the-category",
"title":"The Category",
"description":"",
"parent":17,
"post_count":5
}
],
"tags":[
],
"author":{
"id":2,
"slug":"tom",
"name":"tom",
"first_name":"tom",
"last_name":"",
"nickname":"",
"url":"",
"description":""
},
"comments":[
],
"attachments":[
],
"comment_count":0,
"comment_status":"open"
},
"previous_url":"http:\/\/localhost:8888\/jsontest\/next-post\/",
"next_url":"http:\/\/localhost:8888\/jsontest\/prev-post\/"
}
Use the tpl config option of the Ext.Panel not the itemTpl which doesn't exist.
As someone has mentioned before, be careful when using a Model instance and the update method, you will need to use the model's data property.
Try using this:
myApp.views.postSingleView.postSinglePanel.update(options.post.data);
the reason is that post does not actually expose the underlying data directly, you need to use the property data for that.
Also any particular reason why you are docking the postSinglePanel? I would be very careful using too many docked items as they are a known source of bugs and layout issues.
A simple way is to write your own method to update child panels (you can also see to override the default update method)
myApp.views.PostSingleView = Ext.extend(Ext.Panel, {
initComponent: function () {
// [...]
},
// [...]
myUpdate: function(data) {
this.postSinglePanel.update(data);
this.doComponentLayout(); // not sure if necessary...
}
});
and from your controller:
Ext.regController('masterController', {
// [...]
'viewpost': function (options) {
myApp.views.postSingleView.myUpdate(options.post.data); // note the .data
// [...]
},
});