Behavior of .on().on() vs. only one .on() - event-handling

I want to catch "inward" type events and do ABC, and catch "outward" type events to do XYZ. I'm getting problems with the various events firing at weird times that I didn't expect, so I'm wondering if my approach is actually causing the problem.
<div class="box">first box</div>
<div class="box">second box</div>
$(".box")
.on("focus mouseover mousedown", function(){ /* do ABC */ })
.on("blur mouseout mouseup", function(){ /* do XYZ */ });
I think that stringing the .on() methods together like this makes sure that the blur event is only triggered for that same .box. But other than that, I expect it behaves identically to if I did it this way:
$(".box").on("focus mouseover mousedown", function(){ /* do ABC */ });
$(".box").on("blur mouseout mouseup", function(){ /* do XYZ */ });
or doing something like this: (which seems redundant)
$(".box").on({
focus: function(){ /* do ABC */ },
mouseover: function(){ /* do ABC */ },
mousedown: function(){ /* do ABC */ },
blur: function(){ /* do XYZ */ },
mouseout: function(){ /* do XYZ */ },
mouseup: function(){ /* do XYZ */ }
);
I'm not asking "which is better" but rather, "how are they different"? They seem to be getting different results.

They're different in the sense that your jQuery selector $(".box") only runs once, so chaining them is more efficient. Functionally they're identical.

jQuery function calls return the selector-matched context of this, allowing chaining.
Because of this, your .on() chain will work as you intend.

Related

Detecting right click position on angular leaflet map

I have a mobile page showing a map using angular-leaflet-directive 0.7.11, and have declared my required events like so:
$scope.map = {
events: [
'mousedown',
'contextmenu'
],
...
}
$scope.$on('leafletDirectiveMap.mousedown', function (event) {
debugger;
});
Where the debugger statement is, the event variable contains no information about where the map was clicked. The same event format was provided by the directive when the contextmenu event is triggered.
In fact, if I inspect the entire event variable, it is just an Object, not an Event:
Are the docs wrong? Is the example missing something? How can I obtain the X/Y or Lat/Lng for the particular position that I have right-clicked (tap-hold)?
You need to use the 'leafletEvent'. Try this:
myApp.controller('YourController', ['$scope', 'leafletEvent', function($scope) {
$scope.$on('leafletDirectiveMap.mousedown', function (event, leafletEvent) {
leafletData.getMap().then(function(map) {
map.on('click', function(e) {
console.log('e');
console.log(e);
console.log('e.latlng:');
console.log(e.latlng); // L.LatLng {lat: 19.642587534013046, lng: -4.5703125}
});
});
});
}]);

How do I trigger tinymce 4.x focus and blur events

I realize that this question has been asked and answered several times, but I still can't make many of the solutions work. It seems like most of the discussions are older and maybe not compatible with 4.x.
My goal: be able fire some javascript functions upon focus or blur. Here's the base code:
$('.tiny-mce').tinymce({
script_url : '/xm_js/tinymce4/tinymce.min.js',
});
I tried examples like:
$('.tiny-mce').tinymce({
script_url : '/xm_js/tinymce4/tinymce.min.js',
setup : function(ed) {
ed.onInit.add(function(ed, evt) {
var dom = ed.dom;
var doc = ed.getDoc();
tinymce.dom.Event.add(doc, 'blur', function(e) {
alert('blur!!!');
});
});
}
});
returns:"Uncaught TypeError: Cannot call method 'add' of undefined"
$('.tiny-mce').tinymce({
script_url : '/xm_js/tinymce4/tinymce.min.js',
});
tinymce.activeEditor.on('focus', function(e) {
console.log(e.blurredEditor);
});
returns: "Uncaught TypeError: Cannot call method 'on' of undefined"
(but not sure if I have it in the right place)
$('.tiny-mce').tinymce({
script_url : '/xm_js/tinymce4/tinymce.min.js',
setup: function(editor) {
editor.on('focus', function(e) {
console.log('focus event', e);
});
}
});
returns: "Uncaught SyntaxError: Unexpected identifier"
This works but only when initiating the editor. Other things I've tried but haven't gotten to work:
tinymce.FocusManager
tinymce.activeEditor
What am I missing? Thanks.
UPDATE: found a solution that worked beautifully: Why TinyMCE get focus and blur event, when user jump from other input field?
see the fiddle at http://fiddle.tinymce.com/8Xeaab/1
tinymce.init({
selector: "#editme",
inline: true,
setup: function(editor) {
editor.on('focus', function(e) {
console.log("focus");
});
editor.on('blur', function(e) {
console.log("blur");
});
}
});
Your cuestion title its about TRIGGERING focus and blur events, but you really are asking about HANDLING those events.
If anybody is still looking for triggering the events, the solution should by:
tinymce.init({
selector: "#textarea_123",
...
});
then, to set focus:
var inst = tinyMCE.EditorManager.get('textarea_123');
inst.focus();
I recently ran into this issue and although I am doing this in angularjs I was able to implement a fix for it by creating a directive that registers when you click off of it and then clears the activeElement in the html body. I will add the directive below. You just call it on the object by adding click-off like you would an ng-cloak to a tag.
(function () {
'user strict';
angular.module('app.directives').directive('clickOff', ClickOff);
function ClickOff($parse, $document) {
var directive = {
link: link
};
return directive;
function link(scope, $element, attr) {
var fn = $parse(attr["clickOff"]);
$element.bind('click', function (event) {
event.stopPropagation();
});
angular.element($document[0].body).bind("click", function (event) {
scope.$apply(function () {
$document[0].activeElement.blur();
fn(scope, { $event: event });
});
});
}
}
})();

Meteor: apply function after rendering things from mongodb

I am using sage cell to convert html to math stuff
Template.home.rendered = function(){
\\ apply sagecell and mathjax
}
However, the content that are rendered comes from mongo, so it's sometimes loaded after sage cell is applied to it. I can do something like this
Template.home.rendered = function(){
Deps.autorun(function(){
if (Content.findOne({_id: ...})){
\\ apply sagecell and mathjax
}
});
}
It's better but still doesn't work all the time. Is there other things I can use to detect the content is completely rendered?
Edited with new response:
<template name='pendingAnswer'>
The answer to your question, coming back whenever, is:
{{>answer}}
</template>
<template name='answer'>
{{fromSage}}
</template>
Template.answer.helpers({
fromSage: function () {
Session.get('fromSage');
}
});
Invoked whenever - from a button, from navigating to the page, on blur...
function GetAnswerFromSage(data) {
callHTTP(website,data, callbackFromSage)
}
function callbackFromSage(err, data) {
if (err) then log(err);
Session.set('fromSage', data);
}
Earlier: try transform upon retrieval of mongo:
From Meteor Doc
// An Animal class that takes a document in its constructor
Animal = function (doc) {
_.extend(this, doc);
};
_.extend(Animal.prototype, {
makeNoise: function () {
console.log(this.sound);
}
});
// Define a Collection that uses Animal as its document
Animals = new Meteor.Collection("Animals", {
transform: function (doc) { return new Animal(doc); }
});
// Create an Animal and call its makeNoise method
Animals.insert({name: "raptor", sound: "roar"});
Animals.findOne({name: "raptor"}).makeNoise(); // prints "roar"
The script
<script type='text/javascript' src="http://sagecell.sagemath.org/static/embedded_sagecell.js"></script>
that is supposed to be in the head needs to be removed and instead be loaded after the content is completely loaded like so:
Template.content.rendered = function(){
// sage
Deps.autorun(function(){
if (Session.get('contentChanged')){
// loading this script causes mathjax to run
$.getScript("http://sagecell.sagemath.org/static/embedded_sagecell.js", function(d, textStatus){
if (textStatus=='success'){
// this converts <div class='compute'> to a sage cell
sagecell.makeSagecell({
inputLocation: 'div.compute',
evalButtonText: 'Evaluate',
hide: ['editorToggle']
});
}
})
}
})
and if I go from 1 content template to another content template, it seems that nothing is rerendered and so the mathjax was not applied. The only fix I can think is to force a page reload:
Template.content.events({
'click a': function(evt){
evt.preventDefault();
location.href = evt.currentTarget.href;
}
})
which makes the site much slower, unfortunately.

How to use hammer.js with Angular.js

I'm new with Angular.js and reading i know angular dont have events like tap, double tap, etc. I'm trying to combine with Hammer.js without success.
Code from gist
/**
* angular-hammer.js
* Inspired by AngularJS' implementation of "click dblclick mousedown..."
*
* This ties in the Hammer events to attributes like:
*
* hm-tap="add_something()"
* hm-swipe="remove_something()"
*
* and also has support for Hammer options with:
*
* hm-tap-opts="{hold: false}"
*
* or any other of the "hm-event" listed underneath.
*/
angular.forEach('hmTap:tap hmDoubletap:doubletap hmHold:hold hmTransformstart:transformstart hmTransform:transform hmTransforend:transformend hmDragstart:dragstart hmDrag:drag hmDragend:dragend hmSwipe:swipe hmRelease:release'.split(' '), function(name) {
var directive = name.split(':');
var directiveName = directive[0];
var eventName = directive[1];
angular.module('MYMODULE').directive(directiveName,
['$parse', function($parse) {
return function(scope, element, attr) {
var fn = $parse(attr[directiveName]);
var opts = $parse(attr[directiveName + 'Opts'])(scope, {});
element.hammer(opts).bind(eventName, function(event) {
scope.$apply(function() {
console.log("Doing stuff", event);
fn(scope, {$event: event});
});
});
};
}]);
});
now i've added to my html file
<!--...-->
<script src="/js/jquery-1.9.0.js"></script>
<script src="/js/angular.js"></script>
<script src="/js/hammer.js"></script>
<script src="/js/jquery.hammer.js"></script>
<script src="/js/angular-hammer.js"></script>
<script src="/js/app/controllers.js"></script>
</html>
But when i try to use in my html it doesn't work.
I've tried with
<li hmTap="click($event)">...</li>
<li ngTap="click($event)">...</li>
<li hm-tap="click($event)">...</li>
<li ng-tap="click($event)">...</li>
no one works, can help me with this trouble?
Give this a go.
Add hammer.js and jquery.hammer.js to your solution. With JQuery and Angular.
Something like this:
<script src="js/jquery/jquery.min.js"></script>
<script src="js/hammer/hammer.js"></script>
<script src="js/hammer/jquery.hammer.js"></script>
<script src="js/angular/angular.min.js"></script>
Then use Angular directives to define new HTML attributes.
In this example I am creating a new attribute called on-tap
angular.module('myApp.directives', [])
.directive('onTap', function () {
return function (scope, element, attrs) {
return $(element).hammer({
prevent_default: false,
drag_vertical: false
})
.bind("tap", function (ev) {
return scope.$apply(attrs['onTap']);
});
};
});
You can then create a directive per Hammer event:
hold, tap, doubletap, transformstart, transform, transformend, dragstart, drag, dragend, release, swipe.
Your HTML would then look like this:
<button on-tap="DoAngularMethod()">Tap me</button>
Here is exactly what are you looking for:
https://github.com/wzr1337/angular-gestures
this new libary allows you to implement all of gestures you need.
also read this:
http://www.ng-newsletter.com/posts/angular-on-mobile.html
Hope you find it interesting.
:)
There are some hammer directive implementations for Angular you could try to use:
https://github.com/randallb/angular-hammer
https://github.com/dreame4/angular-hammer
I used randallb lib and i added
'hmPan:pan',
'hmPanLeft:panleft',
'hmPanRight:panright',
in the hmGestures array, so i can use pan too!

How to create custom ExtJS form field component?

I want to create custom ExtJS form field components using other ExtJS components in it (e.g. TreePanel). How can I do it most easily?
I've read docs of Ext.form.field.Base but I don't want to define field body by fieldSubTpl. I just want to write code which creates ExtJS components and maybe some other code which gets and sets values.
Update: Summarized purposes are the followings:
This new component should fit in the
form GUI as a field. It should have
label and the same alignment (label,
anchor) of other fields without need
of further hacking.
Possibly, I have
to write some getValue, setValue
logic. I'd rather embed it into this component than making separated code which copies things into further hidden form fields that I also have to manage.
To extend #RobAgar 's answer, following a really simple Date Time field that I wrote for ExtJS 3 and it's quickport that I made for ExtJS 4. The important thing is the use of the Ext.form.field.Field mixin. This mixin provides a common interface for the logical behavior and state of form fields, including:
Getter and setter methods for field values
Events and methods for tracking value and validity changes
Methods for triggering validation
This can be used for combining multiple fields and let act them as one. For a total custom fieldtype I recommend to extend Ext.form.field.Base
Here is the example that I mentioned above. It should shoe how easy this can be done even for something like a date object where we need to format the data within the getter and setter.
Ext.define('QWA.form.field.DateTime', {
extend: 'Ext.form.FieldContainer',
mixins: {
field: 'Ext.form.field.Field'
},
alias: 'widget.datetimefield',
layout: 'hbox',
width: 200,
height: 22,
combineErrors: true,
msgTarget: 'side',
submitFormat: 'c',
dateCfg: null,
timeCfg: null,
initComponent: function () {
var me = this;
if (!me.dateCfg) me.dateCfg = {};
if (!me.timeCfg) me.timeCfg = {};
me.buildField();
me.callParent();
me.dateField = me.down('datefield')
me.timeField = me.down('timefield')
me.initField();
},
//#private
buildField: function () {
var me = this;
me.items = [
Ext.apply({
xtype: 'datefield',
submitValue: false,
format: 'd.m.Y',
width: 100,
flex: 2
}, me.dateCfg),
Ext.apply({
xtype: 'timefield',
submitValue: false,
format: 'H:i',
width: 80,
flex: 1
}, me.timeCfg)]
},
getValue: function () {
var me = this,
value,
date = me.dateField.getSubmitValue(),
dateFormat = me.dateField.format,
time = me.timeField.getSubmitValue(),
timeFormat = me.timeField.format;
if (date) {
if (time) {
value = Ext.Date.parse(date + ' ' + time, me.getFormat());
} else {
value = me.dateField.getValue();
}
}
return value;
},
setValue: function (value) {
var me = this;
me.dateField.setValue(value);
me.timeField.setValue(value);
},
getSubmitData: function () {
var me = this,
data = null;
if (!me.disabled && me.submitValue && !me.isFileUpload()) {
data = {},
value = me.getValue(),
data[me.getName()] = '' + value ? Ext.Date.format(value, me.submitFormat) : null;
}
return data;
},
getFormat: function () {
var me = this;
return (me.dateField.submitFormat || me.dateField.format) + " " + (me.timeField.submitFormat || me.timeField.format)
}
});
Now that's cool. The other day, I created a fiddle to answer another question before realizing I was off-topic. And here your are, finally bringing to my attention the question to my answer. Thanks!
So, here are the steps required in implementing a custom field from another component:
Creating the child component
Render the child component
Ensuring the child component is sized and resized correctly
Getting and setting value
Relaying events
Creating the child component
The first part, creating the component, is easy. There's nothing particular compared to creating a component for any other usage.
However, you must create the child in the parent field's initComponent method (and not at rendering time). This is because external code can legitimately expect that all dependent objects of a component are instantiated after initComponent (e.g. to add listeners to them).
Furthermore, you can be kind to yourself and create the child before calling the super method. If you create the child after the super method, you may get a call to your field's setValue method (see bellow) at a time when the child is not yet instantiated.
initComponent: function() {
this.childComponent = Ext.create(...);
this.callParent(arguments);
}
As you see, I am creating a single component, which is what you'll want in most case. But you can also want to go fancy and compose multiple child components. In this case, I think it would be clever to back to well known territories as quickly as possible: that is, create one container as the child component, and compose in it.
Rendering
Then comes the question of rendering. At first I considered using fieldSubTpl to render a container div, and have the child component render itself in it. However, we don't need the template features in that case, so we can as well bypass it completely using the getSubTplMarkup method.
I explored other components in Ext to see how they manage the rendering of child components. I found a good example in BoundList and its paging toolbar (see the code). So, in order to obtain the child component's markup, we can use Ext.DomHelper.generateMarkup in combination with the child's getRenderTree method.
So, here's the implementation of getSubTplMarkup for our field:
getSubTplMarkup: function() {
// generateMarkup will append to the passed empty array and return it
var buffer = Ext.DomHelper.generateMarkup(this.childComponent.getRenderTree(), []);
// but we want to return a single string
return buffer.join('');
}
Now, that's not enough. The code of BoundList learns us that there's another important part in component rendering: calling the finishRender() method of the child component. Fortunately, our custom field will have its own finishRenderChildren method called just when that needs to be done.
finishRenderChildren: function() {
this.callParent(arguments);
this.childComponent.finishRender();
}
Resizing
Now our child will be rendered in the right place, but it will not respect its parent field size. That is especially annoying in the case of a form field, because that means it won't honor the anchor layout.
That's very straightforward to fix, we just need to resize the child when the parent field is resized. From my experience, this is something that was greatly improved since Ext3. Here, we just need to not forget the extra space for the label:
onResize: function(w, h) {
this.callParent(arguments);
this.childComponent.setSize(w - this.getLabelWidth(), h);
}
Handling value
This part will, of course, depend on your child component(s), and the field you're creating. Moreover, from now on, it's just a matter of using your child components in a regular way, so I won't detail this part too much.
A minima, you also need to implement the getValue and setValue methods of your field. That will make the getFieldValues method of the form work, and that will be enough to load/update records from the form.
To handle validation, you must implement getErrors. To polish this aspect, you may want to add a handful of CSS rules to visually represent the invalid state of your field.
Then, if you want your field to be usable in a form that will be submitted as an actual form (as opposed to with an AJAX request), you'll need getSubmitValue to return a value that can be casted to a string without damage.
Apart from that, as far as I know, you don't have to worry about the concept or raw value introduced by Ext.form.field.Base since that's only used to handle the representation of the value in an actual input element. With our Ext component as input, we're way off that road!
Events
Your last job will be to implement the events for your fields. You will probably want to fire the three events of Ext.form.field.Field, that is change, dirtychange and validitychange.
Again, the implementation will be very specific to the child component you use and, to be honest, I haven't explored this aspect too much. So I'll let you wire this for yourself.
My preliminary conclusion though, is that Ext.form.field.Field offers to do all the heavy lifting for you, provided that (1) you call checkChange when needed, and (2) isEqual implementation is working with your field's value format.
Example: TODO list field
Finally, here's a complete code example, using a grid to represent a TODO list field.
You can see it live on jsFiddle, where I tries to show that the field behaves in an orderly manner.
Ext.define('My.form.field.TodoList', {
// Extend from Ext.form.field.Base for all the label related business
extend: 'Ext.form.field.Base'
,alias: 'widget.todolist'
// --- Child component creation ---
,initComponent: function() {
// Create the component
// This is better to do it here in initComponent, because it is a legitimate
// expectationfor external code that all dependant objects are created after
// initComponent (to add listeners, etc.)
// I will use this.grid for semantical access (value), and this.childComponent
// for generic issues (rendering)
this.grid = this.childComponent = Ext.create('Ext.grid.Panel', {
hideHeaders: true
,columns: [{dataIndex: 'value', flex: 1}]
,store: {
fields: ['value']
,data: []
}
,height: this.height || 150
,width: this.width || 150
,tbar: [{
text: 'Add'
,scope: this
,handler: function() {
var value = prompt("Value?");
if (value !== null) {
this.grid.getStore().add({value: value});
}
}
},{
text: "Remove"
,itemId: 'removeButton'
,disabled: true // initial state
,scope: this
,handler: function() {
var grid = this.grid,
selModel = grid.getSelectionModel(),
store = grid.getStore();
store.remove(selModel.getSelection());
}
}]
,listeners: {
scope: this
,selectionchange: function(selModel, selection) {
var removeButton = this.grid.down('#removeButton');
removeButton.setDisabled(Ext.isEmpty(selection));
}
}
});
// field events
this.grid.store.on({
scope: this
,datachanged: this.checkChange
});
this.callParent(arguments);
}
// --- Rendering ---
// Generates the child component markup and let Ext.form.field.Base handle the rest
,getSubTplMarkup: function() {
// generateMarkup will append to the passed empty array and return it
var buffer = Ext.DomHelper.generateMarkup(this.childComponent.getRenderTree(), []);
// but we want to return a single string
return buffer.join('');
}
// Regular containers implements this method to call finishRender for each of their
// child, and we need to do the same for the component to display smoothly
,finishRenderChildren: function() {
this.callParent(arguments);
this.childComponent.finishRender();
}
// --- Resizing ---
// This is important for layout notably
,onResize: function(w, h) {
this.callParent(arguments);
this.childComponent.setSize(w - this.getLabelWidth(), h);
}
// --- Value handling ---
// This part will be specific to your component of course
,setValue: function(values) {
var data = [];
if (values) {
Ext.each(values, function(value) {
data.push({value: value});
});
}
this.grid.getStore().loadData(data);
}
,getValue: function() {
var data = [];
this.grid.getStore().each(function(record) {
data.push(record.get('value'));
});
return data;
}
,getSubmitValue: function() {
return this.getValue().join(',');
}
});
Heh. After posting the bounty I found out that Ext.form.FieldContainer isn't just a field container, but a fully fledged component container, so there is a simple solution.
All you need to do is extend FieldContainer, overriding initComponent to add the child components, and implement setValue, getValue and the validation methods as appropriate for your value data type.
Here's an example with a grid whose value is a list of name/value pair objects:
Ext.define('MyApp.widget.MyGridField', {
extend: 'Ext.form.FieldContainer',
alias: 'widget.mygridfield',
layout: 'fit',
initComponent: function()
{
this.callParent(arguments);
this.valueGrid = Ext.widget({
xtype: 'grid',
store: Ext.create('Ext.data.JsonStore', {
fields: ['name', 'value'],
data: this.value
}),
columns: [
{
text: 'Name',
dataIndex: 'name',
flex: 3
},
{
text: 'Value',
dataIndex: 'value',
flex: 1
}
]
});
this.add(this.valueGrid);
},
setValue: function(value)
{
this.valueGrid.getStore().loadData(value);
},
getValue: function()
{
// left as an exercise for the reader :P
}
});
I've done this a few times. Here is the general process/pseudo-code I use:
Create an extension of field that provides the most useful re-use (typically Ext.form.TextField if you just want to get/set a string value)
In the afterrender of the field, hide the textfield, and create a wrapping element around this.el with this.wrap = this.resizeEl = this.positionEl = this.el.wrap()
Render any components to this.wrap (e.g. using renderTo: this.wrap in the config)
Override getValue and setValue to talk to the component(s) you rendered manually
You may need to do some manually sizing in a resize listener if your form's layout changes
Don't forget to cleanup any components you create in the beforeDestroy method!
I can't wait to switch our codebase to ExtJS 4, where these kinds of things are easy.
Good luck!
Since the question was asked rather vague - I only can provide the basic pattern for ExtJS v4.
Even if it's not too specific, it has the advance that it's rather universal like this:
Ext.define('app.view.form.field.CustomField', {
extend: 'Ext.form.field.Base',
requires: [
/* require further components */
],
/* custom configs & callbacks */
getValue: function(v){
/* override function getValue() */
},
setValue: function(v){
/* override function setValue() */
},
getSubTplData: [
/* most likely needs to be overridden */
],
initComponent: function(){
/* further code on event initComponent */
this.callParent(arguments);
}
});
The file /ext/src/form/field/Base.js provides the names of all configs and functions that can be overridden.
Following the documentation at http://docs.sencha.com/ext-js/4-0/#/api/Ext.form.field.Base
This code will create a reusable TypeAhead/Autocomplete style field for selecting a language.
var langs = Ext.create( 'Ext.data.store', {
fields: [ 'label', 'code' ],
data: [
{ code: 'eng', label: 'English' },
{ code: 'ger', label: 'German' },
{ code: 'chi', label: 'Chinese' },
{ code: 'ukr', label: 'Ukranian' },
{ code: 'rus', label: 'Russian' }
]
} );
Ext.define( 'Ext.form.LangSelector', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.LangSelector',
allowBlank: false,
hideTrigger: true,
width: 225,
displayField: 'label',
valueField: 'code',
forceSelection: true,
minChars: 1,
store: langs
} );
You can use the field in a form simply by setting the xtype to the widget name:
{
xtype: 'LangSelector'
fieldLabel: 'Language',
name: 'lang'
}
Many of the answers either use the Mixin Ext.form.field.Field or just extends on some already made class that suits their needs - which is fine.
But I do not recommend fully overwriting the setValue method, that is IMO really bad form!
A lot more happens than just setting and getting the value, and if you fully overwrite it - well you will for instance mess up the dirty state, processing of rawValue etc..
Two options here I guess, one is to callParent(arguments) inside the method you declare to keep things streamlined, or to at the end when you are done apply the inherited method from where ever you got it (mixin or extend).
But do not just overwrite it with no regards for what that already made method does behind the scenes.
Also remember that if you use other field types in your new class - then do set the isFormField property to false - otherwise your getValues method on the form will take those values and run with em!
Another solution could be this tree-field implementation.
It behaves just like a normal form field:
https://github.com/wencywww/Ext.ux.form.field.Tree
Here is an example of a custom panel that extends an Ext Panel. You can extend any component, check the docs for the fields, methods and events you can play with.
Ext.ns('yournamespace');
yournamespace.MyPanel = function(config) {
yournamespace.MyPanel.superclass.constructor.call(this, config);
}
Ext.extend(yournamespace.MyPanel, Ext.Panel, {
myGlobalVariable : undefined,
constructor : function(config) {
yournamespace.MyPanel.superclass.constructor.apply(this, config);
},
initComponent : function() {
this.comboBox = new Ext.form.ComboBox({
fieldLabel: "MyCombo",
store: someStore,
displayField:'My Label',
typeAhead: true,
mode: 'local',
forceSelection: true,
triggerAction: 'all',
emptyText:'',
selectOnFocus:true,
tabIndex: 1,
width: 200
});
// configure the grid
Ext.apply(this, {
listeners: {
'activate': function(p) {
p.doLayout();
},
single:true
},
xtype:"form",
border: false,
layout:"absolute",
labelAlign:"top",
bodyStyle:"padding: 15px",
width: 350,
height: 75,
items:[{
xtype:"panel",
layout:"form",
x:"10",
y:"10",
labelAlign:"top",
border:false,
items:[this.comboBox]
},
{
xtype:"panel",
layout:"form",
x:"230",
y:"26",
labelAlign:"top",
border:false,
items:[{
xtype:'button',
handler: this.someAction.createDelegate(this),
text: 'Some Action'
}]
}]
}); // eo apply
yournamespace.MyPanel.superclass.initComponent.apply(this, arguments);
this.comboBox.on('select', function(combo, record, index) {
this.myGlobalVariable = record.get("something");
}, this);
}, // eo function initComponent
someAction : function() {
//do something
},
getMyGlobalVariable : function() {
return this.myGlobalVariable;
}
}); // eo extend
Ext.reg('mypanel', yournamespace.MyPanel);
Could you describe the UI requirements that you have a bit more? Are you sure that you even need to do build an entire field to support the TreePanel? Why not set the value of a hidden field (see the "hidden" xtype in the API) from a click handler on a normal tree panel?
To answer your question more fully, you can find many tutorials on how to extend ExtJS components. You do this by leveraging the Ext.override() or Ext.Extend() methods.
But my feeling is that you may be over-complicating your design. You can achieve what you need to do by setting a value to this hidden field. If you have complex data, you can set the value as some XML or JSON string.
EDIT Here's a few tutorials. I highly recommend going with the KISS rule when it comes to your UI design. Keep It Simple Stupid!
Extending components using panels