Asp.net core mvc views


Start point

In the previous article we got to know about asp.net core routing. Now, I want to tell about views.

Passing data to view

We already have some views in our project, for example Calculator/Result.cshtml view :


@{
    ViewData["Title"] = "Sum";
}

<h1 class="title is-1">@ViewData["action"]</h1>
<h2 class="subtitle is-2">@ViewData["firstNumber"] @ViewData["mark"] @ViewData["secondNumber"]  = @ViewData["result"]</h2>


As you can see, we passed data to view using ViewData dictionary collection. However, there is most robust approach using strongly typed model, which is called ‘ViewModel’. First of all, let’s create new folder in project root and name it Models. Secondly create folder in Models with name Movies. Last one should contain all models reffering to our new MoviesController. Finnaly, we have to create new empty controller with name MoviesController.

Now, we can add new class to Models/Movies folder with name Movie.cs

project structure with models

public class Movie
{
    /// <summary>
    /// Movie id
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Movie name
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Author
    /// </summary>
    public string Author { get; set; }

    /// <summary>
    /// Movie genere
    /// </summary>
    public string Genre { get; set; }

    /// <summary>
    /// Creation date
    /// </summary>
    public DateTime CreatedAt { get; set; }
}

Change MoviesController to

public class MoviesController : Controller
{

    public IActionResult Index()
    {

        var movies = new List<Movie>
        {
            new Movie { Author = "Todd Phillips", Genre = "Crime , Drama , Thriller", Name = "Joker",
                Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/joker_dabf394a-d4f2-4b68-90e2-011ed6b54012_poster.png?d=270x360&q=20",
                CreatedAt = new DateTime(2019,10,3)
            },
            new Movie { Author = "David Leitch", Genre = "Action , Adventure", Name = "Fast & Furious Presents: Hobbs & Shaw",
                Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/fast-furious-presents-hobbs-shaw_14d1ab4f-c90c-46d1-9e04-f7d69f285ebd_poster.png?d=270x360&q=20",
                CreatedAt = new DateTime(2019,8,2)
            },
            new Movie { Author = "Jon Favreau", Genre = "Adventure , Animation , Drama , Family , Musical", Name = "The Lion King",
                Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/the-lion-king_3904aadc-3a07-4836-892f-763b2dfdeea3_poster.png?d=270x360&q=20",
                CreatedAt = new DateTime(2019,7,19)
            },
            new Movie { Author = "Joachim Rønning", Genre = "Adventure , Family , Fantasy", Name = "Maleficent: Mistress of Evil",
                Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/maleficent-mistress-of-evil_c8507e61-a6b3-404d-b8c5-df6f74bc62be_poster.png?d=270x360&q=20",
                CreatedAt = new DateTime(2019,10,18)
            },
        };


        return View(movies);
    }
}

Now, let’s create Index.cshtml view with strongly typed model

@model IEnumerable<iitu.web.example.Models.Movies.Movie>

@{
    ViewData["Title"] = "Movies";
}

<h1>Index</h1>


<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Id)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Poster)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Author)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.CreatedAt)
            </th>
           
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Id)
                </td>
                <td>
                    <img src="@item.Poster" asp-append-version="true">
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Name)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Author)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Genre)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.CreatedAt)
                </td>               
            </tr>
        }
    </tbody>
</table>

As you can see model now is strongly typed collection @model IEnumerable<iitu.web.example.Models.Movies.Movie>. So we can select one by one and show all list in a table.

Run app , you should see something similar

movies

Now, We need add ability of creating new movie and adding it to a list. So, let’s add new movie form and action which should create and add movie to list. First, add post action to controller

public class MoviesController : Controller
{
    static List<Movie> movies = new List<Movie>
    {
        new Movie { Author = "Todd Phillips", Genre = "Crime , Drama , Thriller", Name = "Joker",
            Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/joker_dabf394a-d4f2-4b68-90e2-011ed6b54012_poster.png?d=270x360&q=20",
            CreatedAt = new DateTime(2019,10,3)
        },
        new Movie { Author = "David Leitch", Genre = "Action , Adventure", Name = "Fast & Furious Presents: Hobbs & Shaw",
            Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/fast-furious-presents-hobbs-shaw_14d1ab4f-c90c-46d1-9e04-f7d69f285ebd_poster.png?d=270x360&q=20",
            CreatedAt = new DateTime(2019,8,2)
        },
        new Movie { Author = "Jon Favreau", Genre = "Adventure , Animation , Drama , Family , Musical", Name = "The Lion King",
            Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/the-lion-king_3904aadc-3a07-4836-892f-763b2dfdeea3_poster.png?d=270x360&q=20",
            CreatedAt = new DateTime(2019,7,19)
        },
        new Movie { Author = "Joachim Rønning", Genre = "Adventure , Family , Fantasy", Name = "Maleficent: Mistress of Evil",
            Poster = "https://dz7u9q3vpd4eo.cloudfront.net/admin-uploads/posters/mxt_movies_poster/maleficent-mistress-of-evil_c8507e61-a6b3-404d-b8c5-df6f74bc62be_poster.png?d=270x360&q=20",
            CreatedAt = new DateTime(2019,10,18)
        },
    };


    public IActionResult Index()
    {
        
        return View(movies);
    }

    [HttpGet]
    public IActionResult Add()
    {            
        return View();
    }

    [HttpPost]
    public IActionResult Add(Movie movie)
    {
        movies.Add(movie);

        return View("Index",movies);
    }
}

As you can noticed, movies variable became static field of contoller, the reason is the collection should be only one for all instances of cotrollers and should not be cleared after instance of controller garbage collected. Note it is for sample purpose only, in another case don’t use static collections, it can cause memory leak
Next, we have to add view to our new action Add with following content

@model iitu.web.example.Models.Movies.Movie

@{
    ViewData["Title"] = "Add";
}

<h1>Add</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Add">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Poster" class="control-label"></label>
                <input asp-for="Poster" class="form-control" />
                <span asp-validation-for="Poster" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Author" class="control-label"></label>
                <input asp-for="Author" class="form-control" />
                <span asp-validation-for="Author" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="CreatedAt" class="control-label"></label>
                <input asp-for="CreatedAt" class="form-control" />
                <span asp-validation-for="CreatedAt" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

Go to Movies/Add, you can see that you have empty inputs. The problem with <label asp-for="Id" class="control-label"></label>, because label is asp.net core mvc tag, however we should add tag support to our view. We can create in Views folder file with name _ViewImports.cshtml and content

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Aslo, we could change our Movie model to next form

public class Movie
{
    /// <summary>
    /// Movie id
    /// </summary>
    [Display(Name = "Id")]
    public int Id { get; set; }

    /// <summary>
    /// Movie poster 
    /// </summary>
    [Display(Name = "Url to poster")]
    public string Poster { get; set; }

    /// <summary>
    /// Movie name
    /// </summary>
    [Display(Name = "Movie name")]
    public string Name { get; set; }

    /// <summary>
    /// Author
    /// </summary>
    [Display(Name = "Author name")]
    public string Author { get; set; }

    /// <summary>
    /// Movie genere
    /// </summary>
    [Display(Name = "List of genres (by comma)")]
    public string Genre { get; set; }

    /// <summary>
    /// Creation date
    /// </summary>
    [Display(Name = "Release date")]
    public DateTime CreatedAt { get; set; }
}

If you run app, you will see that we have rendered labels, but at the same time, page looks terrible. Let’s add some beauty styles from bulma framework.

Bring to the following view Add.cshtml

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model iitu.web.example.Models.Movies.Movie

@{
    ViewData["Title"] = "Add";
}

<h1 class="title is-1">Add</h1>

<h4 class="subtitle is-4">Movie</h4>
<hr />
<div class="column is-two-fifths">
    <div class="box">
        <form asp-action="Add">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>

            <div class="field">
                <label asp-for="Id" class="control-label"></label>
                <div class="control">
                    <input asp-for="Id" class="input" />
                    <span asp-validation-for="Id" class="has-text-danger"></span>
                </div>
            </div>
            <div class="field">
                <label asp-for="Poster" class="control-label"></label>
                <div class="control">
                    <input asp-for="Poster" class="input" />
                    <span asp-validation-for="Poster" class="has-text-danger"></span>
                </div>
            </div>
            <div class="field">
                <label asp-for="Name" class="control-label"></label>
                <div class="control">
                    <input asp-for="Name" class="input" />
                    <span asp-validation-for="Name" class="has-text-danger"></span>
                </div>
            </div>
            <div class="field">
                <label asp-for="Author" class="control-label"></label>
                <div class="control">
                    <input asp-for="Author" class="input" />
                    <span asp-validation-for="Author" class="has-text-danger"></span>
                </div>
            </div>
            <div class="field">
                <label asp-for="Genre" class="control-label"></label>
                <div class="control">
                    <input asp-for="Genre" class="input" />
                    <span asp-validation-for="Genre" class="has-text-danger"></span>
                </div>
            </div>
            <div class="field">
                <label asp-for="CreatedAt" class="control-label"></label>
                <div class="control">
                    <input asp-for="CreatedAt" class="input" />
                    <span asp-validation-for="CreatedAt" class="has-text-danger"></span>
                </div>
            </div>
            <div class="field is-grouped">
                <div class="control">
                    <button class="button is-link">Submit</button>
                </div>             
            </div>

            
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

Goto Movies/Add and add a movie.

Now, I wish to add searching capability. First, I need Search action in my controller

public IActionResult Search(string text)
{
    text = text.ToLower();
    var searchedMovies = movies.Where(movie => movie.Name.ToLower().Contains(text)
                                    || movie.Genre.ToLower().Contains(text)
                                    || movie.Author.ToLower().Contains(text))
                                .ToList();
    return View("Index", searchedMovies);
}

As you can I will return the same Index view for this action.

Partial view

However, I think different pages can contain search ability, so I decide to create partial view for searching component. Create partial view _Search in Shared folder with content.


<br/>
<form asp-action="Search" asp-controller="Movies" method="get">
    

    <div class="field has-addons">
        <div class="control">
            <input class="input" name="text" type="text" placeholder="Find a movie">
        </div>
        <div class="control">
            <button class="button is-info">
                Search
            </button>
        </div>
    </div>


</form>
<br />

Finnaly, we should add partial on top of Index view.

@model IEnumerable<iitu.web.example.Models.Movies.Movie>

@{
    ViewData["Title"] = "Movies";
}

<partial name="_Search" />


<h1>Index</h1>

    ***

Conclusion

We learned how to use strongly typed model within views. How to pass data from action to view. And how get data from client. Also, we got to know how to work with partial views.

Source code

You can download source code from github

Buy Me A Coffee

Related Posts

Avoid reflections of mappers and let Mapster generate mappers for you

Mapster generating tool for onion application

Predict Bitcoin price with ML.net

Live time series coin price predictor with machine learning

Throw exceptions from backend to frontend with blazor

One of advantages of using same code language on both frontend and backend

How to avoid violating of SOLID principles while extending service behaviours

Step by step extending service behaviour with decorator pattern respecting SOLID

Blazor render optimization

Best practices

.Net 6 brings application state to blazor webassembly server prerender

It kills strange blinking on screen

Must have libraries for blazor

List of best nuget packages

Blazor virtualize component

Lazy loading of big lists. Rendering optimization.

Blazor grpc - comunication optimization

Smaller and faster requests to your backend from blazor wasm

Free database for your blazor app

Don't pay for the cloud