PDA

View Full Version : CTemplate - Components in Templates



skirtle
21 Sep 2011, 11:56 PM
http://skirtlesden.com/ux/ctemplate

I've written an extension to Ext.XTemplate that allows full-blown ExtJS 4 components to be injected as template values. It was originally just part of my Component Column (http://www.sencha.com/forum/showthread.php?148064) class but it seemed worth the effort to promote it to a UX in its own right.

When the template is run it injects placeholder HTML rather than the component. Once that placeholder HTML makes it to the DOM the component is switched back in. Sometimes this technique causes sizing or scrollbar issues. It should be used only where it is suitable, it isn't a panacea.

Most use cases for a CTemplate involve wanting to inject a component where the only means provided for simple customization is a template or possibly even raw HTML. As such they are a way of subverting the design decisions taken by other developers and should be used with appropriate caution.

An example:


var template = Ext.create('Skirtle.CTemplate',
'<div>',
'{label} {button}',
'</div>'
);

template.overwrite(Ext.fly(...), {
label: 'Thomas',
button: Ext.create('Ext.button.Button', {
text: 'Edit'
})
});

No attempt is made to manage the size or destruction of the components.

There are several similar UXs already in circulation. To list a few:


ExtJS 3 HtmlLayout: http://www.sencha.com/forum/showthread.php?79191
ExtJS 3 ComponentDataView: http://www.sencha.com/forum/showthread.php?79210
An XTemplate override in Touch: http://www.sencha.com/forum/showthread.php?114317

mberrie
22 Sep 2011, 2:01 AM
Nice! Good to know that now there is an Ext4-ready solution for this kind of technique!

ajaxvador
21 Oct 2011, 12:56 AM
+1

Lilliana
17 Nov 2011, 12:46 PM
This extension is just what I need. This will surely save me lots of time on my current project that I have in development. I have been looking for a template extension like this one to help make my work process more efficient. It seems like I waste a great deal of my time with tedious tasks such as setting up new pages and tables and such. I really love the idea of only having to set everything up once. Thanks a lot.

LisburnLad
10 Jan 2012, 3:12 PM
Hi Skirtle,
I was wondering if you'd had a chance to try out your CTemplate with the new 4.1 Beta release? I had been using your CTemplate in version 4.0.7 and it was all working very nicely. I've just upgraded to 4.1 and now everything has stopped working at the point where I try to append the template.

I know that XTemplates and overrides were 2 of the areas changed in version 4.1.

Thanks for any feedback.
Steve

skirtle
10 Jan 2012, 4:03 PM
I think I have it working with 4.1.0-beta-1 but I'm not going to release it until 4.1.0 is released. I'll send you a preview by PM.

Jad
11 Jun 2012, 1:36 AM
Hello,
Can I have o complete example to use this plugin

Thanks a lot

skirtle
11 Jun 2012, 1:39 AM
There are two complete examples if you just follow the link to the download page.

skirtle
11 Jun 2012, 1:42 AM
Apologies for not yet releasing a version that's compatible with 4.1, I'll do it as soon as I find time.

If anyone needs a quick patch for the stack overflow then you just need to swap around the dependency between apply and applyTemplate. Should be a 2-line change, renaming the method on line 26 and switching round the alias at the end.

Jad
11 Jun 2012, 5:45 AM
Very great plugin ;-)

terryfritsch
14 Jun 2012, 11:53 AM
Does anyone have an example of using this plugin with a dataview?

I've set the dataview.tpl to be the new tpl, and I can confirm that the tpl is in fact set, but it doesn't seem to be handling the component rendering.

A straight tpl.overwrite works just fine, so it has to have something to do with passing this through a store/dataview.

Any ideas?




var tpl = Ext.create('Skirtle.CTemplate',

'<tpl for=".">',
'<div class="historyRecord"><span class="title">{title}</span><span class="close">{button}</span></div>',
'</tpl>'
);

dataview.tpl = tpl;

skirtle
15 Jun 2012, 6:21 AM
Does anyone have an example of using this plugin with a dataview?

IE's idiosyncrasies made this a little trickier than I was expecting. If you try to reuse the same component across multiple view refreshes it'll get nuked by IE's overzealous DOM clean-up. Creating a new component each time seems to work fine though:


var tpl = new Skirtle.CTemplate(
'<tpl for=".">',
'<div class="item">{button}</div>',
'</tpl>'
);

Ext.define('MyView', {
extend: 'Ext.view.View',

prepareData: function(data) {
this.callParent(arguments);

data.button = new Ext.button.Button({text: data.text});

return data;
}
});

new MyView({
itemSelector: 'item',
tpl: tpl,
store: {
autoLoad: true,
data: [{text: 'Red'}, {text: 'Green'}, {text: 'Blue'}],
fields: ['text']
}
});

Note that I'm not destroying any of the button components I create so if you follow this example as-is it'll leak like crazy. You'd need to keep track of the components created and destroy them at an appropriate time.

Jad
3 Aug 2012, 6:44 AM
Hello

Any update for 4.1 ?

mikih
5 Oct 2012, 8:22 AM
Hello,

first thanks for your efforts skirtle!

have you got it working for 4.1? I got it working for the Grid Column by commenting


this.createAlias('apply', 'applyTemplate');


in CTemplate.js which was casung an infite loop.

and then amending Component.js by changing



return this.tpl.apply(data);


to



return this.tpl.applyTemplate(data);



I can't get it working for a Dataview which was my initial intention :/

With the above example I get the following error for the callParent in the prepareData function:



Uncaught Error: this.callParent() was called but there's no such method (collectData) found in the parent class (Ext.Component) ext-dev.js:6255
Base.implement.callParent ext-dev.js:6255
Ext.define.items.prepareData ProductBasket.js:17
Ext.define.collectData AbstractView.js:636
Ext.define.refresh AbstractView.js:488
Base.implement.callParent ext-dev.js:6260
Ext.define.refresh View.js:675
(anonymous function) AbstractView.js:857
(anonymous function) ext-dev.js:2951



Normal variables get replaced but my component stays [object Object]...

this is my code:




Ext.define('XXXX.view.orders.new.ProductBasket', {
extend: 'Ext.panel.Panel',
requires: ['Ext.ux.CTemplate'],
alias: 'widget.ordersNewProductBasket',
locales: {
title: 'orders.new.selectProducts.basket.title'
},
height: 300,
items: [
{
xtype: 'dataview',
store: Ext.getStore('SelectProducts'),
itemSelector: 'item',
tpl: Ext.create('Ext.ux.CTemplate','<tpl for="."><div class="item">x{label}x {itemrenderer}</div></tpl>'),
prepareData: function(data, index, record) {
console.log('prepareData data, this', data, this);
// this.callParent(arguments);
data.label = data.id;
data.itemrenderer = Ext.create('Ext.container.Container', {
// layout: 'absolut',
items: [
{
xtype: 'label',
text: data.id
},
{
xtype: 'label',
text: data.materialDescription
},
{
xtype: 'spinnerfield'
},
{
xtype: 'button',
iconCls: 'delete'
}
]
});


// this.tpl.applyTemplate(data);
// data.button = new Ext.button.Button({text: data.text});


return data;
}
}
]
});


Dont be confused I moed the CTemplate into my Ext.ux namespace

Thanks a lot!


This would be so powerful... I really get so sad everythime I see great functionality in Sencha Touch (DataView useComponents), not being shared as always promised by sencha!!! What a waste of time always :/

skirtle
7 Oct 2012, 8:20 AM
@mikih. I've been working on new versions of CTemplate and Component Column this weekend. I'm struggling a little to get it to work with ExtJS 4.1.1 and IE in a few edge cases but I'm getting there.

I would advise you to rollback the changes you've made to CTemplate and Component Column and just make the changes I've recommended previously. To reiterate, the changes required to make it work with 4.1 are:


Line 26. Rename applyTemplate to apply.
Line 174. Switch round apply and applyTemplate.


That's it. You don't need to change Component Column at all.

In your DataView example you've made a mistake in how you've overridden prepareData. You can't just override a method via a config option like that if you want to use callParent. To fix it you can either create a proper subclass of the DataView (as I did in my example) or you can use xhooks.

Once I fixed that mistake your code ran fine for me.

mikih
8 Oct 2012, 12:57 AM
all working! Thank you very much for your time, especially on the weekend!

is there a way to do a donation? Then I will make that call in the next scrum.. you saved us a lot of time :)

Always more fun to share with everyone ey!


Cheers

Florian

mikih
8 Oct 2012, 7:06 AM
Do you have any plans or ideas to get in working with componentquery? The component lifecircle is broken with that approach.

I can to a query like this 'numberfield[name="test"]' but I cant define a nested selector like 'view1 grid2 numberfield[name="test"]'.


This feature is so powerful :)


Cheers

Florian

skirtle
8 Oct 2012, 1:40 PM
I already have component queries working with Component Column, that'll be in 1.1. To use them with a DataView it'd be something like this:


Ext.define('MyView', {
extend: 'Ext.view.View',

initComponent: function() {
this.childCmps = [];
this.callParent();
},

getRefItems: function(deep) {
var items = [],
childCmps = this.childCmps,
index = 0,
len = childCmps.length,
item;

for ( ; index < len ; index++) {
item = childCmps[index];
items.push(item);

if (deep && item.getRefItems) {
items.push.apply(items, item.getRefItems(true));
}
}

return items;
},

prepareData: function(data) {
var me = this,
childCmps = me.childCmps,
button;

me.callParent(arguments);

button = data.button = new Ext.button.Button({
text: data.text,

// For parent queries
getBubbleTarget: function() {
return me;
}
});

// For child queries
childCmps.push(button);

return data;
}
});

As I noted earlier, this example leaks components really badly. Every time the view is refreshed it'll add a new set of components to the list of child items. Fixing that in the general case is a little involved but it should be easy enough for you to destroy the children and remove them from the array as appropriate to your specific use cases.

sole10g
30 Oct 2012, 1:43 PM
Hi everyone.

I'm new in extJS and my knowledge is pretty bad.
I'm trying to add this template method into my grid, wich is created dynamically.
I don't have a "columns" definition in my grid, instead of that the data structure is sent from the server.
What I need is adding an input and a button in each column header, modifying the column metadata when the store load data.
Can anyone please send me a complete example of that component use or give me some clues about how to do that I need?.

I really appreciate any help that you can offer to me.

skirtle
31 Oct 2012, 10:42 PM
@sole10g. You need to break the problem down.


Create a normal grid that looks roughly the way you want your grid to look.
Add the input and button to the header.
Use reconfigure to set the columns dynamically.
Add the reconfigure into the relevant server callback.


For step 2, you should be able to base this on the combobox example from the CTemplate page on my website. You'll probably want to wrap the button and input in a container with a suitable layout (hbox maybe?). You might also want to consider using a triggerfield to get the input and button all in one.

Before embarking on a customisation like this you might also like to consider whether something like the official filtering extension would get you what you need without having to resort to a community ux:

http://dev.sencha.com/deploy/ext-4.1.0-gpl/examples/grid-filtering/grid-filter-local.html

cmeans
16 Feb 2013, 11:20 AM
I'd be interested in this too...especially if there's support to avoid any potential memory leaks (or at least direction on how to implement that ourselves).

m.mihailova
3 Mar 2013, 11:42 PM
I try to use CTemplate:


onGridpanelAfterLayout: function(abstractcontainer, layout, options) {
var store = Ext.data.StoreManager.lookup('ObjectsStore');

store.each(this.SetObjectClick, this);

var objectsPanel = this.getObjectsPanel();

var tpl = Ext.create('Skirtle.CTemplate', '<tpl for="." >', 'hello', '</tpl >');

objectsPanel.columns[0].tpl = tpl;

console.log(objectsPanel.columns[0].tpl);
},

in console I see the new template, but in browther I see the old one. The old is very simple too.

When I change CTemplate to XTemplate it works ok, in browther I see new template of column.

Thank you for you time

skirtle
4 Mar 2013, 2:18 AM
@m.mihailova. You've not provided much context to your question so it's difficult to guess what's going on. Are you able to extract a complete, minimal test case?

m.mihailova
4 Mar 2013, 4:24 AM
i have a simple project, simple data in json format, model and view with a gridpanel&lt;br&gt;GridPanel contains one template row&lt;br&gt;I need to use extra controls in templates for cross browther result.&lt;br&gt;so I try to change template of column in control&lt;br&gt;
&lt;div&gt;onGridpanelAfterLayout: function(abstractcontainer, layout, options) {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var store = Ext.data.StoreManager.lookup('ObjectsStore');&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; store.each(this.SetObjectClick, this);&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;var objectsPanel = this.getObjectsPanel();&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;var tpl = Ext.create('Skirtle.CTemplate',&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;'&amp;lt;tpl for="."&amp;gt;',&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;'hello',&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;'&amp;lt;/tpl&amp;gt;'&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;);&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;objectsPanel.columns[0].tpl = tpl&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/span&gt;console.log(objectsPanel.columns[0].tpl);&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; },&lt;/div&gt;&lt;br&gt;&lt;br&gt;in this example i only try to change the view of elements in a grid, but don't see it in browther

skirtle
7 May 2013, 9:08 AM
CTemplate version 1.1 is now available:

http://skirtlesden.com/ux/ctemplate

bseddon
23 Feb 2014, 2:54 AM
Great component. It's straight forward to have it work when using SA, it involves just 4 steps:

Add a requires
Update the application Paths
Change the column xtype
Add the renderer

The only small downside is that SA cannot show a rendering of the column component in the designer.

In these comments I assume you've downloaded both the Skirtle.CTemplate and Skirtle.grid.column.Component (http://skirtlesden.com/ux/component-column) and that you have save both into files that are accessible from your application.

Step 1: Go to the App node then add 'Skirtle.grid.column.Component' to the list of required components (under the 'Architect' node.

Step 2: The application needs to know how to find the file containing the component (and how the component finds CTemplate). Select 'LoaderConfig' under the 'App' node then click on the 'Paths' property in the inspector. Paths is an object containing the whole or part of a name of a component. My Paths properties looks like:

{
'Ext.ux':'./ux',
'Skirtle.CTemplate':'./ux/CTemplate.js',
'Skirtle.grid.column.Component':'./ux/ComponentColumn.js'
}

You can see that I've referenced both the column component and the template so when ExtJS looks for, say, Skirtle.CTemplate it will locate the file './ux/CTemplate.js'

Step 3: Change the xtype for a column. I'll assume you've added a placeholder column to your grid. Select the column and enter 'componentcolumn' in the 'createAlias' property. This changes the xtype of the column.

Step 4: The final step is to add the render. The example on the componentcolumn web page is good enough to get you started. At least it worked for me.