[CLOSED] Best practice to integrate custom/generic plugins

  1. #1

    [CLOSED] Best practice to integrate custom/generic plugins

    Hi,

    This post is kind of a continuation from this previous thread that Vladimir and Daniil in particular helped with:
    http://forums.ext.net/showthread.php...event-on-a-tab

    In the previous thread I was given help on how to get doubleclick to work on tabs to support renaming of them, which is neat.

    The second thing I wanted to do is add a final tab that acts like an add button (bit like in IE and Firefox etc).

    After digging around a bit, I came across this little plugin from Animal on the Ext Js forums (and a cut down version by a commenter).
    http://www.sencha.com/forum/showthre...n-in-tab-strip

    I used the cut down version and combined it with the renaming (so you can add new tabs, and rename them by double clicking) which is working really nicely.

    However, I have 2 questions:

    1) I get a JavaScript error (which does not break the page or stop other JS from executing)
    2) I had to slightly tweak the plugin in the Ext Js forum to get it to work with the Ext.Net TabPanel plugin mechanism and want to ask about best practice

    So, first the code, and then the details of the above two things:

    <%@ Page Language="C#" %>
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title>Ext.Net Example</title>
     
        <style type="text/css">
            div.x-tab-strip-wrap .add-tab a span.x-tab-strip-inner {
                background:url(/icons/add-png/ext.axd) no-repeat center center;
                width:16px;
                height:21px;
            }
            
            .new-tab { background-image:url(/icons/page_white_star-png/ext.axd); }
        </style>
    
        <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="ScriptFiles" />
    
        <script type="text/javascript">
            // Courtesy: http://www.sencha.com/forum/showthread.php?83213-Add-new-tab-button-in-tab-strip&p=513914#post513914
            Ext.ux.AddTabButton = Ext.extend(Object, {
                constructor: function (config) {
                    config = config || {};
                    Ext.apply(this, config);
                },
                init: function(tp) {
                    if (tp instanceof Ext.TabPanel) {
                        tp.onRender = tp.onRender.createSequence(this.onTabPanelRender);
                    }
                },
    
                //private, ideally
                onTabPanelRender: function() {
                    this.addTab = this.itemTpl.insertBefore(this.edge, {
                        id: this.id + 'addTabButton',
                        cls: 'add-tab',
                        text: this.addTabText || ' ',
                        iconCls: ''
                    }, true);
    
                    this.addTab.child('em.x-tab-left').setStyle('padding-right', '6px');
                    this.addTab.child('a.x-tab-right').setStyle('padding-left', this.tabPosition == 'top' ? '6px' : '0px');
    
                    new Ext.ToolTip({
                        target: this.addTab,
                        bodyCfg: {
                            html: 'New tab'
                        }
                    });
    
                    this.addTab.on({
                        click: this.onAddTabClick,
                        scope: this
                    });
                }
            });
            Ext.reg('Ext.ux.AddTabButton', Ext.ux.AddTabButton);
        </script>
    
        <script type="text/javascript">
            var bodyInit = function() {
                this.items.each(function(item, index) {
                    setTabEditable(this, index);
                },
                this);
            };
    
            var setTabEditable = function(tabPanel, index) {
                Ext.get(tabPanel.getTabEl(index)).on('dblclick', function(e, t) {
                    Editor1.field.setWidth(Ext.get(t).getWidth());
                    Editor1.startEdit(t);
                });
            };
    
            var newTabAdded = function() {
                this.add({
                    title      : '(untitled)',
                    iconCls : 'new-tab',
                    html     : 'new content',
                    id         : Ext.id()
                });
                setTabEditable(this, this.items.items.length - 1);
            };
     
            var beforeComplete = function(editor, value, startValue) {
                editor.setValue(editor.getValue());
                return true;
            };
     
            var complete = function(editor, value, startValue) {
                Ext.Msg.notify("Editor Changed", String.format("<b>{0}</b><br />changed to<br /><b>{1}</b>", startValue, value));
            }; 
        </script>
    </head>
    <body>
        <form id="Form1" runat="server">
        <ext:ResourceManager ID="ResourceManager1" runat="server" ScriptMode="Debug" SourceFormatting="true" />
        <ext:TabPanel ID="TabPanel1" runat="server" TabPosition="Bottom" Height="100">
            <Items>
                <ext:Panel ID="Panel1" runat="server" Title="Tab1" Html="Content for tab 1" />
                <ext:Panel ID="Panel2" runat="server" Title="Tab2" Html="Content for tab 2" />
                <ext:Panel ID="Panel3" runat="server" Title="Tab3" Html="Content for tab 3" />
            </Items>
            <Listeners>
                <AfterRender Fn="bodyInit" />
            </Listeners>
            <CustomConfig>
                <ext:ConfigItem Name="onAddTabClick" Value="newTabAdded" />
            </CustomConfig>
            <Plugins>
                <ext:GenericPlugin Id="AddTabButton" runat="server" InstanceName="Ext.ux.AddTabButton" />
            </Plugins>
        </ext:TabPanel>
        <ext:Editor ID="Editor1" runat="server" Shadow="None" IgnoreNoChange="true">
            <Alignment ElementAnchor="Left" TargetAnchor="Left" />
            <Field>
                <ext:TextField ID="TextField1" runat="server" AllowBlank="false" Width="110" SelectOnFocus="true" />
            </Field>
            <Listeners>
                <BeforeComplete Fn="beforeComplete" />
                <Complete Fn="complete" />
            </Listeners>
        </ext:Editor>
        </form>
    </body>
    </html>
    1) JavaScript error
    From what I can tell, the JavaScript error only occurs when creating the new tab. In Firebug, I see "item is undefined" inside this code:

    findTargets : function(e){     var item = null, 
        itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip); 
        
        if(itemEl){ 
            item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]); 
            if(item.disabled){ 
                return { 
                    close : null, 
                    item : null, 
                    el : null 
                }; 
            } 
        } 
        return { 
            close : e.getTarget('.x-tab-strip-close', this.strip), 
            item : item, 
            el : itemEl 
        }; 
    },
    More specifically the problem seems to be at "if (item.disabled)"

    This may be an Ext Js issue which we can't do anything about, but just wondering if you can see a possible workaround or fix -- maybe the Add plugin is not doing something right, and I'm not so experienced with Ext Js plugins - which leads nicely to my second point :)

    2) Best practice:
    I was about to see if I can wrap the above functions (bodyInit, beforeComplete, setEditable, etc etc) into its own generic plugin that I could add to any Tab Panel.

    This way, some people can choose what they want; add tabs but not rename, or allow renaming but not adding, or have both.

    To reduce coupling between the two plugins, I guess I'd fire existing events or raise new ones and the other plugins can watch for them if needed. So, the new tab plugin can raise tabAdded or somethign like that which the rename one could watch for...

    But, the renaming relies on an Editor being present. How do you normally handle dependencies like this? Do you supply an editor in the custom config?

    If so, I am guessing that then means if we extend Object we may need to handle an empty config and initialize an Editor somehow?

                constructor: function (config) {
                    config = config || {}; // I presume here we'd have to create a new config with an internally initialized editor with some useful default behaviour somehow... or just throw an exception maybe
                    Ext.apply(this, config);
                },
    From an Ext.Net perspective I guess passing in an Editor in the custom config is generally not an issue: whether it is created by markup or in codebehind the editor can be passed in. Is that how you would approach it?

    Many thanks!
    Last edited by geoffrey.mcgill; Feb 01, 2011 at 4:53 PM. Reason: [CLOSED]
  2. #2
    Hi,

    1) I get a JavaScript error (which does not break the page or stop other JS from executing)
    This plugin is not compatiable with latest ExtJS. To fix the issue need override 'findTargets' method (see above sample)

    2) I had to slightly tweak the plugin in the Ext Js forum to get it to work with the Ext.Net TabPanel plugin mechanism and want to ask about best practice
    You can set Singleton="true" for GenericPlugin to support such type of plugins (which doesn't required instantiation)

    I modified your sample to demonstrate how to move the logic inside plugin.

    <%@ Page Language="C#" %>
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title>Ext.Net Example</title>
      
        <style type="text/css">
            div.x-tab-strip-wrap .add-tab a span.x-tab-strip-inner {
                background:url(/icons/add-png/ext.axd) no-repeat center center;
                width:16px;
                height:21px;
            }
             
            .new-tab { background-image:url(/icons/page_white_star-png/ext.axd); }
        </style>
     
        <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="ScriptFiles" />
     
        <script type="text/javascript">
            // Courtesy: http://www.sencha.com/forum/showthread.php?83213-Add-new-tab-button-in-tab-strip&p=513914#post513914
            Ext.ux.AddTabButton = Ext.extend(Object, {
                onAddTabClick : Ext.emptyFn,
                
                constructor: function (config) {
                    config = config || {};
                    Ext.apply(this, config);
                },
                init: function(tp) {
                    if (tp instanceof Ext.TabPanel) {
                        this.tp = tp;
                        tp.onRender = tp.onRender.createSequence(this.onTabPanelRender, this);
                        tp.findTargets = this.findTargets;                    
                    }
                },
                
                findTargets : function(e){
                    var item = null,
                        itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip);
    
                    if(itemEl){
                        item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
                        if(item && item.disabled){
                            return {
                                close : null,
                                item : null,
                                el : null
                            };
                        }
                    }
                    return {
                        close : e.getTarget('.x-tab-strip-close', this.strip),
                        item : item,
                        el : itemEl
                    };
                },
     
                //private, ideally
                onTabPanelRender: function() {
                    this.addTab = this.tp.itemTpl.insertBefore(this.tp.edge, {
                        id: this.tp.id + 'addTabButton',
                        cls: 'add-tab',
                        text: this.addTabText || ' ',
                        iconCls: ''
                    }, true);
     
                    this.addTab.child('em.x-tab-left').setStyle('padding-right', '6px');
                    this.addTab.child('a.x-tab-right').setStyle('padding-left', this.tp.tabPosition == 'top' ? '6px' : '0px');
     
                    new Ext.ToolTip({
                        target: this.addTab,
                        bodyCfg: {
                            html: 'New tab'
                        }
                    });
     
                    this.addTab.on({
                        click: this.onAddTabClick.createDelegate(this, [this, this.tp]),
                        scope: this
                    });
                    
                    this.bodyInit();
                },
                
                bodyInit : function() {
                    this.tp.items.each(function(item, index) {
                        this.setTabEditable(this.tp, index);
                    },
                    this);
                },
         
                setTabEditable : function(tabPanel, index) {
                    Ext.get(tabPanel.getTabEl(index)).on('dblclick', function(e, t) {
                        window[this.editor].field.setWidth(Ext.get(t).getWidth());
                        window[this.editor].startEdit(t);
                    }, this);
                }
            });
            Ext.reg('Ext.ux.AddTabButton', Ext.ux.AddTabButton);
        </script>
     
        <script type="text/javascript"> 
            var newTabAdded = function(pl, tp) {
                tp.add({
                    title      : '(untitled)',
                    iconCls : 'new-tab',
                    html     : 'new content',
                    id         : Ext.id()
                });
                pl.setTabEditable(tp, tp.items.items.length - 1);
            };
      
            var beforeComplete = function(editor, value, startValue) {
                editor.setValue(editor.getValue());
                return true;
            };
      
            var complete = function(editor, value, startValue) {
                Ext.Msg.notify("Editor Changed", String.format("<b>{0}</b><br />changed to<br /><b>{1}</b>", startValue, value));
            }; 
        </script>
    </head>
    <body>
        <form id="Form1" runat="server">
        <ext:ResourceManager ID="ResourceManager1" runat="server" ScriptMode="Debug" SourceFormatting="true" />
        <ext:TabPanel ID="TabPanel1" runat="server" TabPosition="Bottom" Height="100">
            <Items>
                <ext:Panel ID="Panel1" runat="server" Title="Tab1" Html="Content for tab 1" />
                <ext:Panel ID="Panel2" runat="server" Title="Tab2" Html="Content for tab 2" />
                <ext:Panel ID="Panel3" runat="server" Title="Tab3" Html="Content for tab 3" />
            </Items>     
            <Plugins>
                <ext:GenericPlugin Id="AddTabButton" runat="server" InstanceName="Ext.ux.AddTabButton">
                    <CustomConfig>
                        <ext:ConfigItem Name="onAddTabClick" Value="newTabAdded" />
                        <ext:ConfigItem Name="editor" Value="#{Editor1}" Mode="Value" />
                    </CustomConfig>
                </ext:GenericPlugin>
            </Plugins>
        </ext:TabPanel>
        <ext:Editor ID="Editor1" runat="server" Shadow="None" IgnoreNoChange="true">
            <Alignment ElementAnchor="Left" TargetAnchor="Left" />
            <Field>
                <ext:TextField ID="TextField1" runat="server" AllowBlank="false" Width="110" SelectOnFocus="true" />
            </Field>
            <Listeners>
                <BeforeComplete Fn="beforeComplete" />
                <Complete Fn="complete" />
            </Listeners>
        </ext:Editor>
        </form>
    </body>
    </html>
  3. #3
    Vladimir,

    Many thanks for the hint on overriding findTargets. Really appreciated.

    Regarding the modified example, I apologize if I did not explain clearly, but I was looking for best way to wrap the 2 capabilities (tab edit, and tab add) into two separate plugins (one for each of those capabilities). This way, in some scenarios you can choose to have one or the other if you don't want both.

    I think this is it, basically:

    <%@ Page Language="C#" %>
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title>Ext.Net Example</title>
       
        <style type="text/css">
            div.x-tab-strip-wrap .add-tab a span.x-tab-strip-inner {
                background:url(/icons/add-png/ext.axd) no-repeat center center;
                width:16px;
                height:21px;
            }
              
            .new-tab { background-image:url(/icons/page_white_star-png/ext.axd); }
        </style>
      
        <ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="ScriptFiles" />
    
        <script type="text/javascript">
            Ext.ux.EditableTabName = Ext.extend(Object, {
                constructor: function (config) {
                    config = config || {};
                    Ext.apply(this, config);
                },
    
                init: function(tp) {
                    if (tp instanceof Ext.TabPanel) {
                        this.tp = tp;
                        tp.onRender = tp.onRender.createSequence(this.bodyInit, this);
                        tp.on('add', this.onNewTabAdded, this);
                    }
                },
    
                bodyInit: function() {
                    this.tp.items.each(function(item, index) {
                        this.setTabEditable(this.tp, index);
                    },
                    this);
                },
    
                setTabEditable: function(tabPanel, index) {
                    Ext.get(tabPanel.getTabEl(index)).on('dblclick', function(e, t) {
                        var extTarget = Ext.get(t);
    
                        window[this.editor].on('beforeComplete', this.editorBeforeComplete);
                        window[this.editor].field.setWidth(extTarget.getWidth());
                        window[this.editor].startEdit(extTarget.hasClass('x-tab-strip-text') ? t : extTarget.select('.x-tab-strip-text').elements[0]);
                    }, this);
                },
    
                onNewTabAdded: function(tabPanel, newTab, index) {
                    this.setTabEditable(tabPanel, index);
                },
    
                editorBeforeComplete: function(editor, value, startValue) {
                    editor.setValue(editor.getValue());
                    return true;
                }
            });
    
            Ext.reg('Ext.ux.EditableTabName', Ext.ux.EditableTabName);
        </script>
    
        <script type="text/javascript">
            // Courtesy: http://www.sencha.com/forum/showthread.php?83213-Add-new-tab-button-in-tab-strip&p=513914#post513914
            Ext.ux.AddTabButton = Ext.extend(Object, {
                onAddTabClick: Ext.emptyFn,
                 
                constructor: function (config) {
                    config = config || {};
                    Ext.apply(this, config);
                },
    
                init: function(tp) {
                    if (tp instanceof Ext.TabPanel) {
                        this.tp = tp;
                        tp.onRender = tp.onRender.createSequence(this.onTabPanelRender, this);
                        tp.findTargets = this.findTargets;                   
                    }
                },
                 
                findTargets : function(e){
                    var item = null,
                        itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip);
     
                    if(itemEl){
                        item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
                        if(item && item.disabled){
                            return {
                                close : null,
                                item : null,
                                el : null
                            };
                        }
                    }
                    return {
                        close : e.getTarget('.x-tab-strip-close', this.strip),
                        item : item,
                        el : itemEl
                    };
                },
      
                //private, ideally
                onTabPanelRender: function() {
                    this.addTab = this.tp.itemTpl.insertBefore(this.tp.edge, {
                        id: this.tp.id + 'addTabButton',
                        cls: 'add-tab',
                        text: this.addTabText || ' ',
                        iconCls: ''
                    }, true);
      
                    this.addTab.child('em.x-tab-left').setStyle('padding-right', '6px');
                    this.addTab.child('a.x-tab-right').setStyle('padding-left', this.tp.tabPosition == 'top' ? '6px' : '0px');
      
                    new Ext.ToolTip({
                        target: this.addTab,
                        bodyCfg: {
                            html: 'New tab'
                        }
                    });
      
                    this.addTab.on({
                        click: this.onAddTabClick.createDelegate(this, [this.tp]),
                        scope: this
                    });
                }
            });
            Ext.reg('Ext.ux.AddTabButton', Ext.ux.AddTabButton);
        </script>
      
        <script type="text/javascript">
            var newTabAdded = function(tp) {
                tp.add({
                    title   : '(untitled)',
                    iconCls    : 'new-tab',
                    html    : 'new content',
                    id      : Ext.id()
                });
            };
       
            var complete = function(editor, value, startValue) {
                Ext.Msg.notify("Editor Changed", String.format("<b>{0}</b><br />changed to<br /><b>{1}</b>", startValue, value));
            };
        </script>
    </head>
    <body>
        <form id="Form1" runat="server">
        <ext:ResourceManager ID="ResourceManager1" runat="server" ScriptMode="Debug" SourceFormatting="true" />
        <ext:TabPanel ID="TabPanel1" runat="server" TabPosition="Bottom" Height="100">
            <Items>
                <ext:Panel ID="Panel1" runat="server" Title="Tab1" Html="Content for tab 1" />
                <ext:Panel ID="Panel2" runat="server" Title="Tab2" Html="Content for tab 2" />
                <ext:Panel ID="Panel3" runat="server" Title="Tab3" Html="Content for tab 3" />
            </Items>    
            <Plugins>
                <ext:GenericPlugin Id="AddTabButton" runat="server" InstanceName="Ext.ux.AddTabButton">
                    <CustomConfig>
                        <ext:ConfigItem Name="onAddTabClick" Value="newTabAdded" />
                    </CustomConfig>
                </ext:GenericPlugin>
                <ext:GenericPlugin Id="EditableTabName" runat="server" InstanceName="Ext.ux.EditableTabName">
                    <CustomConfig>
                        <ext:ConfigItem Name="editor" Value="#{Editor1}" Mode="Value" />
                    </CustomConfig>
                </ext:GenericPlugin>
            </Plugins>
        </ext:TabPanel>
        <ext:Editor ID="Editor1" runat="server" Shadow="None" IgnoreNoChange="true">
            <Alignment ElementAnchor="Left" TargetAnchor="Left" />
            <Field>
                <ext:TextField ID="TextField1" runat="server" AllowBlank="false" Width="110" SelectOnFocus="true" />
            </Field>
            <Listeners>
                <Complete Fn="complete" />
            </Listeners>
        </ext:Editor>
        </form>
    </body>
    </html>
    Your tip to pass in the editor via the config helped; so I just moved that to the new plugin.

    I also modified setTabEditable slightly to ensure that only the target text element got edited even if we click on a parent element.

    So I think it now works quite nicely; you can remove one of the plugins from the markup and the other one will still work.

    I think the only thing remaining to figure out is how to avoid the URL getting a # appended on the end when I click on the "add new tab" tab. Dunno if you have any thoughts on that? I tried returning false in newTabAdded but that didn't do the trick.
  4. #4
    Quote Originally Posted by anup View Post
    I think the only thing remaining to figure out is how to avoid the URL getting a # appended on the end when I click on the "add new tab" tab. Dunno if you have any thoughts on that? I tried returning false in newTabAdded but that didn't do the trick.
    .stopEvent() do the trick.

    You could pass 'stopEvent: true' to .on() method.

    Example 1

    this.addTab.on({
        click: this.onAddTabClick.createDelegate(this, [this.tp]),
        scope: this,
        stopEvent: true
    });
    Also you could call .stopEvent() explicitly - it will cause the same effect:
    Example 2
    var newTabAdded = function(tp) {
        Ext.EventObject.stopEvent();
        tp.add({
            title: '(untitled)',
            iconCls: 'new-tab',
            html: 'new content',
            id: Ext.id()
        });
    };
  5. #5
    Thanks. I keep forgetting about the top event and the global event object in Ext JS - probably too used to jQuery and C# where the event object is passed to the handler :)

    I think you can mark this thread as closed.

Similar Threads

  1. [CLOSED] Integrate .NET Data annotations with Ext .NET Controls
    By T3rryChan in forum 1.x Legacy Premium Help
    Replies: 1
    Last Post: Jul 18, 2011, 3:06 PM
  2. Replies: 0
    Last Post: Apr 12, 2011, 3:32 PM
  3. [CLOSED] Integrate Add capability into the custom search
    By SFritsche in forum 1.x Legacy Premium Help
    Replies: 2
    Last Post: Dec 08, 2009, 8:02 PM
  4. Replies: 4
    Last Post: Nov 27, 2008, 11:17 AM
  5. Replies: 3
    Last Post: Jul 29, 2008, 6:31 PM

Tags for this Thread

Posting Permissions