Monday, November 21, 2016

Create an MVC Html Helper to play Audio

What are HTML Helpers? In ASP.Net a developer could rely on controls to help generate HTML code.  Controls are precompiled libraries that allowed a developer to reuse the code which in turn shortened development time.
HtmlHelpers take in a parameter or a group of parameters and returns a simple string.  This string is HTML formatted code that gets rendered to the browser.  It is reusable and has less overhead than a control.  We don’t need to reference any additional assemblies because we will write the code. 

For our sample project, let’s create an extension that…. plays mp3 files in HTML5 fashion.  In order to do this, we need to know the location of the file we want to play.  We can determine the file type by the extension.  So let’s get started.

It is beyond the scope of this blog to explain MVC, or C#.  It is assumed that you are proficient with both skillsets. 

Once you have your MVC templated site up, we need to add a folder.  In the solution explorer add a folder named ‘Components’ =>
image

Next we need to add a class called MvcHtmlMP3Player =>
image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace MediaBlog
{     public class MvcHtmlMP3Player     {     }
}

So in order for it to be an extension, we need to make the class and methods static.  This will allow us to use the extension anywhere within our application.  The purpose of creating a directory is that we can simply add an existing item when we change projects and copy in the extension.  This will save us development time when we want to reuse the code.

We also need to reference the System.Web.Mvc namespace.  So now our code should look like =>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MediaBlog
{     
public static class MvcHtmlMP3Player    
{        
public static MvcHtmlString MP3Player(this HtmlHelper helper,
              string src)
        {
        }
    } }

Now we need to code up the method to return the HTML string we need.  Instead of using StringBuilder (old fashioned way) we will be using the TagBuilder.  The TagBuilder is the best practices way of Core.  So now the first thing we need to do is expand our signature of our method.  We need the source – the source of the mp3 file, we need the ability to be able to reference the tag when we render it to the browser.  We use ‘name’ and/or ‘id’ attributes in the tag to identify this particular instance.  We also need a way to display the controls that allow playback, rewind, fast-forward and stop of the media.  Finally, we need a way to allow unplanned attributes to be added.  For instance, if we use bootstrap then there are a bunch of data tags or knockout uses Observables which are attributes directly in the tag.  This will be the format of the new signature =>

public static MvcHtmlString MP3Player(this HtmlHelper helper,
             string name,
             string src,
             bool controls,
             object htmlAttributes = null)

In the above example, helper is a htmlhelper object – this allows us to extend the htmlhelper object.  Name is the name we want to give to this instance of the MP3Player, Src is the source file for the mp3.  Controls is a boolean (yes/no) that represents whether we want to display the player controls.  Finally, htmlAttributes are any and all unaccounted for attributes.  It’sa attribute bucket.  We default it to null so that there is no requirement to populate the attribute.

So the first thing we need to do inside our method is to instantiate the tagbuilder object.  We can do this like so =>

TagBuilder tb = new TagBuilder("audio");

This creates the opening tag for the HTML string that is returned.  Later, we will see how we close the tag.  Next we need to start building our tag attributes =>

if (!string.IsNullOrWhiteSpace(name))
             {
                 name = TagBuilder.CreateSanitizedId(name);
                 if (string.IsNullOrWhiteSpace(id))
                 {
                     tb.GenerateId(name);
                 } else
                 {
                     tb.MergeAttribute("id", TagBuilder.CreateSanitizedId(id));
                 }
             }
             tb.MergeAttribute("name", name);

Here we are trying to create the name/id attributes.  We look to make sure name has a value, then we sanitize it.  We sanitize the name to ensure that it is a correct value.  Now we make the same checks for id.  The MergeAttribute method is what actually adds the attribute to the TagBuilder object. 
Why not just use TagBuilder.Attributes.Add? The difference between TagBuilder.Attributes.Add and TagBuilder.MergeAttribute is that they are actually the same method with one exception.  TagBuilder.Attributes.Add is a SortedDictionary.  A SortedDictionary is based on a unique key/value system which means you can not add two tags with the same name (not sure why you would want to as this is bad HTML formatting but…)  you can add a value to replaceExisting and it will overwrite the existing key.  MergeAttribute performs the same functionality but replaceExisting is defaulted to True.

Now we need to add the attribute that actually goes out and fetches the mp3 file to play =>

tb.MergeAttribute("src", src);

We need to perform a check to make sure that the user wants the controls displayed.  We can do this by using the boolean controls parameter =>

if (controls)
             {
                 tb.Attributes.Add("Controls", "Controls");
             }

Finally, remember that we wanted to create a parameter that will allow us to add bootstrap or unaccounted for attributes =>

tb.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

So to close the tag and return the string =>

return MvcHtmlString.Create(tb.ToString(TagRenderMode.SelfClosing));

This completes our HtmlHelper method.  We still have room to improve on this code.  For instance, let’s go ahead and break out some overloads.  Why are overloads important?  Well we can use the overloads so that we don’t have to populate all the parameters and make the helper extension a lot more flexible.  Here I can see having 3 overloads to account for the various ways we can create the tag.  We will always need a src (if we’re not loading an mp3 file, what’s the point?)  =>

public static MvcHtmlString MP3Player(this HtmlHelper helper,
     string src,
     object htmlAttributes = null) { return MP3Player(helper, string.Empty, src, false, htmlAttributes); } public static MvcHtmlString MP3Player(this HtmlHelper helper,
     string src,
     bool controls,
     object htmlAttributes = null) { return MP3Player(helper, string.Empty, src, controls, htmlAttributes); } public static MvcHtmlString MP3Player(this HtmlHelper helper,
     string name,
     string src,
     object htmlAttributes = null) { return MP3Player(helper, name, src, false, htmlAttributes); }

Finally, we know that we will need to add name/id to generally most all controls.  So we can go ahead and move this to a static method that can be used by all our html extensions =>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MediaBlog
{
     public static class MvcHtmlCommon
     {
         public enum AudioMediaType
         {
             MP3,
             OGG,
             WAV
         }
         public static void AddName(TagBuilder tb, string name, string id)
         {
             if (!string.IsNullOrWhiteSpace(name))
             {
                 name = TagBuilder.CreateSanitizedId(name);
                 if (string.IsNullOrWhiteSpace(id))
                 {
                     tb.GenerateId(name);
                 } else 
                 {
                     tb.MergeAttribute("id", TagBuilder.CreateSanitizedId(id));
                 }
             }
             tb.MergeAttribute("name", name);
         }
     } }

This is our common class.  I went ahead and created an enum and a AddName method.  This will be referenced by our MP3Player methods.  Finally, we add the code to our view like so =>

@Html.MP3Player("chad", "/Media/Infectious Groves - Feed the Monkey.mp3", true)

I added this to the About view.  In total, here is all the code listings =>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MediaBlog
{
     public static class MvcHtmlMP3Player
     {
         public static MvcHtmlString MP3Player(this HtmlHelper helper,
             string src,
             object htmlAttributes = null)
         { return MP3Player(helper, string.Empty, src, false, htmlAttributes); }
         public static MvcHtmlString MP3Player(this HtmlHelper helper,
             string src,
             bool controls,
             object htmlAttributes = null)
         { return MP3Player(helper, string.Empty, src, controls, htmlAttributes); }
         public static MvcHtmlString MP3Player(this HtmlHelper helper,
             string name,
             string src,
             object htmlAttributes = null)
         { return MP3Player(helper, name, src, false, htmlAttributes); }
         public static MvcHtmlString MP3Player(this HtmlHelper helper,
             string name,
             string src,
             bool controls,
             object htmlAttributes = null)
         {
             TagBuilder tb = new TagBuilder("audio");
             MvcHtmlCommon.AddName(tb, name, "");
             tb.MergeAttribute("src", src);
             if (controls)
             {
                 tb.Attributes.Add("Controls", "Controls");
             }
             tb.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
             return MvcHtmlString.Create(tb.ToString(TagRenderMode.SelfClosing));
         }
     } }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace MediaBlog
{
     public static class MvcHtmlCommon
     {
         public enum AudioMediaType
         {
             MP3,
             OGG,
             WAV
         }
         public static void AddName(TagBuilder tb, string name, string id)
         {
             if (!string.IsNullOrWhiteSpace(name))
             {
                 name = TagBuilder.CreateSanitizedId(name);
                 if (string.IsNullOrWhiteSpace(id))
                 {
                     tb.GenerateId(name);
                 } else
                 {
                     tb.MergeAttribute("id", TagBuilder.CreateSanitizedId(id));
                 }
             }
             tb.MergeAttribute("name", name);
         }
     } }
@{     ViewBag.Title = "About";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>
 
<p>Use this area to provide additional information.</p>
@Html.MP3Player("chad", "/Media/Infectious Groves - Feed the Monkey.mp3", true)

If you debug the code, you get the about page and an MP3 player.  I clicked play and it renders as =>
image

Håþþ¥ .ñꆆïñg…

No comments:

Post a Comment