How to Save and Retrieve Session Data Using jQuery in an ASP.NET MVC Application

in #utopian-io8 years ago (edited)

What Will I Learn?

How to Work with Session data Using jQuery in an ASP.NET MVC Application

Requirements

  • jQuery JavaScript Library
  • ASP.Net MVC

Difficulty

Intermediate

Tutorial Contents

  1. Use case and goals
  2. Scaffolding for read/write with the Session
  3. Controllers and views for the UX
  4. Adapting to changes in requirements

How to Save and Retrieve Session Data Using jQuery in an ASP.NET MVC Application

With ASP.NET MVC it is easy to write up controller actions that work with JSON data and add some jQuery code in views to interface with those actions via Ajax. We can use that approach to capture and post user settings to some server side code and return the user's settings back without having to reload a page or redirect the user.

We will be creating a prototype application that will have a list page that displays a list of data records consisting of a name and a description. It will allow the user to set the list view to either rows or a grid of blocks. There will also be an option to show or hide the description. When the user sets their desired options we will store them in the Session state. Finally, we want to update the view without reloading the page.

Scaffolding for read/write with the Session

We begin by creating a class for our settings structure that we will use with model binding to store and work with our JSON data from our view. This allows us to use one key/value pair in our Session state instead of storing a key/value for each of our settings. It also provides us with additional control over our data content, thus we can craft some code to protect ourselves from potential cross site scripting vulnerabilities since we will be receiving post data into our action method. Our class will be named ListSettings and will contain two properties named ListView and ShowDescription. The ListView will have a backing field so we can have some logic in the get code block to validate that the string is a list view type that we allow and set it to our default one if it is not.

ListSettings.cs

namespace SessionDataInMvc3.Models
{
    public class ListSettings
    {
        private string listView;
        public string ListView
        {
            get { return listView; }
            set
            {
                switch(value.ToLower())
                {
                    case "grid":
                        listView = "Grid";
                        break;
                    default:
                        listView = "Row";
                        break;
                }
            }
        }
        public bool ShowDescription { get; set; }
    }
}

With our settings class defined we can build out our SettingsController class. This class will contain an action method named Save that will take in a ListSetting object, save it in the Session state, and return the object (we will learn why when we get into the jQuery code later in the article). We will also create an action method named Load that will instantiate a default ListSettings object, get the data in the session (if any) and return it as a JSON object.

SettingsController.cs

using System.Web.Mvc;
using SessionDataInMvc3.Models;
namespace SessionDataInMvc3.Controllers
{
    public class SettingsController : Controller
    {
        public JsonResult Save(ListSettings item)
        {
            Session["ListSettings"] = item;
            return Json(item);
        }
        public JsonResult Load()
        {
            var item = new ListSettings { ListView = "Rows", ShowDescription = true };
            if(Session["ListSettings"] != null)
                item = Session["ListSettings"] as ListSettings;
            return Json(item);
        }
    }
}

Controllers and views for the UX

Next we need to set up our user experience layer. We will add a home page and a list page so we can navigate between the two and test our session state. The HomeController will contain the action methods Index and List. Since we want to handle all of our UX updates via Ajax we do not need to add any logic in here to load up the settings for our display.

HomeController.cs

using System.Web.Mvc;
namespace SessionDataInMvc3.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
        public ActionResult List()
        {
            return View();
        }
    }
}

The Index.cshtml view file contains a heading tag to let us know where we are at.

Index.cshtml

@{ ViewBag.Title = "Home"; }
<h2>Home</h2>

The List.cshtml view file will contain the buttons to set the list view type, a button to toggle the description option and an unordered list of our sample data. We will decorate the unordered list with a CSS class named Hidden that will hide the display of the list until we can determine the user settings via some jQuery. The buttons and the unordered lists will get id attributes so we can target them within our jQuery. The data in the list items will also get CSS classes for some styling and to handle the toggle of the description. The html markup portion of the List.cshtml file looks like:

@{ ViewBag.Title = "List"; }
<h2>List</h2>
<div>
    Set the display type: 
    <span id="SetRowView" class="Button">Rows</span> 
    <span id="SetGridView" class="Button">Grid</span>
</div>
<div>
    <span id="ToggleDescription" class="Button">Toggle Description</span>
</div>
<ul id="ItemListing" class="Hidden">
    <li>
        <div class="Name">File 1</div>
        <div class="Description">The quarterly sales earnings for 2018 Q1</div>
    </li>
    <li>
        <div class="Name">File 2</div>
        <div class="Description">Our marketing strategy for the rest of 2018</div>
    </li>
    <li>
        <div class="Name">File 3</div>
        <div class="Description">The top secret project</div>
    </li>
    <li>
        <div class="Name">File 4</div>
        <div class="Description">Feedback from our customers</div>
    </li>
    <li>
        <div class="Name">File 5</div>
        <div class="Description">Insight from our stakeholders</div>
    </li>
</ul>

Before we craft the jQuery code let's build out the CSS that we need. We can add the following CSS to the existing Site.css file that is in the default MVC project.

.Button { cursor:pointer; color:Blue; }
.Button:hover { text-decoration:underline; }
.Hidden { display:none; }
#ItemListing { width:600px; list-style:none; margin:0; padding:0; }
#ItemListing li { border-top:1px solid #999; }
#ItemListing .Name { font-weight:bold; }
#ItemListing .Description { color:#666; }
#ItemListing.Rows li { float:none; width:600px; padding:4px 0 4px 0; }
#ItemListing.Grid li { float:left; width:180px; height:120px; display:block; 
                       margin:0 6px 6px 0; border:1px solid #999; padding:4px; }

The Button class styles our span tags to make them look interactive. The Hidden class sets the display to none. We use this class for the unordered list as well as the description elements. Using the ItemListing id, we set a fixed width on the unordered list so our grid layout will be a set number of blocks wide (in this case it will be 3). We also normalize the styling on the unordered list by setting the list type to none and the margin/padding to zero. This will remove the disc glyph and left indention so we can have our list looking like rows or columns of blocks. The Name and Description classes add some styling to our text. Finally, the Rows and Grid classes set the style of the li elements to control the way the list items are viewed. The Rows sets the width of each li to the same width as the unordered list. The Grid sets the width on each one smaller, floats them to the left and adds some margin to the right and bottom of each so they don't run into each other. The width of the unordered list will force the grid blocks to wrap, thus achieving the 3 column look.

The jQuery code in the List.cshtml file will handle the user interaction with the settings buttons and updates to the list view. We begin by defining a settings variable that will be global so our functions can access the data in it. Then we will add the jQuery on document load block to wire up our button clicks and add a call to a function called LoadSettings. The final part of script will be our functions to save and load the settings, a callback function to handle the settings loaded event, and a function to set the list view.

<script type="text/javascript">
    var settings;
    $(function () {
        $('#SetRowView').click(function () {
            settings.ListView = 'Rows';
            SaveSettings();
        });
        $('#SetGridView').click(function () {
            settings.ListView = 'Grid';
            SaveSettings();
        });
        $('#ToggleDescription').click(function () {
            settings.ShowDescription = !settings.ShowDescription;
            SaveSettings();
        });
        LoadSettings();
    });
    function SaveSettings() {
        $.post('/Settings/Save', settings, SetListView);
    }
    function LoadSettings() {
        $.post('/Settings/Load', OnSettingsLoaded);
    }
    function OnSettingsLoaded(data) {
        settings = data;
        SetListView(settings);
    }
    function SetListView(settings){
        $('#ItemListing')
            .removeClass('Hidden')
            .removeClass('Rows')
            .removeClass('Grid')
            .addClass(settings.ListView);
        if (settings.ShowDescription) {
            $('#ItemListing .Description').removeClass('Hidden');
        }
        else {
            $('#ItemListing .Description').addClass('Hidden');
        }
    }
</script>

The code for setting the list view type handles setting the ListView property of the settings JavaScript object to the selected list view and calls the SaveSettings method. The SaveSettings method posts the settings object to our Save action method in the SettingsController and upon success receives the returned JSON object from that action method and calls the SetListView function. The code to toggle the description visibility sets the ShowDescription property to the opposite of what it currently is (in effect, toggling its state) and then makes the call to SaveSettings.

The SetListView function makes the view changes to the ItemListing by removing the Hidden class and each of the possible list view classes and then adds the selected list view class (we are using the name of the list type as the name of the CSS class). It also checks the ShowDescription property and either removes or adds the Hidden class on the elements with the Description class.

The LoadSettings function makes a post call to the Load action method in the SettingsController and upon success calls the OnSettingsLoaded function. The OnSettingsLoaded function sets the returned JSON object to our settings global variable and then calls the SetListView function. The reason we need to set the global settings object here is so the event handler for the toggle description button has access to the current settings for the ShowDescription property.

That's all that is needed for our view. The end result for our List.cshtml file:

List.cshtml

@{ ViewBag.Title = "List"; }
<h2>List</h2>
<div>
    Set the display type: 
    <span id="SetRowView" class="Button">Rows</span> 
    <span id="SetGridView" class="Button">Grid</span>
</div>
<div>
    <span id="ToggleDescription" class="Button">Toggle Description</span>
</div>
<ul id="ItemListing" class="Hidden">
    <li>
        <div class="Name">File 1</div>
        <div class="Description">The quarterly sales earnings for 2018 Q1</div>
    </li>
    <li>
        <div class="Name">File 2</div>
        <div class="Description">Our marketing strategy for the rest of 2018</div>
    </li>
    <li>
        <div class="Name">File 3</div>
        <div class="Description">The top secret project</div>
    </li>
    <li>
        <div class="Name">File 4</div>
        <div class="Description">Feedback from our customers</div>
    </li>
    <li>
        <div class="Name">File 5</div>
        <div class="Description">Insight from our stakeholders</div>
    </li>
</ul>
<script type="text/javascript">
    var settings;
    $(function () {
        $('#SetRowView').click(function () {
            settings.ListView = 'Rows';
            SaveSettings();
        });
        $('#SetGridView').click(function () {
            settings.ListView = 'Grid';
            SaveSettings();
        });
        $('#ToggleDescription').click(function () {
            settings.ShowDescription = !settings.ShowDescription;
            SaveSettings();
        });
        LoadSettings();
    });
    function SaveSettings() {
        $.post('/Settings/Save', settings, SetListView);
    }
    function LoadSettings() {
        $.post('/Settings/Load', OnSettingsLoaded);
    }
    function OnSettingsLoaded(data) {
        settings = data;
        SetListView(settings);
    }
    function SetListView(settings){
        $('#ItemListing')
            .removeClass('Hidden')
            .removeClass('Rows')
            .removeClass('Grid')
            .addClass(settings.ListView);
        if (settings.ShowDescription) {
            $('#ItemListing .Description').removeClass('Hidden');
        }
        else {
            $('#ItemListing .Description').addClass('Hidden');
        }
    }
</script>

The last piece we will add is some navigation in our Layout.cshtml file so we have a way to switch back and forth between the home and list pages.

_Layout.cshtml

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content(" ~ content site.css")" rel="stylesheet" type="text/css"/>
    <script src="@Url.Content(" ~ scripts jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content(" ~ scripts modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
    <nav>
        <ul>
            <li>@Html.ActionLink("Home", "Index", "Home")</li>
            <li>@Html.ActionLink("List", "List", "Home")</li>
        </ul>
    </nav>
    @RenderBody()
</body>
</html>

Now we can run our application, change up our view settings, and navigate back and forth between the home and list pages to see that our settings are remaining in their proper state.

Adapting to changes in requirements

By setting up our UX layer to save and load our settings via JSON we have decoupled the logic of doing the actual state storage from our display. This allows us to make changes to the way we store those settings without having to change our view. For example, we could change our storage logic to use cookies instead by updating our SettingsController class.

SettingsController.cs

using System;
using System.Web;
using System.Web.Mvc;
using SessionDataInMvc3.Models;
namespace SessionDataInMvc3.Controllers
{
    public class SettingsController : Controller
    {
        public JsonResult Save(ListSettings item)
        {
            var cookieListView = new HttpCookie("ListView", item.ListView);
            cookieListView.Expires = DateTime.Now.AddDays(1);
            Response.Cookies.Add(cookieListView);
            var cookieShowDescription = 
                new HttpCookie("ShowDescription", item.ShowDescription.ToString());
            cookieShowDescription.Expires = DateTime.Now.AddDays(1);
            Response.Cookies.Add(cookieShowDescription);
            return Json(item);
        }
        public JsonResult Load()
        {
            var item = new ListSettings { ListView = "Rows", ShowDescription = true };
            if (Request.Cookies["ListView"] != null)
            {
                item.ListView = Request.Cookies["ListView"].Value;
            }
            if (Request.Cookies["ShowDescription"] != null)
            {
                item.ShowDescription = 
                    Convert.ToBoolean(Request.Cookies["ShowDescription"].Value);
            }
            return Json(item);
        }
    }
}

With this approach we are able to store and retrieve data on the fly from our application interface and can respond to the user's request for change with an instant update rather than a page reload. Enhancements to the user experience accomplished with relative ease using MVC and jQuery.



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Hey @cheretta I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x

@cheretta good use of jQuery in ASP.NET MVC for the beginner. I upvote and follow you .plzz follow and upvote. I'll be thankful to you.
https://steemit.com/aspdotnet/@umair72023/introduction-to-asp-net-mvc-architecture

Coin Marketplace

STEEM 0.04
TRX 0.33
JST 0.096
BTC 62055.71
ETH 1734.96
USDT 1.00
SBD 0.39