Saturday 26 May 2012

Creating a Tabbed Entity Template using Wijmo Open for Juice UI

Wijmo Open for Juice UI is quite cool and offers an alternative to using Ajax Control Toolkit which is still cool but Wijmo Open for Juice UI supports IE6+, Firefox 3+, Safari 3+, and Chrome browsers which should mean everything works.

I started customising Entity Templates here Custom Entity Templates – Dynamic Data 4 I decided to take this a little further and create a Tabbed UI for editing really long forms, I have done this with the Ajax Control Toolkit but thought I like to have a jQuery UI version which brings theming to the table.

Creating the Entity Template

Wijmo tabs in action

Figure 1 – Wijmo tabs in action

<asp:Panel ID="Panel1" runat="server">
    <ul>
        <li><a href="#tabs-1">Nunc tincidunt</a></li>
        <li><a href="#tabs-2">Proin dolor</a></li>
        <li><a href="#tabs-3">Aenean lacinia</a></li>
    </ul>
    <div id="tabs-1">
        <p>
            Proin elit arcu, rutrum commodo, vehicula tempus...
</p>
</div> <div id="tabs-2"> <p> Morbi tincidunt, dui sit amet facilisis feugiat...
</p> </div> <div id="tabs-3"> <p> Mauris eleifend est et turpis. Duis id erat...
</p> </div> </asp:Panel> <wijmo:WijTabs ID="Tabs1" runat="server" TargetControlID="Panel1"> </wijmo:WijTabs>

Listing 1 – mark-up to create the Wijmo tabs

As you can see there are two blocks the UL and the DIVs the UO is used to create the tabs and each DIV matched an LI element and is shown when the tab is selected.

This is not quite as easy to render in an Entity Template as was the AJAX tabs because we need to render two sets of elements two the page, first the UL then the DIVs and we also need the IDs of the DIVs for link in the LIs. so we have to first create the DIVs so we can get each ones client ID then create the UL and add the hyperlinks.

protected override void OnLoad(EventArgs e)
{
    var groupAttribute = Table.GetAttribute<GroupNamesAttribute>();
    if (groupAttribute == null)
        throw new InvalidOperationException("A GroupsAttribute is required for AJAX tab group to work.");

    var row = new HtmlTableRow();
    var td = new HtmlTableCell();
    row.Controls.Add(td);
    this.Controls.Add(row);
    // create tab container to hold each children column
    var panel = new Panel();
    panel.ID = "tabContainer_" + Table.Name;

    // add a tab panel for each children table
    var unorderedList = new HtmlGenericControl("ul");

    // SortedList of tabs
    var tabs = new SortedList<String, HtmlGenericControl>();

    // add DIV for each group
    foreach (var gi in groupAttribute.Groups)
    {
        var groupName = gi.Value;

        var tabDiv = new HtmlGenericControl("div");
        tabDiv.ClientIDMode = ClientIDMode.Static;
        tabDiv.ID = String.Format("{0}-{1}", Table.Name, groupName).Replace(" ", "-");

        // get columns for this group
        var columns = from c in Table.GetScaffoldColumns(Mode, ContainerType)
                      where c.GetAttributeOrDefault<GroupAttribute>().Index == gi.Key
                      orderby c.GetAttributeOrDefault<DisplayAttribute>().GetOrder()
                      select c;

        // add table for this tabs fields
        var htmlTable = new HtmlTable();
        htmlTable.Attributes.Add("class", "DDDetailsTable");
        htmlTable.CellPadding = 6;
        htmlTable.Attributes.Add("Name", groupName);

        // add fields
        foreach (MetaColumn column in columns)
        {
            // new row
            var htmlRow = new HtmlTableRow();
            htmlTable.Controls.Add(htmlRow);

            // add header cell
            var tdHeader = new HtmlTableCell();
            tdHeader.Attributes.Add("class", "DDLightHeader");
            tdHeader.InnerText = column.DisplayName;
            // add cell to row
            htmlRow.Controls.Add(tdHeader);

            // add data cell
            var tdData = new HtmlTableCell();
            // get field template
            var dynamicControl = new DynamicControl() 
                { 
                    Mode = Mode,
                    DataField = column.Name, 
                    ValidationGroup = this.ValidationGroup 
                };
            // add field template to cell
            tdData.Controls.Add(dynamicControl);
            // add cell to row
            htmlRow.Controls.Add(tdData);
        }

        // add the DynamicControl to the tab panel
        tabDiv.Controls.Add(htmlTable);

        // add the tab to list
        tabs.Add(groupName, tabDiv);
    }

    foreach (var gi in groupAttribute.Groups)
    {
        var groupName = gi.Value;

        // new 'LI'
        var tabLi = new HtmlGenericControl("li");

        // new hyperlink
        var hyperlink = new Literal();
        hyperlink.Text = String.Format("<a href=\"#{0}\" >{1}</a>", tabs[groupName].ClientID, groupName);

        // add hyperlink to 'LI'
        tabLi.Controls.Add(hyperlink);

        // add LIs items to UL
        unorderedList.Controls.Add(tabLi);
    }

    // add UL to panel
    panel.Controls.Add(unorderedList);

    // add DIVs to panel
    foreach (var gi in groupAttribute.Groups)
    {
        var groupName = gi.Value;
        panel.Controls.Add(tabs[groupName]);
    }

    // add the panel to the page
    td.Controls.Add(panel);

    // associate the panel with the Wijmo tabs
    var wijmoTabs1 = new WijTabs()
        {
            ID = "wijmoTabs1",
            TargetControlID = panel.ID
        };

    // add Wijmo tabs to the page
    td.Controls.Add(wijmoTabs1);
}

Listing 2 – the main code for the Entity Template

26-05-2012 12-13-50

Figure 2 – the finished Wijmo Tabs EntityTemplate

To setup you will need to have the Attributes and the Advanced Field Template Factory these are in the sample project. I will be putting on NuGet along with my other bits and pieces hopefully soon.

public static void RegisterRoutes(RouteCollection routes)
{
    // add new entity template factory that works with single files also
    DefaultModel.EntityTemplateFactory 
        = new AdvancedEntityTemplateFactory();

    DefaultModel.RegisterContext(typeof(Models.NorthwindEntities), 
        new ContextConfiguration() { ScaffoldAllTables = true });

    routes.Add(new DynamicDataRoute("{table}/{action}.aspx")
    {
        Constraints = new RouteValueDictionary(new 
        { 
            action = "List|Details|Edit|Insert" 
        }),
        Model = DefaultModel
    });
}

Listing 3 – setting up the project Global.asax.cs

[MetadataTypeAttribute(typeof(Employee.EmployeeMetadata))]
[EntityUIHint("WijmoTabs")]
[GroupNames("Personal","Company","Address")]
public partial class Employee
{
    internal sealed class EmployeeMetadata
    {
        public int EmployeeID { get; set; }

        [Group(0)]
        [Display(Order = 0)]
        public string Title { get; set; }
        [Group(0)]
        [Display(Order = 1)]
        public string TitleOfCourtesy { get; set; }
        [Group(0)]
        [Display(Order = 2)]
        public string FirstName { get; set; }
        [Group(0)]
        [Display(Order = 3)]
        public string LastName { get; set; }
        [Group(0)]
        [Display(Order = 4)]
        public Nullable<DateTime> BirthDate { get; set; }
        [Group(0)]
        [Display(Order = 5)]
        public string Extension { get; set; }
        [Group(0)]
        [Display(Order = 6)]
        public byte[] Photo { get; set; }
        [Group(0)]
        [Display(Order = 7)]
        public string HomePhone { get; set; }

        [Group(1)]
        [Display(Order = 8)]
        public Nullable<DateTime> HireDate { get; set; }
        [Group(1)]
        [Display(Order = 9)]
        public Employee Manager { get; set; }
        [Group(1)]
        [Display(Order = 10)]
        public Nullable<int> ReportsTo { get; set; }
        [Group(1)]
        [Display(Order = 10)]
        public EntityCollection<Employee> Staff { get; set; }
        [Group(1)]
        [Display(Order = 11)]
        public EntityCollection<Order> Orders { get; set; }
        [Group(1)]
        [Display(Order = 12)]
        public EntityCollection<Territory> Territories { get; set; }
        [Group(1)]
        [Display(Order = 13)]
        public string Notes { get; set; }

        [Group(2)]
        [Display(Order = 14)]
        public string Address { get; set; }
        [Group(2)]
        [Display(Order = 15)]
        public string City { get; set; }
        [Group(2)]
        [Display(Order = 16)]
        public string PostalCode { get; set; }
        [Group(2)]
        [Display(Order = 17)]
        public string Region { get; set; }
        [Group(2)]
        [Display(Order = 18)]
        public string Country { get; set; }

        [Display(AutoGenerateField = false)]
        public string PhotoPath { get; set; }
    }
}

Listing 4 – Sample Metadata

You will also need NuGet and add the Wijmo Open for Juice UI to your project.

Get Wijmo Open for Juice UI from NuGet

Later I will add all the features that you get with jQuery UI such as tab sorting and alignment see the online Juice Explorer for all the features.

Just for good measure I have added the Wijmo Accordion to the project

Wijmo Accordian Entity Termplate

Figure 3 – Wijmo Accordion Entity Template

You can of course get even more by getting Studio for Studio for ASP.NET Wijmo from ComponentOne this is the best thing for ASP.Net I have seem come out for a while Open-mouthed smile as everyone else seems to be concentrating on either pure client or MVC Sad smile

Download from my sky drive WijmoTabs.zip

17 comments:

Anonymous said...

How do youdisplay another entity template within the accordion?

Stephen J. Naughton said...

you woudl need to create a custom page template or custom pager to do that. An Entity Template for for a single Entity its in the name :)

I do have a DetailsList page in which you view the details of an entity and see tabbed lists of all its children.

Steve.

Anonymous said...

Great work, again!

I was curious, is there any reason you could not have used the existing DynamicDate attributes DisplayAttribute.GroupName and DisplayAttribute.Order instead of creating new ones in
Group[0] and Order[0]?

And, would it be possible to use BOTH : that is, ordered groupings within tabs?

Stephen J. Naughton said...

yes that is certanly possible I just decided to make things easier for myself :)

Steve

Anonymous said...

Hi Steve,

first of all congratulate you on your blog.

I have successfully tested your project and it works fine, but when I try to implement it in my own project installing via NuGet the latest version of Wijmo Open Juice (4.0.20122.6), Wijmo (Tab & Accordian) entity page seems that does not load the css or javascripts and shows me the format tab groups in plane mode (without format).

Viewing the HTM source code, script src links are not broken...

What am I doing wrong?

Thanks in advance

Anonymous said...

Hi Steve,

first of all congratulate you on your blog.

I have successfully tested your test project, but when I try to implement it in my project with the latest version
of Wijmo Open Juice (4.0.20122.6), Wijmo entity page (Tab & Accordian) does not load the css or javascripts and shows me the tab groups in plane mode (Without format).

What am I doing wrong?

Thanks in advance

Anonymous said...

Hello Steve

I write again to talk to you about the outcome of a test I have done.

I used your sample project (which works OK) and I uninstalled Juice Wijmo Open for UI.

I heave installed the latest version of Wijmo Open for Juice UI (4.0.20.122.6) via NuGet and the result is the same as when I incorporate into my project -> shows entity in flat format with no styles.

It might have to do something extra using the latest version of Wijmo?

Thanks in advance
Xavier

Stephen J. Naughton said...

Hi there not sure what you issue is I just installed the packages and it worked fine and a few others have used this and it worked greate for them.

Steve

P.S. sorry for the delay in responding I have been away on vacation with little internet access.

Stephen J. Naughton said...

Hi Xavier, again sorry for the late response :( I have been away with little internet access, you summation that there may be breaking changes with the latest Wijmo free stuff is right.

Steve

Anonymous said...

Hi Esteve. Thanks for the reply.

Are you about to update your code to work with the latest version of Wijmo soon?

Other way I've tried to update manually code files, copy manually wijmo files from your sample and add manually refrences to my project, but the resuly is the same: Showing groupped fields wtih no javascript/css running.

Any ideas? am I missing something?

Thanks in advance.
Xavier

Stephen J. Naughton said...

Hi Xavier, I play to do just that as soon as I get a moment, been on vacation and then my brother died tragically :( but should be back together real soon.

Steve

Xavier said...

I'm sorry about your brother's death.

Xavier

Stephen J. Naughton said...

It's very sad but life must go on, and I'm getting back in the gamer now so I should get to have a look later this week.

Steve

Anonymous said...

Hi Steve, I hope you are well

Were you able to update your code to Work with the latest version of Wijmo?

Stephen J. Naughton said...

Sorry not done so as yet but will try soon :)

Steve

Unknown said...

Hi Steve, I used Wijmo and this example for tabs creation. The grouped boxes are getting created but they are not being placed in respective. I am getting bulletted group name instead of being arranged in tab. What could be the issue? Could you please provide me clues?

Regards,
Sudhakar

Stephen J. Naughton said...

If you are getting bullets point then you seem to be missing some CSS styles or they are being overridden.

Note I am not using WIJMO anymore I am using Bootstrap and I have an open source project on GitHub here

https://github.com/NotAClue/bootstrap-web-controls

Steve