views:

717

answers:

1

So I have been waxing lyrical about a ASP.NET MVC to a friend who is about to start development of a new user interface....

He asked me if you could solve the following problem with ASP.NET MVC:

Imagine a web app that supports plugins. In the current ASP.NET WebForms app the pluggin developer provides a usercontrol and some JQuery. The IDs of the controls are unique so that the JQuery can always select the correct DOM elements and so that the code behind can deal with the correct control collections.

I suggested that in MVC since we can have any number of forms... each plugin could be implemented as a partialView.

Each partialView would be wrapped by its own form so the relevant Controller Action and therefore would only receive form data defined in the partialView - so from this point of view we dont care about DOM id collisions.

However the HTML would be invalid if ID collision did occur and hence JQuery written by the plugin developer could fail!

I'm not sure how we could get round this...

I dont like the idea of parsing the partialView for collisions when the plugin is added and I dont like the idea of restricting the ids that the plugin developer has access to.

Maybe the the ids could be augmented with a prefix at run time and the model binders could be provided with this prefix?

+1  A: 

You could just wrap the contents of the plugin within a DIV or FORM element and give that a unique ID on the page. Then just use jQuery to only select elements that are within this "parent" DIV or FORM element.

You could probably auto generate a GUID to use as the unique ID at runtime, but this would require some effort by the person writing the plugin. Although, you could probably architect it out in a way to make it automatically generate the "parent" DIV and ID, then you could just access the ID within the view as a Property of the Plugin.

Just some thoughts, I haven't built a an ASP.NET MVC plugin based system like this yet, but it doesn't seem too difficult.

Here's an example of a PartialView that uses a custom ViewUserControl base class:

ViewUserControl1.ascx:

<%@ Control Language="C#" Inherits="MvcPluginPartialView.PluginViewUserControl" %>
<input class="txtText" type="text" value="<%=this.ID %>" />
<input class="txtButton" type="button" value="Show Alert" />
<script type="text/javascript">
    jQuery(function() {
        // This is the Unique ID of this Plugin on the Page
        var pluginID = "<%=this.ID %>";

        // Attach the OnClick event of the Button
        $("#" + pluginID + " .txtButton").click(function() {
            // Display the content of the TextBox in an Alert dialog.
            alert($("#" + pluginID + " .txtText").val());
        });
    });
</script>

MvcPluginPartialView.PluginViewUserControl:

namespace MvcPluginPartialView
{
    public class PluginViewUserControl : ViewUserControl
    {
        public PluginViewUserControl()
        {
            this.ID = "p" + Guid.NewGuid().ToString().Replace("-", "");
        }

        public override void RenderView(ViewContext viewContext)
        {
            viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
            ViewUserControlContainerPage containerPage = new ViewUserControlContainerPage(this);
            //this.ID = Guid.NewGuid().ToString();
            RenderViewAndRestoreContentType(containerPage, viewContext);
        }

        internal static void RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext)
        {
            string contentType = viewContext.HttpContext.Response.ContentType;
            containerPage.RenderView(viewContext);
            viewContext.HttpContext.Response.ContentType = contentType;
        }

        private sealed class ViewUserControlContainerPage : ViewPage
        {
            public ViewUserControlContainerPage(ViewUserControl userControl)
            {
                this.Controls.Add(userControl);
            }

            protected override void Render(System.Web.UI.HtmlTextWriter writer)
            {
                writer.Write("<div id='" + this.Controls[0].ID + "'>");

                base.Render(writer);

                writer.Write("</div>");
            }
        }
    } 
}

Then to place the View on the page you can use the "Html.RenderPartial" method as usual, plus you can place as many of them on the Page as you want and they'll all work as expected.

<%Html.RenderPartial("ViewUserControl1"); %>
<%Html.RenderPartial("ViewUserControl1"); %>
<%Html.RenderPartial("ViewUserControl1"); %>
Chris Pietschmann
Whatever the solution, it should be transparent to the plugin developer - they shouldnt need to care about the id generation
ListenToRick
Ok. I just added some example code that shows how to do this, and the plugin developer doesn't have to care about how the unique id is set, they just have to use it to access the control from within jQuery.
Chris Pietschmann