Using Editor Templates Multiple Times on the Same Page in MVC

banner5

In the last post we created a star rating control that can be easily used in model binding and in displaying a record on our page. The rating control used FontAwesome and some CSS hacks to change radio buttons (which are usable from screen readers) into stars. This gave us a reusable rating control in MVC that is accessible and looks pretty darn okay, and meets our needs for model binding.

image_thumb6

But what if you wanted to build out a list of elements that you wanted to rate? Rather than just one movie, you wanted users to be able to rate your whole collection?  Understanding the mechanics of the binding engine are important if you want to POST a list of entities back to an MVC controller.

Getting the Basics

One of the earliest things that attracted me to the MVC Framework was model binding, and the freedom to stop fishing the in the Form object for parameters that may or may not exist. Over time, many of us built libraries of code that we’d suck in to each project so that we could do things like wrap the extraction of a list of checkbox or radio values up into some kind of abstraction. We don’t need to do that in MVC.

You’ll recall that a list of checkboxes in HTML is quite straightforward. The simplest approach might be something like this:

<form method="POST" action="Index3">

    <input type="checkbox" name="movies" title="The Last Starfighter" value="The Last Starfighter" id="movie1" />
    <label for="movie1">The Last Starfighter</label><br />

    <!-- ... -->

    <input type="checkbox" name="movies" title="Amelie" value="Amelie" id="movie2" />
    <label for="movie2">Amelie</label><br />

    <button type="submit">Save</button>
</form>

And to “catch” that data, we need an action (the target of the form above) with the same name that accepts a List<string> with the name of the checkbox. The value attribute of the checkbox will be added to the list parameter in your action. Your action will look something like the following:

[HttpPost]
public ActionResult Index3(List<string> movies)
{
    return View();
}

The model binder allows any of the selected checkboxes to have a landing spot in your list. You don’t have to check for nulls, or iterate through the form collection to discover all that might have been submitted. It’s worth noting here, as well, that if your value attribute were all integers in your form, you could just as easily have the framework bind to a List<int> in your action signature.

So, that’s great for simple values, but…

…What About Binding Complex Objects?

I’m so glad you asked. Smile

imageIf we now look back at our rating control, the control itself is actually ready to go as-is, but we’d need to get a bit more code into our view page (not the partial, but more like, the list of the movies) to get it to tick correctly. 

Here’s what we’ll do:

  • Change our index action to return a list
  • Iterate over the list in our view
  • Pass in some secret magic sauce to the HtmlHelper that renders our stars control

We’ll load up some fake data to push to the view. You would typically want to get this data from a database or an API call of some kind. For now, we’ll go with this:

public ActionResult Index()
{
    var movies = new List<Movie>
    {
        new Movie{Title = "The Last Starfighter", Rating = 4},
        new Movie{Title = "Flight of the Navigator", Rating = 3},
        new Movie{Title = "Raiders of the Lost Ark",  Rating = 4},
        new Movie{Title = "Amelie",  Rating = 5}
    };
            
    return View(movies);
}

Great, now we just need to update our view. Basically, you can take everything out of the form and just replace it with the following, a foreach over the collection we’re passing in:

    @foreach (var movie in Model)
    {
        <div class="row">
            <div>
                <p>@movie.Title</p>
                <input type="hidden" value="@movie.MovieId" name="@string.Format("movies[{0}].MovieId", i)" />
                <input type="hidden" value="@movie.Title" name="@string.Format("movies[{0}].Title", i)" />
                <p>@Html.EditorFor(p => movie.Rating, "StarRating", string.Format("movies[{0}].Rating", i++))</p>
            </div>
        </div>
    }

Also, don’t forget to change the @model on your page to the collection of Movie as so:

@model List<YourApplication.Models.Movie>

Finally, update the code block at the top of the page to declare a variable that we will use as a counter over our collection:

@{
    ViewBag.Title = "My Awesome List of Awesome Movies";
    var i = 0; // movie control index
}

We’re moving along now! There’s two crucial parts in how all this will work when it gets rendered on the client and eventually hits the model binder on the way back in when the form is submitted. The first is the way I’ve constructed the name attribute on our controls. For instance:

name="@string.Format("movies[{0}].MovieId", i)"

In the above code, on the first iteration, the name would be rendered as “movies[0].MovieId”. The prefix “movies” is what our parameter needs to be called on our action (for the POST). The index notation [0] tells MVC which element this object is in our list, and finally, the “MovieId” is simply the property we want to pass in. 

Note: While I’m passing in the ID and the Title as hidden params, I’m only doing so to show the model binder in action. You should always test/cleanse these values in your controller to prevent over-posting of data, where a malicious user could modify the object without your consent.

The second noteworthy point is the overload that I’m calling on the EditorFor method:

@Html.EditorFor(p => movie.Rating, "StarRating", string.Format("movies[{0}].Rating", i++))

That’s important because I’m passing the similarly generated name (as above) as the name of the HTML field name. Remember that in the last post the EditorTemplate contained references to the ViewData.TemplateInfo.HtmlFieldPrefix? That is normally provided for you by default, but in the case of a list of objects, you need to provide it on your own. That code, in the Shared\EditorTemplates folder, was referenced like this:

var htmlField = ViewData.TemplateInfo.HtmlFieldPrefix;

…and it was sprinkled throughout the control template.

Finally, we’re going to need to get that POST action in line.  Update your controller method to look like this:

[HttpPost]
public ActionResult Index(List<Movie> movies)
{
    return View(movies);
}

Now, when the form is submitted each set of indexed form fields will be used to new up an instance of our Movie class and placed in the collection. I’ve set a breakpoint so that you can see it in action:

image

You’d obviously want to do more than just cycle the movies through the controller, so rather than just returning the list you could do your validation, update your database, or aggregate stats from this response to a running average. So much fun to be had.

Summary & Next Steps

I always like to encourage folks to try things out and see if they can get it working on your own. It all starts with File –> New Project. Be sure to check out my Bootstrap series or purchase a copy of my book for more examples like this, and happy coding!

Using Font Awesome in an Accessible, Bindable Star Rating Control


banner

Creating a rating control that works across browsers, looks good and can be taken advantage of by the MVC Framework requires a little CSS know-how, a couple of freely-available frameworks and some convention-following. There’s a bit of work to get it set up, but it’s reusable, accessible and gosh darn it, it looks good, too.

Don’t overlook the accessible bits! It’s important to remember that most organizations and agencies have policies in place ensuring that folks with disabilities can use alternate methods to view your site. While I am by no means an expert in this area I do have the help of a friend who will try things out for me from time to time in a screen reader and I try to keep these concerns at the front of my mind when doing something “off course”.

Our Starting Point

I’m building off of the work in the last post, where I added Font Awesome to our Bootstrap-enabled MVC 5 web site. Let’s drop a model class for a movie in our project in the Models folder that looks like this:

public class Movie
{
    public int MovieId { get; set; }
    public string Title { get; set; }
    [UIHint("StarRating")]
    public int Rating { get; set; }
}

Next, let’s update the HomeController to have the following actions:

public ActionResult Index()
{
    var movie = new Movie
    {
        Title = "The Last Starfighter", 
        Rating = 4
    };
    return View(movie);
}

[HttpPost]
public ActionResult Index(Movie movie)
{
    return View("Index", movie);
}

Nothing too fancy here, just a GET and POST action for Index requests. The GET populates (simulates) someone navigating to an item that they can rate. The POST allows you to set a breakpoint so that you can inspect the movie result when you submit.

Finally, scaffold a view for Index. First, delete the Index.cshtml from the Views/Home folder in your project. Next, right-click on one of the the Index methods above and select Add View, then choose the Edit template (don’t create a partial view here) and let it build the page for you.  Finally, run your project to see what you get; it should look like this:

image

That’s okay, but, here’s what we want to see:

image

Getting Stylish

So, believe it or not, those are just radio buttons! There’s a couple of things we do to make it come together, including the use of a container DIV, but it’s mostly straightforward. Let’s look at the basic HTML structure that makes this tick.

<div class="rating">
  <input type="radio" id="star5-Rating" name="Rating" value="5">
  <label for="star5-Rating" title="Best">5 stars</label>
  
  <!-- ...more like these... -->
  
  <input type="radio" id="star1-Rating" name="Rating" value="1">
  <label for="star1-Rating" title="Pretty Bad">1 star</label>
</div>

The container holds a collection of inputs and labels. These elements are attributed as you’d expect, but are inserted in the document in reverse order so that “5 stars” is at the top and “1 star” is at the bottom. This has to do with CSS bits, which we’ll come to in a minute.

Oh…here we are, the CSS bits!

Right, so we start by setting up the container and getting our input off-screen.

.rating {
    float:left;
}

.rating:not(:checked) > input {
    position:absolute;
    top:-9999px;
    clip:rect(0,0,0,0);
}

This works okay because if a user taps, clicks or selects a label with a screen reader, the input is still appropriately checked. Next, we wire up the label.

.rating:not(:checked) > label {
    float:right;
    width:1em;
    padding:0 .1em;
    overflow:hidden;
    white-space:nowrap;
    cursor:pointer;
    font-size:3em;
    line-height:1.2;
    color:#e0e0e0;
}

.rating:not(:checked) > label:before {
    font-family: FontAwesome;
    content: "\f005";
}

These two styles push the label off-screen and setup the fixings for our star, including the insert of the character from Font Awesome.

We float these to the right so that we correct our order; remember that we inserted the stars in reverse order in our HTML, here we use CSS to correct that. This workaround is needed for the foreseeable future as we do not have CSS selectors to help us resolve things “before” our element, only after. By putting rating 1 after rating 3, we can select 3, 2 and 1 with various combinations of selectors; however, there is no mechanism allowing us to select the other way, i.e., “before”.  (The before psuedo-selector allows us to insert content, but not apply style transformations.)

Finally, we handle the color states with various combinations to handle selected stars, stars that were previously selected and stars that are hovered (but not yet selected).

.rating > input:checked ~ label {
    color: #fa0;
}

.rating:not(:checked) > label:hover,
.rating:not(:checked) > label:hover ~ label {
    color: #fe0;
}

.rating > input:checked + label:hover,
.rating > input:checked + label:hover ~ label,
.rating > input:checked ~ label:hover,
.rating > input:checked ~ label:hover ~ label,
.rating > label:hover ~ input:checked ~ label {
    color: #ea0;
}

It may seem like there’s a lot of action going on in that code, but it’s required. The >, +, ~ and : selectors in there allow us to be very specific about which elements in which state require coloring.

Add all of those CSS bits to your Site.css file (in the Content folder) and your styles will be in place. All that’s left to do now is to take that rough-in of HTML we did before and turn it into something the MVC Framework can use.

Bringing Some MVC to the Equation

You’ll have noticed the attribute UIHint decorating our Rating property on the Movie model class that we created in the first step. That was no accident! Now, let’s get that working for us by creating a reusable partial view in our project. Add a file called StarRating.cshtml to the Views\Shared\EditorTemplates folder. By convention, this is the folder that the MVC Framework checks for custom views used to render properties or models (you can see another sample here from my Bootstrap series).

The StarRating.cshtml should look like this:

@model int

@{
    var chk = "checked";
    var checked1 = Model == 1 ? chk : null;
    var checked2 = Model == 2 ? chk : null;
    var checked3 = Model == 3 ? chk : null;
    var checked4 = Model == 4 ? chk : null;
    var checked5 = Model == 5 ? chk : null;
    var htmlField = ViewData.TemplateInfo.HtmlFieldPrefix;
}

<div class="rating">
<input type="radio" checked="@checked5" id="star5-@htmlField" name="@htmlField" value="5" /><label for="star5-@htmlField" title="Best">5 stars</label>
<input type="radio" checked="@checked4" id="star4-@htmlField" name="@htmlField" value="4" /><label for="star4-@htmlField" title="Good">4 stars</label>
<input type="radio" checked="@checked3" id="star3-@htmlField" name="@htmlField" value="3" /><label for="star3-@htmlField" title="Average">3 stars</label>
<input type="radio" checked="@checked2" id="star2-@htmlField" name="@htmlField" value="2" /><label for="star2-@htmlField" title="Not Great">2 stars</label>
<input type="radio" checked="@checked1" id="star1-@htmlField" name="@htmlField" value="1" /><label for="star1-@htmlField" title="Pretty Bad">1 star</label>
</div>

Here’s a breakdown of the interesting bits that make this work:

  • The model is of type int, allowing it to be rendered for properties of the same type.
  • There is the use of nullable attributes in play, set up with the check against the model so that we can correctly render which star is to be selected. This is important when our model already has a value.
  • The radio buttons are ordered in reverse and take their name from the ViewData.TemplateInfo.HtmlFieldPrefix value.
  • We use an ID on each star, and a label that is related back to that input. This allows selection via the label (you should always use this!)

Viola! With the CSS from above in place, the custom editor template saved and the UIHint on your Movie model class, your page will now be rendered with a star rating control for your movie.

Set a breakpoint in your POST action of your controller. If you submit your form while debugging, you will see that the value you have selected is indeed bound to the property of the Movie class. Huzzah!

Credits

I have leaned on many star rating controls over the last decade, but have come back to variants of this one (based on this work from @Lea Verou) for the last two or three years now. I would like to stress that I haven’t yet found a perfect control (this one, for example works well with up/down arrows, but not left/right) and I am sure this version won’t work for all people in all scenarios. Also, the idea for this example came from the Font Awesome site, but the astute reader would soon realize why the basic example on their site would not be a good candidate for MVC (primarily because of the lack of structure to accommodate multiple controls on the same page).

Next Steps

If you’ve built the sample along with the article, you’ll likely have had at least a few ideas on how you can create reusable controls for your project. Be sure to check out my Bootstrap series or purchase a copy of my book for more examples like this, and happy coding!

Adding Some (Font) Awesome to MVC and Bootstrap

While Bootstrap includes many great icons from Glyphicons baked right in, I’m often left looking for That One Missing Graphic when I try to put a project together. In this post we’ll look at the open source Font Awesome collection of images that serve as a wonderful addition to your site in the form of a font and CSS toolkit.

See more tips, tricks and tutorials like this in my book, Bootstrapping MVC.

Just a quick note: If you already know what Font Awesome is and just want my advice on how to add it to your project via NuGet, skip to the end of the post. If you want more background on what Font Awesome is and why you might want to use it, grab a coffee and have a read!

A Bit About Font Awesome

A Picture of a Heart IconFont Awesome is a font and CSS toolkit that was created by Dave Gandy, originally designed to be a compliment to the Bootstrap library. There is no JavaScript component to it and it does not add any new behaviors to your site, but it does have some great-looking, scalable icons that you’ll likely love.  See what I did there, with the heart? Winking smile

There are two core elements to using the library: the font files themselves and the CSS classes that wire things up for use on your site. With the library in place, you can get the above heart icon to appear simply by adding the following syntax:

<i class="fa fa-heart"></i>

Because it’s implemented as a scalable vector-based font, rather than raster images, the icons look great on any screen type and at any size. You get the base styling, margins, sizing and the like by attributing the <I> element with the fa class, then select your icon by name. There are CSS classes for creating variant sizes (twice as large):

<i class="fa fa-camera-retro fa-2x"></i>

…adding spinner effects (here, a spinning cog):

<i class="fa fa-cog fa-spin"></i>

…or stacking images together to make composite icons, like a flag on a circle:

<span class="fa-stack fa-lg">
  <i class="fa fa-circle fa-stack-2x"></i>
  <i class="fa fa-flag fa-stack-1x fa-inverse"></i>
</span>

…as well as other options, like rotating and flipping, adding borders and pulling the icons to the left or right. You can find the complete source on the Font Awesome examples page.

While these display classes are all built to work with Bootstrap natively, the CSS for Font Awesome also includes everything it needs class-wise (such as pull-left) to work independently of Bootstrap.

The icons tend to be aptly named and terse, so you’re not typing a metric tonne of code. I’m using Visual Studio 2013 and working with the library is a breeze because of the automatic syntax completion offered through IntelliSense.

HTML Code: <i class="fa fa-heart"></i>

If you download the ZIP from the Font Awesome site, you’ll get the CSS (and minified version), the font files for multi-platform and multi-browser support as well as the LESS and SCSS files to customize things should you so need.

A Note on Accessibility

Though the icons and images of Font Awesome are indeed implemented as a font, it’s not like wingdings or anything where a letter becomes repurposed. The author elected to use the standards-based Private Use Area mapping from Unicode, meaning screen readers for accessibility will ignore the characters of the font and not attempt to read random characters off.

The Naked “Installation”

From the docs on the site, the easiest way to start using Font Awesome is just to use the CDN version of the toolkit.

<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">

Using the CDN version may help improve load times, especially when your users are spread out over multiple geographic regions. Not all corporate policies allow for public CDN use for in-house projects, however, so you still have the option to download and use Font Awesome as an asset in your deployment.

Solution structure for typical installation of Font AwesomeIf you do download the ZIP, you’ll need to execute a couple of familiar and straightforward tasks to use the toolkit:

  • Copy the CSS and Fonts folder into your project, likely under the Content folder
  • Add a new bundle to your site and include the CSS in your _Layout (or modify an existing, in-use bundle to include the CSS).

The only thing to watch for is that you’re going to need to honor the relative path structure of the CSS and Fonts to avoid any rendering problems. The CSS assumes the fonts are in a folder called Fonts at the same level in the path.

Using a NuGet Package

There’s always an easier way to do it, and usually the way to do that with .NET is going to involve a little bit of NuGet.

I found no fewer than half a dozen packages on NuGet for Font Awesome. While they all seem to list Mr. Gandy as the author, none of them appear to be officially part of the project. So, for better or for worse, there’s no officially sanctioned project here. Just the same, the packages are there if you want to use them and I have my opinions on them. Smile

The package I recommend is FontAwesome.MVC by JiveCode aka JustLikeIcarus. This package takes a dependency on his Font Awesome package containing the CSS and fonts, then it adds a class (FontAwesomeBundleConfig) that registers a style bundle for you.

All that’s left to do is to modify your _Layout, adding the following line of code to your HEAD:

@Styles.Render("~/Content/fontawesome")

There are other packages that use Font Awesome and try to augment MVC giving your Razor helper methods, but I’ve got mixed feelings on this approach. A Razor helper that requires me to type more code and allows me to know less about the CSS I’m using doesn’t feel quite right. With IntelliSense support for CSS in VS, I’m not sure I see the need for Razor helpers, and I really like learning the classes I’m using.

Next Steps

With a groovy new set of icons now available to my site, my next post will be on leveraging those to create something interesting with the MVC Framework.

Like this post? You can see more tips, tricks and tutorials
like this in my book, Bootstrapping MVC.

Happy coding! Smile

30 Days of Bootstrap and Asp.Net MVC 5

Source Code and Book Now Available

I had many requests through my recent series on Bootstrapping MVC to post the completed source code for the project that I created, and I’m happy to share that now.

View the Completed Project on GitHub

I will note that this is how the project is at the end of the series and things do change. Some of the days later on in the exercises are refactorings over the earlier work.

Getting All the Code, Day-by-Day + Bonuses

I took things further, however, and I’ve edited the series into both an eBook and a print version. Included with either is access to the repository with complete day-by-day branches so that you can work on any aspect of the project, play around, make changes or customizations and then just move to a different chapter.

Can’t get past a certain issue with your syntax or markup? Do a diff against my work and see what got away. Each chapter branch contains the completed, working code for that chapter. You can use the previous chapter’s branch to start work as you follow along from anywhere in the book.

As well, for any of my blog readers (yes, that’s you!) I’m also offering the book at a deeply discounted price of $6.99 (from a suggested $24.99) for the first 50 folks who snatch up the offer!

Get Bootstrapping MVC for the discounted price of $6.99 with this coupon: http://leanpub.com/bootstrappingmvc/c/readblogsFTW

For those of us (me included) who still like killing trees and having a paper copy next to the keyboard, I’ve also got a print version available of the book which would make a great addition to any web developer’s library.

Buy the paperback edition here: http://www.lulu.com/shop/james-chambers/bootstrapping-mvc/paperback/product-21757965.html

Finally, for the Kindle readers out there, you can also pick up a copy of the book on Amazon!

Enjoy the book and code, and happy coding!

Day 30: Loading Bootstrap Modal Content via AJAX

This is the final installment in a 30 day series on Bootstrap and the MVC Framework. To see the rest of the series be sure to check out Day 0 for an index. I hope you have enjoyed following along! Smile

Here in our last post in the series we’re going to revisit our modal dialog confirmation box that pops up when a user tries to delete a record. At this point, regardless of the notification selected for delete, the user will see the same static dialog.

Loading Remote Content With Bootstrap’s Modal

There is built-in support for Bootstrap to load content into a modal, but there are some limitations. First, Bootstrap loads the content the first time the constructor is called on the modal. Secondly, the modal’s constructor is only ever called once, meaning, you can’t refresh the content without some extra script.

Rather than trying to put a square peg in a round hole, we can take the simpler approach of just loading the content with jQuery. After all, it’s clean, easy to understand, and at the end of the day the modal is just HTML.

This approach will work better for us for the following use case:

  • User selects to delete a notification
  • They cancel out of the dialog
  • The select a different notification to delete

At this point, the remote loading capabilities of Bootstrap’s modal break down and we’d have to get into hacky stuff that may not survive a minor version upgrade. Pass! In my opinion, the modal should have an overload that accepts a ‘remote url’ parameter.

If, however, you have a use case that doesn’t involve refreshing the dialog, I encourage you to check out the docs for using the built-in functionality.

Improving our Delete Confirmation

Rather than just giving a generic message, we’re going to instead load some content via AJAX. This will give us a chance to put a visual preview of the data that will be deleted in front of the user.

image

To accomplish this we’re going to need to make small change to our modal markup (in Views\Account\_RenderNotifications.Modal.cshtml), as follows:

<div class="modal-body" id="notificationPreview">
    <p>Loading content...</p>
</div>

What I’ve done here is removed the content from the modal-body section of the markup and replaced it with a loading message.  I’ve also given the DIV an ID so that we can address it from JavaScript more directly.  Also, revisit your Views\Account\_RenderNotifications.js.cshtml and update the confirmDelete method, as we’re now going to clear the contents and load the text dynamically.

function confirmDelete(id) {
    currentNotificationId = id;
    previewContainer.html('<p>Loading content...</p>');
    previewContainer.load(confirmUrl, { id: currentNotificationId });
    $('#deleteConfirmModal').modal('show');
}

To pull that all together we need a couple more variable declarations at the top of the script. Here’s my final list of vars:

var readUrl = '@Url.Action("MarkNotificationAsRead")';
var deleteUrl = '@Url.Action("Delete")';
var confirmUrl = '@Url.Action("GetNotification")';
var previewContainer = $('#deleteConfirmModal #notificationPreview');
var currentNotificationId;

You’ll notice that I have added a confirm URL and captured the element (in previewContainer) that we are using for the confirm. This saves me from having to requery the DOM on each display of the dialog.

Adding the Controller Action

We’re going to use that same pattern for loading up the notification for the confirmation that we used for modifying and deleting. Let’s add a GetNotification method to our AccountController that has the following code:

public ActionResult GetNotification(int id)
{
    var userNotification = GetUserNotifications().FirstOrDefault(n => n.NotificationId == id);

    if (userNotification == null)
    {
        return new HttpNotFoundResult();
    }

    return PartialView("_RenderNotifications.ModalPreview", userNotification);
}

This time, we’ll be returning a partial view, as indicated in the code above, that will help us render the relevant content for the user to make their choice.

But hey…that code to load a single notification for the logged in user is crying for some refactoring, am I right?

Adding the Partial View

The controller is wired to pass the single notification to a partial view, which will essentially have the same information we had in the content section of the modal markup, but now we’ll also render in some notification-specific information. You can use your imagination on how this could be further extended with more complex objects in your own project.

Here are the contents of _RenderNotifications.ModalPreview.cshtml, which you should put into Views\Account:

@model SimpleSite.Models.Notification

<p>You have selected to delete this notification.</p>
<p>
    <div class="panel panel-warning">
        <div class="panel-heading">
            <h4>@Model.Title</h4>
        </div>
        <div class="panel-body">
            If this was the action that you wanted to do,
            please confirm your choice, or cancel and return
            to the page.
        </div>
    </div>
</p>

Next Steps

Wow! Thirty days of Bootstrap and MVC! It’s been a long 30 days…50 days actually, but no one could have foreseen the passing of my Grandma, my three-day fever of 103 degrees or the fact that this would end up running into my vacation for my 14th wedding anniversary and a most excellent trip to Seattle with my high school sweetheart (to whom I am married).

I have a few more things to add to this series, but for now we’ll call it a wrap. Thanks for your questions, suggestions, feedback and comments along the way!

Happy coding! Smile

Day 29: Confirmation Dialogs for Delete Actions

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

We just gave our users the ability to delete a record from the database in Day 28, but a single click does the deed without confirmation. It would likely be better to at least give them a little prompt to confirm that this is what they were trying to do in the first place.

image

Let’s first talk about how dialogs are composed and how to display them in a page.

Bootstrap Modal Dialogs

Modals are made up of a wrapper, an outer and inner container and three common sections that provide default styling and handle proper rendering: the header, the body and the footer. You can represent the basic structure of the modal as follows:

<div class="modal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">...</div>
            <div class="modal-body">...</div>
            <div class="modal-footer">...</div>
        </div>
    </div>
</div>

You can show a dialog by using a button with an attribute that Bootstrap is aware of:

<button class="btn" data-toggle="modal" data-target="#theModal">
  Show my modal
</button>

Or you can invoke the modal by calling it from JavaScript:

$('#deleteConfirmModal').modal('show');

For other events, defaults and other JavaScript method calls that are available, be sure to check out the docs on the Bootstrap site.

When using modals you’ll want as little interference as possible with the styles and layout of the elements being used, so the recommendation is to put the container for the modal as close to the root of the document structure as possible.

One thing that I’ve run into when using the button approach is the need to have the CSS selector on the data-target attribute (see the code above). I’m not sure why this is the approach, but if you forget that you need the hashtag as a prefix to the ID, your modal won’t display. Regardless, we’re going to be using JavaScript to do our work today, but keep that tip in mind as you start working with this on  your own.

The Game Plan

For this exercise we’re going to hit a couple of areas of the project, so here’s a quick checklist if you want to follow along. We’re going to:

  • Clean up our JavaScript and put it in a separate file
  • Add an optionally rendered section to our _Layout page
  • Add a partial view for the markup needed for our dialog
  • Update the Manage view to include our new bits

Some Cleanup, Some JavaScript

We’re going to start growing our client-side scripts on the view that renders notifications a little and one thing I like to do is to keep those bits organized. The JavaScript we have so far to give some UX for notifications is in our _RenderNotifications partial view, which presents some limitations as to when script can execute and whether or not jQuery has been loaded.

One easy way to keep these things straight, and organized in our project, is to add a _PartialViewName.js.cshtml file to our project in the same folder as our view. We can then consider the JavaScript related to the view as another asset that needs to be loaded on the full view.

So let’s do that. Add a new view to the Views\Account folder called _RenderNotificationsjs. If you use the scaffolding tools, you’ll notice that you can’t properly name it (it complains about invalid characters), so add it as I’ve shown, then rename it to _RenderNotifications.js.cshtml once the file is there.  Next, remove the old JavaScript from the _RenderNotifications.cshtml and put it in the new .js.cshtml file.

<script type="text/javascript">
    
    var readUrl = '@Url.Action("MarkNotificationAsRead")';
    var deleteUrl = '@Url.Action("Delete")';
    var currentNotificationId;

    function updateNotification(id, action) {
        $("#notificationFormItemId").val(id);
        switch (action) {
        case 'read':
            $("#notificationForm").attr('action', readUrl).submit();
            break;
        case 'delete':
            $("#notificationForm").attr('action', deleteUrl).submit();
            break;
        default:
            console.debug('Unknown action ' + action);
        }
    }

    function confirmDelete(id) {
        currentNotificationId = id;
        $('#deleteConfirmModal').modal('show');
    }

    $(function() {
        $("#deleteConfirmModal").on('click', "#deleteConfirm", function() {
            updateNotification(currentNotificationId, 'delete');
        });
    });

</script>

Remember that .cshtml gives us a little more power than just a straight .js file. We can do things like interact with the ViewBag or the page Model, write code that is parsed by Razor and make use of Html helper methods, all of which can be quite handy.

There’s not too much more going on here than there was before, except that now we capture the ID of the notification in our confirmDelete method, and we are wiring up a click handler for the modal dialog that we’re going to implement.

One final thing to do on our partial is to update the Delete button (the A tag) to call our new JavaScript method as follows:

<a href="javascript:confirmDelete(@notification.NotificationId)">Delete</a>

Rendering at the Root Using Optional Sections in our Layout

Remember the note about keeping the modal as close to the document root as possible? Well, there’s a problem there when we’re down at the view or partial view layer, stuck inside our other “main content” area of the page. We’re inside all these other containers already and we can’t break out without a little help from the MVC Framework.  What we want to do is to add another optional section, much like the scripts section. We can call this topLevel for now, though you’d likely want it to be more meaningful in a real project.  Add the following code to your _Layout file.

@RenderSection("topLevel", required: false)

It should appear right after the BODY tag, first thing in the document, like this.

image

Now, whenever you have something that will be injected from a view into the page in this topLevel section, you will be able to put elements directly into the root of the document.

Create the Modal Partial

Next up, we’re going to create a _RenderNotification.Modal.cshtml partial view that has the HTML for our modal in it. This will help to keep our core view simple, and keep our related files together in our project.  The markup for the modal follows the same basic structure I highlighted above and adds a few other elements to the mix.

<div class="modal fade" id="deleteConfirmModal" tabindex="-1" role="dialog" aria-labelledby="deleteLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title" id="deleteLabel">Deleting a Notification</h4>
            </div>
            <div class="modal-body">
                <p>You have selected to delete this notification.</p>
                <p>
                    If this was the action that you wanted to do,
                    please confirm your choice, or cancel and return
                    to the page.
                </p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-success" data-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-danger" id="deleteConfirm">Delete Notification</button>
            </div>
        </div>
    </div>
</div>

There’s a class ‘fade’ in there that tells bootstrap to slide and fade the modal in from the top. There are a few ARIA attributes in there for accessibility.  I’ve added a title to the header, content in the body and buttons to either dismiss the dialog (cancelling the delete) or to confirm the operation when the user elects to delete the notification.

Perhaps the most interesting bit in there is the data-dismiss attribute on the cancel button, which tells Bootstrap that this dialog can be hidden when the button is clicked.

Updating Our View

Finally, we update our view, adding the modal to the page and including the JavaScript that we need to pull it all together.

At the bottom of Manage.cshtml in Views\Account we add the topLevel section to the view and in it we render the modal markup from the partial view we created.

@section topLevel{

    @{ Html.RenderPartial("_RenderNotifications.Modal"); }

}

Immediately after that, we update our scripts section to include the JS we need and created above.

@section Scripts { 

    @Scripts.Render("~/bundles/jqueryval") 
    @{ Html.RenderPartial("_RenderNotifications.js"); }

}

Because we’ve kept things fairly organized along the way, changes to our view are minimal but at the same time we’re able to improve the user experience a fair bit. We’ve added an easy way to get content into the root of our document and simplified our partial views. And look…

image

…we even managed to keep our related files in one place in the solution explorer. 

Next Steps

Our delete is now protected by the dialog, but we’d like to give the user a better picture of what it is they are deleting from the database. Tomorrow, in our last post in the series, we’ll look at wiring up the dialog box to load contents via AJAX.

Day 28: Doing More Interesting Things With Buttons

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

Using the styling of Bootstrap or a replacement theme is a great way to make your site stand out, and buttons are a great example of making the UI more rich and friendly to users. They are likely the first thing you’d write some CSS for if you were starting from scratch on your site, so the tweaks and highlights they offer out-of-box are quite welcome.

Basic Buttons

If you don’t style your buttons, they’re going to kinda suck. Well…it’s not that they won’t work or anything, but you will just get the boring old browser-styled version of a button.

image

And because each browser has it’s own default stylesheet, you can’t be guaranteed that any user will see the same font, layout or spacing. Thankfully, enforcing these attributes across clients is only an attribute away.

<button type="button" class="btn btn-primary btn-sm">A button</button>
<button type="button" class="btn btn-success btn-sm">Another button</button>

You’ll notice that I used the btn-sm class in the source above, there are large (btn-lg), regular (no class needed), small (btn-sm) and extra-small (btn-xs) to choose from. And now my buttons look like so, in every browser:

image

If I want them to be the same width, I can also use the grid system for sizing, dropping, say, a col-md-4 into the class attribute to get the following result:

image

Grouping Things Together

The buttons look better now, but they’re bumping up against each other. They’d look better still if they looked like they were part of the same control group. We can assemble buttons into groups by providing a touch more markup to the elements, as seen below, with a simple DIV wrapper.

<div class="btn-group">
    <!-- buttons here -->
</div>

image

I changed the text here because this is the code that I’ll be working into my partial for the notifications.  I also made the buttons the same color – from a design stand point I think it looks better (and, heck, I’m even color blind) – and I removed the grid sizing on the buttons to allow them to group properly without having to add custom CSS styles.

Putting Some Fancy On

Chances are we could get away here with the two buttons side-by-each, but in the event that we need to add more actions the page is going to start to get a little wide. An improvement we can make here is to give the users a default action, but then to put the rest of the less-commonly accessed actions into a dropdown to look like this:

image

This is called a split button dropdown, where a clickable action is available and additional commands are tucked into the drop down. Rather than wrapping the buttons in just a DIV, we also introduce a caret for the dropdown and have a UL to act as a container for the non-visible actions.

<div class="btn-group">
    <button type="button" class="btn btn-success btn-sm">Mark as Read</button>
    <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown">
        <span class="caret"></span>
        <span class="sr-only">Toggle Dropdown</span>
    </button>
    <ul class="dropdown-menu" role="menu">
        <li><a href="#">Delete</a></li>
        <li><a href="#">Send SMS</a></li>
        <li><a href="#">Make cheese</a></li>
        <li class="divider"></li>
        <li><a href="#">Baz</a></li>
    </ul>
</div>

I’ve added in the correct attributes on the caret button element – namely the data toggle – so that it plays properly with the framework. Per the documentation, I’ve also added an sr-only class that allows screen readers to make sense of the control grouping. The inner commands are now simply anchor tags and don’t require any of the button classes.

So, there all-of-a-sudden seems to be a lot going on there, but let’s break down the basic structure:

  • A DIV to group everything together
  • A BUTTON for the main action
  • A BUTTON for the caret (dropdown)
  • A UL with a list of LIs for the additional commands.

The skeleton looks like this:

<div class="btn-group">
    <button type="button" class="btn">Primary Action</button>
    <button type="button" class="btn dropdown-toggle" data-toggle="dropdown">
        <span class="caret"></span>
        <span class="sr-only">Toggle Dropdown</span>
    </button>
    <ul class="dropdown-menu" role="menu">
        <li><a href="#">...</a></li>
        <li><a href="#">...</a></li>
    </ul>
</div>

One final note: You would correctly assume, were you the assuming type, that the drop down functionality requires the JS library to work (which is already included in our web site bundle and layout).

Updating our View

Now, forget what we’ve done so far in the TD for our actions. We’re going to change it up a bit and get things properly in line for some real-world action.

Since we’re going to be changing data we are going to want to do a POST back to the server, so we have the option of using the Html.BeginForm helper.  But generating a series of forms representing all actions for all notifications could get complicated quite quickly (in other words, we don’t want a form for each action, for each notification). Instead, we need to decorate our split buttons with the appropriate IDs, then put together a bit of JavaScript to do the submit for us on a single, static form. The form contains a hidden input for the ID and will be at the end of the document in _RenderNotifications, followed by the related JavaScript:

<form id="notificationForm" method="POST"><input type="hidden" name="id" id="notificationFormItemId" /></form>

<script type="text/javascript">
    
    var readUrl = '@Url.Action("MarkNotificationAsRead")';
    var deleteUrl = '@Url.Action("Delete")';
    
    function updateNotification(id, action) {
        $("#notificationFormItemId").val(id);
        switch (action) {
            case 'read':
                $("#notificationForm").attr('action', readUrl).submit();
                break;
            case 'delete':
                $("#notificationForm").attr('action', deleteUrl).submit();
                break;
            default:
                console.debug('Unknown action ' + action);
        }
    }
    
</script>

Here we are using the URL helper method to resolve the URLs needed for read and delete actions.  The JavaScript simply accepts the ID and an action parameter, then sets up the form correctly and submits it.

Next, skip back up the document a little to the TD that contains our split button. We’ll replace the entire contents of the TD with the following:

<div class="btn-group">
    <a class="btn btn-success btn-sm" href="javascript:updateNotification(@notification.NotificationId, 'read')">Mark as Read</a>
    <button type="button" class="btn btn-success btn-sm dropdown-toggle" data-toggle="dropdown">
        <span class="caret"></span>
        <span class="sr-only">Toggle Dropdown</span>
    </button>
    <ul class="dropdown-menu" role="menu">
        <li>
            <a href="javascript:updateNotification(@notification.NotificationId, 'delete')">Delete</a>
        </li>
        <li><a href="#">Send SMS</a></li>
        <li><a href="#">Make cheese</a></li>
        <li class="divider"></li>
        <li><a href="#">Baz</a></li>
    </ul>
</div>

You’ll notice that our A tags are now just invoking our JavaScript method, passing in the notification ID of the current record (remember that we’re in that outer foreach loop here). We can continue to develop new methods by wiring up the A tag and extending the switch statement should we like.

A Couple of Controller Methods

Finally we’re going to need to wire up our controller to catch those user actions and do the dirty bits. Marking the notification as viewed will be first. We’ll use the DB context to retrieve, update and save the selected record, so we’ll make a small change to the way we initiate the DB context and move it to a class-level declaration as such:

private readonly SiteDataContext _context = new SiteDataContext();

Remember to remove the following line of code from the GetUserNotifications method and update all occurrences of context to _context (your code won’t compile if you don’t).

[HttpPost]
public ActionResult MarkNotificationAsRead(int id)
{
    var userNotification = GetUserNotifications().FirstOrDefault(n=>n.NotificationId == id);

    if (userNotification == null)
    {
        return new HttpNotFoundResult();
    }

    userNotification.IsDismissed = true;
    _context.SaveChanges();

    return RedirectToAction("Manage");
}

Similarly, the code for the delete action will use the same DB context to delete the requested record.

[HttpPost]
public ActionResult Delete(int id)
{
    var userNotification = GetUserNotifications().FirstOrDefault(n => n.NotificationId == id);

    if (userNotification == null)
    {
        return new HttpNotFoundResult();
    }

    _context.Notifications.Remove(userNotification);
    _context.SaveChanges();

    return RedirectToAction("Manage");
}

There are a couple of quick notes here that I should mention. From a security standpoint we’re doing some good things, namely, we’re using a common method to only load into scope the notifications of the currently logged in user. We’re leveraging the fact that the framework provides a mechanism to correctly provide the user’s identity and limiting the user’s request to only allow execution of modifying or deleting records in a “row-level” security kind of way. Our controller is marked with the Authorize attribute, so if we couple this with SSL we’d be in pretty good shape.

Then there’s the bits that aren’t really good practice, but are good enough for the point of this exercise. First, we’ve got this GetUserNotifications method on the AccountController class.  What about “AccountController” says anything about “loading the notifications for a specific user”? Nothing, right? This data access, as I’ve mentioned before, should be pushed into a repository that makes use of the DB context through a Dependency Injection mechanism that only creates one instance of the DB context per request. And, the controller should take that repository through DI.  This would make things more flexible (why write data access in more than one spot) and testable (we could use mocks to test more easily).

Next Steps

Certainly you wouldn’t want your users deleting data without confirmation (if at all), so in the next installment we’ll have a look at putting a dialog in place to ensure users are invoking the intended action.

Day 27: Rendering Data in a Bootstrap Table

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

We created a nice tab for our users to be able to manage the different components of their account, hi-jacking the Manage Account feature of the default MVC template. Now we want to render the user’s outstanding notifications in a good looking table in one of those tabs.

Caution: this is the lipstick on a pig post of the series. Here be dragons…

Rendering Tables with Bootstrap

The first thing you need to know is that you don’t have to throw away a single thing you’ve learned in your years of HTML ninja skill building. Tables are still not to be used for layout. Tables are for lists of data. And more importantly, Bootstrap doesn’t try to change the semantics or document structure for a table. In fact, to get the Bootstrap look-and-feel in an HTML table, you need to apply but one class.

<table class="table">
  ...
</table>

So what else does it offer?  A bunch of common things that you might try to do, like “tighter” versions of a table, striped styling or hover-states. You can also use the now familiar contextual classes on rows or cells to highlight pertinent information to your users.

All in all, it’s a clean looking table that will suit most needs and fit in with the rest of the site.

image

Another important aspect of the Bootstrap table is the ability to easily make a table responsive. I don’t have the answer as to why this isn’t the default, but it’s easily added with a DIV wrapper that has the table-responsive class. This adds horizontal scroll bars to your tables, when needed, when they are viewed on smaller screens to ensure that the data doesn’t end up in some non-accessible, off-device part of the screen.

image

That wrapper looks like the following:

<div class="table-responsive">
    <table class="table table-striped ">
       <!-- table rows here -->
    </table>
</div>

Retrieving the Notifications

We’re going to need to get the list of notifications that haven’t yet been dismissed. You may have already created some test data with the efforts from the past few days, and that will work great here.

Open the AccountController.cs class and navigate down to the Manage action.  Just before it, add the following code :

private IEnumerable<Notification> GetUserNotifications()
{
    // get the user ID
    var userId = User.Identity.GetUserId();

    // load our notifications
    var context = new SiteDataContext();
    var notifications = context.Notifications
        .Where(n => n.UserId == userId)
        .Where(n => !n.IsDismissed)
        .Select(n => n)
        .ToList();
    return notifications;
}

All this guy is doing, really, is snapping up the rows for this currently logged in user.

One thing to mention here is that we’re really now crying for some Dependency Injection. Our NotificationFilter and our AccountController are now both creating instances of our SiteDataContext – now multiple instances per request – and it’s making our code harder to test.

Adding the Data to Our ViewBag

Both the GET and POST versions of Manage are already relying on ViewBag to get data up to the view, so we’ll follow the same cue and put our notifications in there. In both methods you’ll find the ReturnUrl being assigned to a ViewBag property, so immediately after that, add the following line of code:

ViewBag.NotificationList = GetUserNotifications();

Adding a Partial View for the Table

Next, let’s get a partial view added called “_RenderNotifications.cshtml”.  The model type for the page will be a IEnumerable<Notification>, and we’ll iterate over the collection of rows to generate the TR and relevant TDs inside each of those. The entire view will look something like this:

@using SimpleSite.Models
@model IEnumerable<Notification>

<div class="table-responsive">
    <table class="table table-striped ">
        <tr>
            <th>Type</th>
            <th>Notification</th>
            <th>Actions</th>
        </tr>
        @foreach (var notification in Model)
        {
            var badgeClass = NotificationType.Email == notification.NotificationType
                ? "label-success"
                : "label-info";
            <tr>
                <td><span class="label @badgeClass">@notification.NotificationType</span></td>
                <td>@notification.Title</td>
                <td></td>
            </tr>
        }
    </table>
</div>

There’s a placeholder in there for “Actions”, which we’ll look at tomorrow when we revisit buttons and explore drop-downs.

Updating our Manage View

With the partial view in place and the data being loaded into the ViewBag we’re ready to update our view. Return to the Manage.cshtml file and locate our placeholder for the notifications. Update it to render the partial view, passing in the collection of notifications.

<div class="tab-pane active" id="notifications">
    @Html.Partial("_RenderNotifications", ViewBag.NotificationList as IEnumerable<Notification>)
</div>

That should be it! I found what seems to be a bug in Bootstrap where if you have a table in a tab, and the tab is set to active and you have the fade class, the table doesn’t seem to be visible on page load. It shows up fine after clicking on a tab, but to work around this you’ll notice that I’ve removed the fade class from the tab.

Oh Noes! It’s Turned Into A Mess!

Folks, I’m not going to lie; though the Account controller and Manage views may have never have been intended to handle our humble little notifications, I found several things in both the controller and related views that left me uncomfortable with the design. I’m trying to keep this as a “how to get some MVC in your Bootstrap” type series, but all of the following were worth noting as things I’d try to avoid:

  • Nested views with non-obvious dependencies
  • Multiple methods that do the same work as each other
  • Magic strings
  • Methods doing too many things or things that you wouldn’t attribute of them by their name (for example, the “Manage” action which resolves status messages based on optional parameters)
  • ViewBag use that gets in the way of the view’s maintainability
  • Views that don’t leverage Bootstrap enough
  • Way too much reliance on ViewBag, and, therefore,
  • Absence of reasonable view models
  • Dogs and cats playing together

I will perhaps one day sit down and hash through the AccountController, but needless to say, there’s some amount of work to be done! I, for one, would like to see more best practices in place, a more referencable example of how to do things and sample views that better embrace the Bootstrap visuals (since, after all, it’s included by default in the template).

Now on our end, we’ve done some off-script things as well, namely putting direct database access in a controller (and filter),  pushing database models directly up to the view and leaning on those ViewBag properties to shuffle data around. So, yes, kettle, meet teapot.

All of these things are leading up to another set of posts on best practices Smile.

Next Steps

Okay, our users can now see the list of notifications that are outstanding, but they have no way to manage them just yet. Tomorrow we’ll get some drop-down button action in play and explore some of the ways we can compose some pushable UI.

Day 26: Bootstrap Tabs for Managing Accounts

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

We’ve got this notification thing going on now and we’d like to give users a way to review notifications. There’s a fairly acceptable landing spot on the “Manage Account” view (at /Account/Manage in your browser), at least for the purpose of these exercises, so we’ll flesh things out there.

However, the view is isn’t really set up for notifications (it’s truthfully not the best spot) so we’ll need to give us some UI to make it work.

Understanding the Tab Component

There are two main elements you’ll need to get the tabs going correctly, a UL tag that will set up as the menu elements, and a DIV to act as a container for the content.

<ul class="nav nav-tabs" role="tablist" id="accountTab">
  <!-- content -->
</ul>

<div class="tab-content">
  <!-- content -->
</div>

Visually, you can link those elements as illustrated below:

image

Because our site includes the JavaScript library for Bootstrap our tabs will automatically render and behave correctly. The classes help with the visuals, and the JS takes care of the behavior.

The UL will contain LI elements for each tab that you wish to display on the page. For us, that will the notifications, linked accounts and password reset. Those last two are the content that already exists on the page at \Views\Account\Manage.cshtml, and the notifications bits are what we’ll fill in after our tabs are in place.

In addition to those two root elements, you can use a bit of JavaScript to manipulate the tabs if needed. For example, if you wanted a particular tab displayed on page load, you could use the ID as part of a jQuery selector and call the show method as follows:

$('#accountTab a[href="#linkedAccounts"]').tab('show');

The DIV for content will in turn hold container elements for the rest of the content you want on the page. The structure will look something like this:

<div class="tab-content">
    <div class="tab-pane active" id="notifications">
        <!-- content -->
    </div>
    <div class="tab-pane" id="linkedAccounts">
        <!-- content -->
    </div>
    <div class="tab-pane" id="passwordReset">
        <!-- content -->
    </div>
</div>

Each of the tab-pane DIVs could also have a fade class applied, which creates a nice content-switching visual. Let’s use that.

Updating the View

If you haven’t done so already, open up the \Views\Account\Manage.cshtml file and start cutting it up! Inside the DIV.row DIV.col-md-12 structure, add the UL for the tab headers, and add a DIV to contain the tab pages including a placeholder for notifications, and the DIVs for linked accounts and password reset. Move the content from those parts of the page in.

The final page code should be similar to the following:

@using SimpleSite.Models;
@using Microsoft.AspNet.Identity;
@{
    ViewBag.Title = "Manage Account";
}

<h2>@ViewBag.Title.</h2>

<div class="row">
    <div class="col-md-12">
        <p class="text-success">@ViewBag.StatusMessage</p>

        <ul class="nav nav-tabs" role="tablist" id="accountTab">
            <li class="active"><a href="#notifications" role="tab" data-toggle="tab">Notifications</a></li>
            <li><a href="#linkedAccounts" role="tab" data-toggle="tab">Linked Accounts</a></li>
            <li><a href="#passwordReset" role="tab" data-toggle="tab">Password Reset</a></li>
        </ul>

        <div class="tab-content">
            <div class="tab-pane fade active" id="notifications">
                <p>Here's where we'll put our notifications.</p>
            </div>
            <div class="tab-pane fade" id="linkedAccounts">
                <section id="externalLogins">
                    @Html.Action("RemoveAccountList")
                    @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { Action = "LinkLogin", ReturnUrl = ViewBag.ReturnUrl })
                </section>
            </div>
            <div class="tab-pane fade" id="passwordReset">
                @if (ViewBag.HasLocalPassword)
                {
                    @Html.Partial("_ChangePasswordPartial")
                }
                else
                {
                    @Html.Partial("_SetPasswordPartial")
                }
            </div>
        </div>
    </div>
</div>


@section Scripts { @Scripts.Render("~/bundles/jqueryval") }

Next Steps

All that’s left now is to get our list of user-specific notifications into our view. Tomorrow we’ll get a view model figured out, populate it with the user’s notifications and get it rendering in a Bootstrap-styled table in the view.

Day 25: Personalizing Notifications

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

Back on Day 19 we introduced persistent storage for notifications, allowing us to pop a custom badge up in the navbar. This was great, but as we left it, it was creating notifications only in the seed method of our DbContext migrations configuration, and the notifications were shown to all users. Not too helpful for dialing in on specific details or messages for specific users.

Today we’re going to get that “everyone’s data” out of the mix, update our notifications so that they belong to a single user and then create a temporary way for us to add new, user-specific notifications to the site.

Clearing our DB and Seed Method

Let’s get those records (from our seed method) out of the database. Locate your DB in SQL Server Management Studio and delete the rows. Something this simple is adequate:

delete from notifications

Now, the only thing is that effort is all in vain if we don’t clean out our seed method. Each time the database configuration class is used to perform or check for migrations it will re-insert those rows.

Locate the Configuration class, which should be in \Migrations directory in the root of your solution. Comment out or delete all the code we added to upsert the notifications.

Extend our Notifications Class

Let’s next add a couple of properties to our Notification class, located in \Models.

public string UserId { get; set; }
public bool IsDismissed { get; set; }

Remember that when we modify a class that takes part in our Entity Framework works that we’ll also need to generate a migration and update the database to match our model. Type these commands into the Package Manager Console, updating the namespace appropriately:

Add-Migration PersonalNotifications -ConfigurationTypeName SimpleSite.Migrations.Configuration
Update-Database -ConfigurationTypeName SimpleSite.Migrations.Identity.Configuration

The first command creates a migration for us, and the second updates the database accordingly.

Updating our ActionFilter

So, if notifications belong only to users, we never want our filter to execute if the current request is for an unauthenticated user. We’ll also want to make sure that we only capture notifications for the user that is logged in, and then, only the notifications that have not yet been seen. 

Here’s the updated code for the NotificationFilter (located in \Filters):

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    if (!filterContext.HttpContext.User.Identity.IsAuthenticated) return;

    var userId = filterContext.HttpContext.User.Identity.GetUserId();

    var context = new SiteDataContext();
    var notifications = context.Notifications
        .Where(n => n.UserId == userId)
        .Where(n => !n.IsDismissed)
        .GroupBy(n => n.NotificationType)
        .Select(g => new NotificationViewModel
        {
            Count = g.Count(),
            NotificationType = g.Key.ToString(),
            BadgeClass = NotificationType.Email == g.Key
                ? "success"
                : "info"
        });

    filterContext.Controller.ViewBag.Notifications = notifications;
}

Scaffolding a Temporary Controller and View

The MVC Framework has some great scaffolding capabilities, as we’ve explored in minor detail so far. Today we’re going to use this feature to create our whole controller and the related views for all our CRUD operations. Right-click as you normally do on the controllers folder, and click Add->Controller.

This time ‘round, use the Scaffold for the “MVC5 Controller with views, using Entity Framework”. Fill out the options as follows:

image

You are selecting the Notification model, checking off “Generate views” and “Use a layout page”. The controller name should automatically be set to NotificationsController for you. Click Add to finish it out.

image

One more thing: you’re going to want an easy way to get your UserId – it’s what we’re using to match the notifications – so add the following to your Create view in Views\Notifications\Create.cshtml:

@if (User.Identity.IsAuthenticated)
{
    <p>Your User ID is <b>@User.Identity.GetUserId()</b></p>
}

That will output your UserId (which is a Guid) so that you can create notifications for yourself.

Now when you run the site, sign in and navigate to the /Notifications path. This will show you an empty list, but you’ll have a link to create some new records. Add some to the site, using your UserId, and watch the navbar light up.

image

Next Steps

Now, I did say this is a temporary solution. By no means would you actually have a situation where you’d have users enter their own notifications, you’d more likely have events happen in the system that require some notification to be required – perhaps the completion of a job or a required update to some business process.  That’s why we just used the scaffolding today…the NotificationsController and view are something that you’ll likely eventually just delete…and that’s okay! One of the nice things about scaffolding is that you’re not married to it. So delete it when you’re done with it.

In the real world, however, you would likely want users to be able to see and manage notifications in some way. Tomorrow we’ll look at getting the first of those bits in place.