Tech Musings Just another tip's, tricks, and how-to's blog

12Sep/113

Shape Methods as an Alternative to Widgets

I've talked about shape methods in my previous post, and talked about using shape builders as alternatives to widgets in an earlier iteration of our designs, but when talking with someone in IRC I realized that going into detail about how we utilize shape methods to render different pieces of our website in different ways in different places might be helpful. So here goes:

Say for instance you had the need to display a Facebook like button at different places throughout the site, on different types of content, in different ways. You could create a widget to display on certain layers, but creating a widget just to display a Like button seems over the top. We have probably 100 different widget-like shapes that get displayed in various ways on our site. That many widgets would be a pain to deal with, and aren't nearly as flexible in how we can interact with them. My colleague, Jesse Wise, came up with a beautiful, simple, painless way to do without widgets: By using shape methods as sort of a partial controller/partial view.

Here is the code for our Like button:

[Shape]
        public IHtmlString ContentLike(dynamic Display, ContentItem item, int Width, int Height, string ShowFaces,string ButtonType)
        {
            string url;
            var shortUrl = item.As<ShortUrlPart>();
            if (shortUrl != null)
            {
                if (String.IsNullOrEmpty(shortUrl.ShortUrl))
                { //if the short url wasn't set at content creation time then we're creating it now. (helpful for pre-existing content)
                    shortUrl.ShortUrl = ShortUrlService.GetShortUrl(GetAbsoluteURL(item));
                }
                url = shortUrl.ShortUrl;
            }
            else
            {
                url = ShortUrlService.GetShortUrl(GetAbsoluteURL(item));
            }
            var title = GetTitle(item);
            return Display.PageLike(Display: Display, pageUrl: url, Width: Width, Height: Height, ShowFaces: ShowFaces,Title:title,ButtonType:ButtonType);
        }

[Shape]
        public IHtmlString PageLike(dynamic Display, string pageUrl, int Width, int Height, string ShowFaces)
        {
            return Display.LikeBlogView(ContentUrl: pageUrl, Width: Width, Height: Height, ShowFaces: ShowFaces);
        }

And then in LikeBlogView.cshtml:

@{

    int width = Model.Width;
    int height = Model.Height;
    string faces = Model.ShowFaces;
    string url = Model.ContentUrl;
}
@{
    <fb:like href="@url" send="false" width="@width" height="@height" show_faces="@faces" font=""></fb:like>
}

So as you can see, we have sort of a mini controller that handles the logic outside of the view, then passes the necessary data into the view for rendering. I'll touch on a couple of the finer points here:

public IHtmlString ContentLike(dynamic Display, ContentItem item, int Width, int Height, string ShowFaces,string ButtonType)

With a shape method, you can inject a ShapeHelper into the method by creating a dynamic parameter named Display. This is what allows the magic to happen down below where we pass off rendering to the Razor view.

ShortUrlService is actually a read-only property that resolves the IShortUrlService at call-time:

 private IShortUrlService ShortUrlService {
            get {
                return _workContextAccessor.GetContext(_httpContextAccessor.Current()).Resolve();
            }
        }

This may or may not be necessary, and I think it may be a hold-over from our original solution to the widget problem, as the Orchard docs show being able to inject services in the normal fashion.

return Display.LikeBlogView(ContentUrl: pageUrl, Width: Width, Height: Height, ShowFaces: ShowFaces);

Display.ShapeName will return an IHtmlString, allowing us to use the result of a Razor view as the return for our shape method. This is the part that really tied everything together, as it allows for a re-usable, lightweight means of separating our logic from our view.

To call ContentLike, you can simply place @Display.ContentLike(myContentItem, 100, 50, "true", "normal") in any view that you have a reference to the content item that you want to use with the Like button. Voila.

Now, I must add as a caveat that widgets have their place. If I was distributing a piece of UI as part of a module that I wanted a user to be able to place on any page without any effort, I would use a widget. If I'm writing my own code and I have the need to re-use specific functionalities/partial views throughout my site, a shape method is the obvious choice.

Comments (3) Trackbacks (0)
  1. Very cool post!
    I have, however, three questions:

    1. Do the Shape methods have to exist in any specific place, or will any class do?
    2. In one of your samples you invoke: Display.LikeBlog(…); However, you have only defined ContentLike and PageLike shape methods. Or does that sample assume you have also defined a LikeBlog shape method?
    3. Is an ASP.NET webcontrol as we used in WebForms?

  2. Sorry, my 3rd question removed is unclear. My question is: Is “[fb:like /]” an ASP.NET webcontrol as we used in WebForms? “`

  3. Thanks Sipke.
    1. Shape methods can exist anywhere in a module. We would tend to put them in a Shapes.cs class in whatever module they related to.
    2. It was actually calling Display.LikeBlogView, which is defined in LikeBlogView.cshtml in this example. Its a shape method calling out to a Razor-view shape.
    3. the FB:Like markup is just part of the standard FBML/javascript package Facebook makes available.


Leave a comment

(required)

No trackbacks yet.