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
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
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.
Search
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