Click here to Skip to main content
15,889,281 members
Articles / Web Development / HTML

How To Make a Web Form Read-only the Easy Way

Rate me:
Please Sign up or sign in to vote.
4.92/5 (4 votes)
2 Feb 2017CPOL4 min read 22.5K   5   4
How to make a web form read-only the easy way

Making a web form read-only. Sounds simple, right? Well, there are a couple of ways we could approach it. We could use JavaScript and update the disabled attribute on each text box. We could create 2 big chunks of HTML within the page. One of the forms should be read-only, the other if not. Feels a bit messy though.

I find templates work very well in these situations. MVC provides some great and easy conventions for working with templates. In this article, we’ll look at display and editor templates and how they work.

The idea behind display and editor templates is a simple one. If you’re displaying editable data, use an editor template. If you’re displaying read-only data, use a display template. In terms of how they work, the template acts as a partial view. MVC has built-in conventions, which it uses when looking for those templates. We’ll see them in action in a moment.

Step 1: Create a ViewModel and Bind It to the View

First, we need a ViewModel. Create a new ASP.NET project in Visual Studio. Add a ReadOnlyViewModel to the Models folder. I always add the ViewModel suffix to my ViewModel class names. This helps keep clear separation from any domain models I might be using.

C#
public class ReadOnlyViewModel
{
    public bool IsReadonly { get; set; }

    public string PieceOfData { get; set; } 

    public string AnotherPieceOfData { get; set; } 
}

Now, bind the ReadOnlyViewModel to the Home/Index view. I've changed the default namespace for this project to Levelnis.Learning.ReadOnlyViews. Yours might be different.

C#
@model Levelnis.Learning.ReadOnlyViews.Models.ReadOnlyViewModel

We need to pass an instance of the model to the view. We'll do that in the controller. Here's what that looks like:

C#
public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new ReadOnlyViewModel();
        return View(model);
    }
}

Step 2: Create Display and Editor Templates

We can now create our templates. Create a DisplayTemplates and an EditorTemplates folder within Views/Shared. In each folder, add a partial view called ReadOnlyViewModel.cshtml.

Visual Studio add template popup

Once you’re done, your project structure should look like this:

Visual Studio project structure

When we come to using the templates, MVC will use those built-in conventions to look for them. For the display template, it will look for Views/Home/DisplayTemplates, followed by Views/Shared/DisplayTemplates. If it finds a template bound to the ReadOnlyViewModel type, it will use that. If it doesn’t, it will build up a template from the properties within the ViewModel.

View the original article.

Templates are a lot more flexible than partial views. Visual Studio has in-built templates for the standard C# data types, for instance. Take our display template. There are built-in templates for bool and string. What would happen if we'd misspelt our template name? MVC would display a read-only checkbox and a label for our IsReadOnly property. It would show labels containing the property name for our PieceOfData and AnotherPieceOfData properties. Partial views don’t do this. If we give a partial the wrong name, MVC throws an error because it can’t find it. Something to bear in mind if you’re using templates.

We need read-only form controls in our display template. Add this code to Views/Shared/DisplayTemplates/ReadOnlyViewModel.cshtml:

HTML
@model Levelnis.Learning.ReadOnlyViews.Models.ReadOnlyViewModel

<div class="row">
    <div class="col-xs-8">
        <p>This is a read-only view!</p>
        <div class="form-group">
            @Html.LabelFor(m => m.PieceOfData, "Piece of Data")
            @Html.TextBoxFor(m => m.PieceOfData, 
            new { @class = "form-control", disabled = "disabled" })
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.AnotherPieceOfData, "Another Piece of Data")
            @Html.TextBoxFor(m => m.AnotherPieceOfData, 
            new { @class = "form-control", disabled = "disabled" })
        </div>
    </div>
</div>

The editor template is pretty similar. The form controls are editable in this case. Add this code to Views/Shared/EditorTemplates/ReadOnlyViewModel.cshtml:

HTML
@model Levelnis.Learning.ReadOnlyViews.Models.ReadOnlyViewModel

<div class="row">
    <div class="col-xs-8">
        <p>This is an editable view!</p>
        @using (Html.BeginForm())
        {
            <div class="form-group">
                @Html.LabelFor(m => m.PieceOfData, "Piece of Data")
                @Html.TextBoxFor(m => m.PieceOfData, 
                new { @class = "form-control" })
            </div>
            <div class="form-group">
                @Html.LabelFor(m => m.AnotherPieceOfData, "Another Piece of Data")
                @Html.TextBoxFor(m => m.AnotherPieceOfData, 
                new { @class = "form-control" })
            </div>
            <div class="form-group">
                <p class="pull-right">
                <button type="submit" 
                class="btn btn-primary">Save</button></p>
            </div>
        }
    </div>
</div>

Step 3: Display the Templates in the View

The final step is to call the extension methods to display the correct template. Stick this into the Index view:

C#
@(Model.IsReadonly ? Html.DisplayForModel() : Html.EditorForModel())

All we're doing here is calling DisplayForModel or EditorForModel, based on whether the model is read-only. These are extension methods on the HtmlHelper. We could also use DisplayFor or EditorFor, which take in an expression. This allows us to use templates for individual properties in our model.

Next, we’ll add a Post method to the controller, which toggles the IsReadOnly flag in the ViewModel:

C#
[HttpPost]
public ActionResult Index(ReadOnlyViewModel model)
{
    model.IsReadonly = true;
    return View(model);
}

Now, when we run the application, we’ll see an editable form to begin with:

Editor template example

Let’s stick some random data in and hit Save. Now, we see the read-only display template:

Display template example

What's With that PRG (Post-Redirect-Get) Reference?

As the screenshots show, we're not using the recommended PRG pattern here. If we were, we would do things a little different. But how, exactly? Well, our Index controller action would display the Index view as before. The Post action would not return a ViewResult, as it does here. In a real application, we might well be saving this data to a data store somewhere. After saving, we would get the database id back from the save function. We would then redirect from the Post method to another method in our controller. Let's call it Read. Read would take the id as a parameter and pull the data back that we had just saved. The Post method simply redirects to the Read method after saving. Hence Post-Redirect-Get.

What's the advantage? Well, if we hit F5 and refresh our browser right now, we'll post that same data back to the server again. That's fine if it's updating existing data. It's annoying for the user, but it doesn't cause any issues. What if this was a method to add something to the database? Hit F5, add it again. And again. And again. Not ideal. PRG solves that problem, because here we'd be refreshing the Read View, rather than the Index View.

That’s it! We’ve seen one usage of templates here. Remember that you can use them wherever you want to control the markup on your pages. You can also nest them inside one another. Don’t make it too complicated though!

View the original article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead Levelnis Ltd
United Kingdom United Kingdom
Follow along my journey as I create a newsletter about launching websites. Just message me with "I'm in" and I'll add you

Comments and Discussions

 
-- No messages could be retrieved (timeout) --