ExtJS and MVC : 07 - Implementing Dynamic Tabs and Partial Views

This website is no longer actively supported

Written by John DeVightJohn DeVight on 05 Jul 2011 18:27

Download RAZOR Source Code
ASPX Source Code Coming Soon
* Note: the extjs folder has all been removed to reduce the zip file size

Overview

In the wiki article ExtJS and MVC : 06 - Implementing the GridPanel I showed how a grid can be displayed, updated and saved. When a row is selected in the grid an "edit form" is displayed that allowed the user to change the information about a Wiki Page and then click an Update button to update the information in the grid.

To demonstrate the use to a TabPanel, I decided to take the "edit form" and place it in a partial view. When a row is selected in the grid, an Ajax call is made to get the partial view. A tab is dynamically added to the TabPanel and the partial view is displayed in the tab. When the user changes the information about the Wiki Page and clicks the Update button, the grid is updated and the the tab is removed from the TabPanel.

Adding the TabPanel

To add the TabPanel, created a div with an id="tabPanelDiv" as a "placeholder" for the TabPanel to be rendered to. I then created a div with an id="tab0" as a "placeholder" for the "Wiki Series" tab to be rendered to. I placed the wikiForm form element inside the "tab0" div. In the WikiSeries.js script file, I added code to create the TabPanel and the "Wiki Series" tab.

Here is the code:

// Create an instance of the TabPanel and render the TabPanel to the tabPanel div.
var tabPanel = new Ext.TabPanel({
    id: 'tabPanel',
    renderTo: 'tabPanelDiv',
    activeTab: 0,
    items: [
        { contentEl: 'tab0', autoScroll: true, title: 'Wiki Series', layout: 'fit' }
    ]
});

What I ended up with is this:

wiki-series-tab.png
Screenshot of Wiki Series Tab

Creating the EditWikiPage Partial View

Since the "edit form" is going to appear in it's own tab, I created a partial view called EditWikiPage. Since I am going to display a WikiPage in the partial view, I created the partial view as a "strongly-typed view" and selected the "WikiPage" class as the model. I then moved the wikiPageEditDiv div from the WikiSeries view and placed it in the EditWikiPage partial view. Since I am going to be rendering multiple instances of the same partial view, I needed to make sure that the element id's are unique within the partial view. To accomplish this, I concatenated the WikiPage.Id to the id of each of the elements in the partial view. I also converted code for rendering the input elements to use the HTML Helper.

Here is the code:

@model ExtjsMvcRazorApp.Models.WikiPage
 
@{
    Layout = null;
}
 
<div id="@string.Format("wikiPageEditDiv_{0}", Model.Id)" class="content">
    <div class="tableRow">
        <div class="tableColumn">
            <label for="nameTextBox" class="fieldLabel x-form-field">Name:</label>
        </div>
        <div class="tableColumn">
            @Html.TextBox(string.Format("nameTextBox_{0}", Model.Id), Model.Name, 
                new { @class="x-form-field", @style="width:450px" })
        </div>
    </div>
    <div class="tableRow">
        <div class="tableColumn">
            <label for="urlTextBox" class="fieldLabel x-form-field">URL:</label>
        </div>
        <div class="tableColumn">
            @Html.TextBox(string.Format("urlTextBox_{0}", Model.Id), Model.Url, 
                new { @class = "x-form-field", @style = "width:450px" })
        </div>
    </div>
    <div class="tableRow">
        <div class="tableColumn">
            <label for="pageViewsTextBox" class="fieldLabel x-form-field">Page Views:</label>
        </div>
        <div class="tableColumn">
            @Html.TextBox(string.Format("pageViewsTextBox_{0}", Model.Id), Model.PageViews, 
                new { @class = "x-form-field", @style = "width:50px" })
        </div>
    </div>
    <div class="tableRow">
        <div class="tableColumn">
            <label for="uniquePageViewsTextBox" class="fieldLabel x-form-field">Unique Page Views:</label>
        </div>
        <div class="tableColumn">
            @Html.TextBox(string.Format("uniquePageViewsTextBox_{0}", Model.Id), Model.UniquePageViews, 
                new { @class = "x-form-field", @style = "width:50px" })
        </div>
    </div>
    <div style="padding-top:10px;">
        <div id="@string.Format("updateWikiPageButton_{0}", Model.Id)"></div>
    </div>
</div>
<br />

Adding the Controller Method to Get the Partial View

To display the EditWikiPage partial view, I created a method in the HomeController called GetEditWikiPage. The method has a parameter of type WikiPage. I pass the WikiPage through the Ajax call to be rendered in the partial view. I do this because the client has the current version of the WikiPage until it is saved.

Here is the code:

namespace ExtjsMvcRazorApp.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult GetEditWikiPage(WikiPage wikiPage)
        {
            return View("EditWikiPage", wikiPage);
        }
    }
}

ExtJS Code to Get the Partial View and Create the Tab

In the sample application for ExtJS and MVC : 06 - Implementing the GridPanel, when a row in the grid was clicked, the editWikiPage function was called that displayed the "edit form" and populated it with the information about the Wiki Page. Now, when a row in the grid is clicked, and the editWikiPage function is called, the editWikiPage function checks to see if the Wiki Page information is already displayed in a tab. If it is, then the tab is displayed. If it is not, then the Wiki Page information is encoded and sent to the server in an Ajax call. The Wiki Page information is populated in the partial view and is returned. A tab is created and the html for the partial view is added to the contents of the tab. The Update button is created that will update the Wiki Page information in the JsonStore associated with the Grid and remove the tab.

Here is updated code for the editWikiPage function:

editWikiPage = function(rec) {
    // Is the tab already open?
    var tab = Ext.getCmp('Tab' + rec.get('Id'));
 
    if (tab != null) {
        Ext.getCmp('tabPanel').setActiveTab(tab);
    }
    else {
        Ext.Ajax.request({
            url: '/Home/GetEditWikiPage',
            params: Ext.urlEncode(rec.data),
            success: function(response, opts) {
                // Add the new tab.
                var tab = Ext.getCmp('tabPanel').add({
                    title: rec.get('Name'),
                    id: 'Tab' + rec.get('Id'),
                    html: response.responseText,
                    layout: 'fit',
                    closable : true
                    });
                Ext.getCmp('tabPanel').doLayout();
 
                // Set the new tab as the active tab.
                Ext.getCmp('tabPanel').setActiveTab(Ext.getCmp('tabPanel').items.length - 1);
 
                // NOTE: The new tab had to be activated before being able to access any of the
                // HTML elements in the new tab!!!
 
                // Create the Update button.
                var updateWikiPageButton = new Ext.Button({
                    text: 'Update',
                    applyTo: 'updateWikiPageButton_' + rec.get('Id'),
                    handler: updateWikiPageButton_Click.createDelegate(this, rec.get('Id'), true)
                });
            }
        });
    }
}

Here is what the page looks like after the Wiki Page is added as a tab:

wiki-page-tab.png
Screenshot of Tab for a Wiki Page

ExtJS Code to Update the Grid with the Modified Wiki Page Information

In the sample application for ExtJS and MVC : 06 - Implementing the GridPanel, when the update button is clicked, I got the Id for the WikiPage from a hidden field. Now, when the update button is created, I create a delegate using the ExtJS createDelegate function so that I can pass additional information to the update button's click event handler to include the Id for the WikiPage being updated. I use the Id to get the appropriate record from the JsonStore and also to get the appropriate input elements for the WikiPage by formatting the name of the input element to now include the WikiPage Id as part of the input element names.

Here is the code:

updateWikiPageButton_Click = function(sender, e, id) {
    // Save the changes to the wiki page.
    var rec = wikiPagesStore.getById(id);
    rec.beginEdit();
    rec.set('Name', Ext.getDom('nameTextBox_' + rec.get('Id')).value);
    rec.set('Url', Ext.getDom('urlTextBox_' + rec.get('Id')).value);
    rec.set('PageViews', Ext.getDom('pageViewsTextBox_' + rec.get('Id')).value);
    rec.set('UniquePageViews', Ext.getDom('uniquePageViewsTextBox_' + rec.get('Id')).value);
    rec.endEdit();
 
    Ext.getCmp('tabPanel').setActiveTab(0);
    Ext.getCmp('tabPanel').remove('Tab' + id);
}

References

Discussion Closed

Add a New Comment

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License