PDA

View Full Version : Passing arguments to component constructors via HTML templates



oracle
1 Jul 2009, 9:04 AM
Hi all, i've written a patch to Ext.Component that lets you specify some constructor arguments directly in the placeholder or in the template of the new component.

The code is the following:




Ext.Component.prototype.initComponent = function() {
var parent = Ext.fly(this.renderTo);
if(! parent || ! parent.dom.attributes['ext:config']) {
return;
}

var config = parent.dom.attributes['ext:config'].nodeValue;
var params = Ext.util.JSON.decode(config);
Ext.apply(this, params);
Ext.apply(this.initialConfig, params);
}

You can put this code just at the beginning of your app code.

Here is how it works:
Immagine to have a DOM element in your page that is a place holder for a component, in this element you can add an attribute in some namespaces (eg. ext) and put a json encoded string in.

Example:


<div id="component" ext:config="{width:400,cls:'test'}" ></div>
In this example we'll pass the arguments width: 400 and cls: 'test' to the constructor of the new class.



var txt1 = new Ext.form.TextField({
renderTo: "component"
});
This is equivalent on writing:



var txt1 = new Ext.form.TextField({
renderTo: "component",
width: 400,
cls: 'test'
});
But the advantage is that you don't have to modify your js code to modify the width of your component, everybody will be able to modify that parameter.

Here is a more complex example using a template:



<div id="componentTemplate">
<div style="height:50px;margin-left:20px;width:100%;">
<div id="{subComponntId}" ext:config="{width:400,cls:'toto'}" ></div>
</div>
</div>
and the js:




Ext.app.MyComponent = Ext.extend(Ext.Component, {
onRender: function(ct, position){
Ext.app.MyComponent.superclass.onRender.call(this, ct, position);

var tpl = Ext.Template.from("componentTemplate");

var ids = {
subComponntId: Ext.id()
};
var html = tpl.apply(ids);

this.el.update(html);

var txt1 = new Ext.form.TextField({
text: "test",
renderTo: ids.subComponntId
});
}
});



Please feel free to leave your comments, suggestions and bug reports in this thread.

jerrybrown5
1 Jul 2009, 8:41 PM
What a cool idea! +1

oracle
2 Jul 2009, 4:59 AM
Here is an interesting improovement to my code:



Ext.Element.prototype.hasConfig = function() {
return this.dom.attributes['ext:config'] ? true : false;
}

Ext.Element.prototype.getConfig = function() {
if(! this.hasConfig()) {
return {};
}

var config = this.dom.attributes['ext:config'].nodeValue;
return Ext.util.JSON.decode(config);
}


Ext.Component.prototype.initComponent = function() {
var configEl = Ext.fly(this.configElement || this.renderTo);

if(! configEl || ! configEl.hasConfig()) {
return;
}

var params = configEl.getConfig();
Ext.apply(this, params);
Ext.apply(this.initialConfig, params);
}

With this one we can pass the config to an element, even if it isn't directly created in a placeholder:




var panel = new Ext.Panel({
layout: 'form',
renderTo: "myPanel",
items: [{
xtype: 'textfield',
configElement: "textFieldConfig"
}]
});

The html would be the following:



<div id="textFieldConfig" ext:config="{fieldLabel: 'aaa', width:300}"></div>
<div id="myPanel"></div>



it is the same thing than writing the following:



var panel = new Ext.Panel({
layout: 'form',
renderTo: "myPanel",
items: [{
xtype: 'textfield',
width: 200,
fieldLabel: "aaa"
}]
});

Foggy
3 Jul 2009, 12:02 AM
Thats a nice idea...

oracle
3 Jul 2009, 3:24 AM
Update:

- added some checking for the attribute ext:config
- now the component's root DOM ement has two new attributes: ext:xtypes and ext:configElement

ext:xtypes contains the xtypes of the component, so you can check easier the API documentation, without inspecting js code

ext:configElement is the id of the element used to configure the component





Ext.Element.prototype.hasConfig = function() {
return this.dom.attributes['ext:config'] && this.dom.attributes['ext:config'].nodeValue.length > 0 ? true : false;
}

Ext.Element.prototype.getConfig = function() {
if(! this.hasConfig()) {
return {};
}

var config = this.dom.attributes['ext:config'].nodeValue;
return Ext.util.JSON.decode(config);
}

Ext.Component.prototype.initComponent = function() {
var configEl = Ext.get(this.configElement || this.renderTo);

if(! configEl || ! configEl.hasConfig()) {
return;
}

this.configElement = configEl.id;

var params = configEl.getConfig();
Ext.apply(this, params);
Ext.apply(this.initialConfig, params);
}

Ext.Component.prototype.afterRender = function() {
if(! this.el || ! this.configElement) {
return;
}
this.el.set({
'ext:xtypes': this.getXTypes(),
'ext:configElement' : this.configElement
});
}

oracle
6 Jul 2009, 5:35 AM
Update:

- possibility to pass more than one config element for the same component
- the config can be written both in an DOM attribute or in an external JS file

So the following code will apply to the component before configA and then configB


new Ext.Panel({
items: [{
xtype: 'textfield',
configElement: [ "configA", "configB" ]
}]
});



You will be also able to include an external JS file that contains the configuration, more or less as it is a CSS file:



config.js:

Ext.namespace("Ext.app.Config");

var cfg = Ext.app.Config;

cfg.configA = {
width:200
}

cfg.configB = {
height: 100
}



Here is the source




Ext.Element.prototype.hasConfig = function() {
return this.dom.attributes['ext:config'] && this.dom.attributes['ext:config'].nodeValue.length > 0 ? true : false;
}

Ext.Element.prototype.getConfig = function() {
if(! this.hasConfig()) {
return {};
}

var config = this.dom.attributes['ext:config'].nodeValue;
return Ext.util.JSON.decode(config);
}

Ext.namespace("Ext.app.Config");
Ext.Component.prototype.namespace = Ext.app.Config;

Ext.Component.prototype.initComponent = function() {
var element = this.configElement || this.renderTo;

var me = this;
Ext.each(element, function(v) {
if(Ext.app.Config[v]) {
return me.applyConfig(Ext.app.Config[v]);
}

var configEl = Ext.get(v);

if(! configEl || ! configEl.hasConfig()) {
return;
}

if(! me.configElements) {
me.configElements = [];
}

me.configElements.push(configEl.id);

var params = configEl.getConfig();
me.applyConfig(params);
});
}

Ext.Component.prototype.applyConfig = function(config) {
Ext.apply(this, config);
Ext.apply(this.initialConfig, config);
}

Ext.Component.prototype.afterRender = function() {
if(! this.el || ! this.configElements) {
return;
}
this.el.set({
'ext:xtypes': this.getXTypes(),
'ext:configElement' : this.configElements.join(' ')
});
}