how to create reusable and MCV-compatible form fields in extjs4? - extjs

I have some form fields and field sets and i want to reuse them in different views( Which may have different layouts and/or more fields).
I need a solution under which i dont have to repeat these form fields in every view i create and i want to reuse previously defined ones.What do you suggest for a MVC-based application?

Here is a very basic custom component that you can reuse in forms (it would go in your app/form/field folder with name InterestRate.js):
Ext.define('app.form.field.InterestRate', {
extend: 'Ext.form.field.Number',
alias:'widget.ratefield',
minValue:0,
step:0.05,
fieldLabel:'Rate'
});
You could then use it in a form like this:
Ext.define('app.view.Quote', {
extend:'Ext.form.Panel',
requires:[
'app.form.field.InterestRate'
],
items:[
{
xtype:'ratefield'
}
]
});
You can make the component as complex as you want, such as a whole grid or a fieldcontainer with multiple items.
The question is, do you want its behaviour to be self-contained, or to be controlled by the controller of the main form?

Related

Add logic to Ext.Component initialize Sencha Touch

I've an application that need to be multilanguage.
The translations comes from the server and are based on the user that is using the application.
My current approach is to create my own field for everything that is used in the app, and during the initialize, I change what it says to the translated text, based on a given code.
For example, in a button, I create my own button like this:
Ext.define('myapp.view.shared.MyButton', {
extend: 'Ext.Button',
xtype: 'myappbutton',
initialize: function () {
this.callParent();
this.setText(myapp.util.Helper.getTranslation(this.textCode, this.defaultText));
}
})
Then I change de default button configuration to something like this, where I just change the xtype, remove text, and add textCode and defaultText.
{
xtype: 'myappbutton',
textCode: 'back',
defaultText: 'Back',
...
}
Then I define the text code and the default text. The getTranslation method, inspects in a local storage to get the translation for the code, and return the finding, or the default text sent.
¡Here is the question!
Since I've 12 different components (so far), and every component extends in some way from Ext.Component, I want to add my code to the initialize of Ext.Component, in order to apply this piece of code, avoiding this crap of creating a custom control for each different control that I need to give translations. Is this possible ?
TIA!
Milton.-
I guess you can always try something along the lines
Ext.define('MyApp.override.Internationalization',{
override:'Ext.Component',
initialize: function () {
me.callOverridden(arguments);
// Your code here.
}
});

Form controller in marionette

So I have a form that appears on a few pages and contains a number of groups of inputs. Say group a, group b and group c. On some pages they might have a and b and on others the form contains b and c. Each group may require its own client side custom validation maybe executed from the form controller.
What is the best way to achieve this using backbone and marionette?
Conceptually, and i'm fairly new to both, I'd assume I'd need a FormController that is instantiated from a page specific Controller which also instantiates the group controllers that I need for that page. Any advice would be great.
TL;DR
To make this work you would create two objects per input group. One for its custom functions, and one for its events. In the form controller you'd _.extend the function objects of each input group with a base form view to get the custom functions into your new form view. Next you'd pass in your custom events objects into the constructor for the new form view, where a utility function of your basic view would add the new custom events to the basic form view events. Finally, you'd have to ensure that you have a template for that form that contains the correct input groups.
I should also mention that you can accomplish this with Marionette.LayoutViews and dynamic regions, in a possibly cleaner way (you could specify an atomic template for each input group), albeit with a lot more overhead.
The expanded explanation
I've been toying with this idea a bit. I've been thinking a lot lately about using Underscore _.extend() to plug-in stock functionality to different views. Your problem would be ideal for this solution. Here's a sample of how you'd implement it.
Including functionality
Say that your input group A had a validate function that did it's thing,
validateA: function () {
// custom validation routine
},
and a submit function to handle form submissions,
submitA: function () {
// custom submit routine
}
The first thing you do is package these functions inside an object:
groupA = {
validateA: function() {...},
submitA: function() {...}
}
You'd have as many function objects as you have input groups.
You'd also build a generic form view that would house common form functionality, and which would also serve as the basic view you'd use to render your form
var GenericForm = Backbone.Marionette.ItemView.extend({...});
In here you'll put all your baseline events and functions common to all forms.
Then, as you mentioned you'd set up a FormController that would plug in the custom functionality, like this,
var formController = function () {
callFormZ: function() {
var genericForm = new GenericForm({ groupEvents: [eventsA, eventsB] });
var formZ = _.extend(genericForm, groupA, groupB);
SomeRegion.show(formZ);
}
}
By using _.extend on your basic view and your two input groups you end up with a new view, formZ that is a composite of the two input groups. If you'd look inside it would have all the functionality of GenericForm plus
{
validateA: function () { ... },
submitA: function () { ... },
validateB: function () { ... },
submitB: function () { ... },
}
Events
At this point, though there is no way to bind any events to your custom functions.
You may have noticed in the callFormZ controller that I passed in an array in a property called groupEvents. These are the custom events for your input groups. They'd normally have the form
eventsA: {
'blur .groupA': 'validateA',
'click #groupA button': 'submitA'
}
Ideally, we would just use extend to merge your events the way we did your functions. But since we want to end up with just one events property in your view, we run into a problem. _.extend will overwrite all the events properties in your view object with the events property of the last object passed into _.extend. So to get around this problem we have to pass an array of custom events, one for each form group.
First, you'd create a config array of events objects with your custom events for the particular form you're working with,
customEvents = [
eventsA: {
'blur .groupA': 'validateA',
'click #groupA button': 'submitA'
},
eventsB: {
'blur .groupB': 'validateB',
'click #groupB button': 'submitBA'
}];
You'd pass this array into your constructor, as we did in the callFormZ controller function. The array will now be loaded in your options parameter. In your initialize you can call something like this,
initialize(options) {
combineEvents(options.groupEvents);
this.delegateEvents();
}
where, combinedEvents is
combineEvents: function() {
var extendEvents = [this.eventsA, this.eventB]; // Make an array of the extended groups
_.each(extendEvents, function (extendEvent) {
for (prop in extendEvent)
this.event[prop] = extendEvent[prop];
}
}
Also, note that after combining events I called delegateEvents so that we'd rewire the new events.
Templates
To make all this work you'll have to provide a template that has the portions of each input group. I'm not aware of how we construct the template programmatically from individual input group templates. Instead, you'd have to have a template with the input groups relevant to the form.
Putting it all together
So, to make this work you would create two objects per input group. One for its custom functions, and one for its events. In the form controller you'd _.extend the function objects of each input group with a base form view to get the custom functions into your new form view. Next you'd pass in your custom events objects into the constructor for the new form view, where a utility function of your basic view would add the new custom events to the basic form view events. Finally, you'd have to ensure that you have a template for that form that contains the correct input groups.

Ext JS Forms at multiple depth

So in an Ext js View I have a form which can potentially contain fields and multiple forms inside it and those forms that are contain by main form can also contain forms and fields. there is no limitation of how deep will the last form be. When I submit I want to get a thing like this
main-form
{
key1: value1,
key2: value2,
form-l2
{
key-l2:value-l2,
form-l3
{
key-l3:value-l3,
...
}
}
}
as an object.
In HTML, form cannot contain another form and in ExtJS, even if we could somehow trick it, it wouldn't play well and I doubt is is worth the effort.
Better would be to have one bounding form in which you'd implement the tree-like UI plus some custom methods (or overrides of the existing methods) to return the nested json made of the field values.

editorgrid as variable is not editable

In my application, I´m creating several modal windows which contains a form and an editorgrid. In order to re-use the components, I´ve created the combos, fieldtext, checkbox and other stuff as variables, and only add the necesarry to each window. One of those variables is an editorgrid, xtype: 'editorgrid', and there is the issue:
If I add the variable myEditorGrid to the panel, it works OK the first time I open the window, but the second time that any window has to render the same editorgrid, then the fields cannot be edited any more.
If I create the editorgrid inside the panel (and don´t use the variable), then it works OK everytime I open the window, but I need to copy&paste the same code over and over to all the windows, and that´s not very professional.
I thought the problem is that the variable is not destroyed, and made sure that the windows is closed, but I don´t know how to destroy the variable, and even if this is the solution.
Any idea?
Thanks
You can't reuse an EditorGrid in this manner, because it's column model gets destroyed after use.
The best way to reuse a component is to use the Ext.extend method described here, and then in your initComponent have something like..
initComponent : function() {
this.cm = new Ext.grid.ColumnModel({
columns: [
//define columns here
]
});
this.ds = new Ext.data.JsonStore({
//store config
});
//...
}

Ext JS: what is xtype good for?

I see there are lot's of examples in Ext JS where instead of actually creating Ext JS objects, an object literal with an xtype property is passed in.
What is this good for? Where is the performance gain (if that's the reason) if the object is going to be created anyway?
xtype is a shorthand way to identify particular components: panel = Ext.Panel, textfield = Ext.form.TextField, etc. When you create a page or a form, you may use these xtypes rather than instantiate objects. For example,
items: [{
xtype: 'textfield',
autoWidth: true,
fieldLabel: 'something'
}]
Moreover, creating pages in this manner allows Ext JS to render lazily the page. This is where you see a "performance gain." Instead of creating a large number of components when the app loads, Ext JS renders components when the user needs to see them. Not a big deal if you have one page, but if you exploit tabs or an accordion, many pages are initially hidden and therefore the app will load more quickly.
Furthermore, you may create and register new components creating xtypes of your choosing. Ext JS will similarly render your components lazily.
You may also retrieve components by ID. Since your component (as well as the Ext JS components) may provide a bunch of nice behavior, it is sometimes convenient to search for and retrieve a component rather than a simple DOM element or node.
In short, xtypes identify components and components are a key aspect of Ext JS.
I'm new to Sencha/Ext JS but I think at this point the odd notion of having a shorthand definition identifier string for only UI components must be to satisfy legacy users.
Look at the "List of xtypes" here: http://docs.sencha.com/touch/2-0/#!/guide/components
Is there any good reason to use a similar-but-not-quite-the-same string identifier as the "class" name as the shorthand definition identifier? I don't think so.
Check the following sample of some xtype to class name mappings for Sencha touch:
video - Ext.Video Ok this sort of makes sense - lowercase version of 'class' name
carousel - Ext.carousel.Carousel Same pattern here
carouselindicator - Ext.carousel.Indicator Um, ok - we'll include a package too
navigationview - Ext.navigation.View And again here
datepicker - Ext.picker.Date Ok, wtf?
Some of the arguments above for xtype were that it allowed deferred instantiation of components. I think that is completely irrelevant - what allows deferred instantiation is the fact that Sencha/Ext JS supports the specification of a string identifier in place of an instantiated component in a view hierarchy.
The mapping of a particular string to a particular component that might be instantiated later is completely arbitrary - and in the case of Sencha/Ext JS, unfortunately silly (see examples above).
At least just follow a sensible pattern - for example why couldn't a Ext.Label have an "xtype" of Label? Too simple?
In reality I know why - it's because they made xtype names that read well - there are many repeated class names that wouldn't work (Ext.Panel and Ext.tab.Panel), and pickerDate would just sound stupid.
But I still don't like it - it's an odd little inconsistent shortcut that obfuscates more than it helps.
I asked the same question as Joe, but I found the answer. If you use xtype, one approach is to also specify an itemId in the same object:
{
itemId: 'myObject',
xtype: 'myClass'
...
}
Then you can find it with getComponent() as in
this.getComponent('myObject');
If you declare a class and give it an xtype, you can query it later with Ext.ComponentQuery.query()
For example:
Ext.create('MyApp.view.MyButton', {
xtype: 'mybutton',
.....
});
Later in your code, if you do:
var buttonArray = Ext.ComponentQuery.query('mybutton');
then buttonArray will contain an array of components of that class type. If you create components inline, your component query will be more complex.
Another advantage of xtypes is that if you move your classes around (let's say, you add another subdirectory under "view": MyApp.view.button.MyButton), then your component queries can still remain the same, since your xtype doesn't change. Once your project gets large, you will start creating subdirectories and moving classes around.
An xtype is simply a name given to represent a class. It is a
definition object which don't need to be instantiated when used in
any part of application.
While registering a xtype, we simply use this syntax: Ext.reg(<xtype name>,<classname>). But, we don't use the new keyword with the class name because the Component Mgr will automatically create instance of this class only if needed eg. in response to an event like click.
We don't need to get an instance manually because after registering an xtype, the 'Component Mgr' will automatically create an instance for the class represtented by that xtype only if it is used anywhere in the application or it simply don't instantiate that class if not used elsewhere. Component Mgr runs this code:
create : function(config, defaultType){
return new types[config.xtype || defaultType](config);
}
xtype don't instantiate the class when Ext.Ready runs. But, new Ext.Container() will create all instances when Ext.Ready runs. So, using xtype is intelligent for large applications to get rid of garbage objects.

Resources