big_mamba24
big_mamba24

Reputation: 33

Send view with sorted items and query parameters (Search Bar) in Asp .Net Core 3.1

I created a search bar like this

 <form method="post" asp-action="Search">
    <input type="text" name="search" placeholder="Enter here the Name " />
    <select name="type" id="type" class="form-control">
        <option value="Success">Inactive Reservation</option>
        <option value="Approved">Active Reservation</option>
        <option value="Pending">Pending Reservation</option>
    </select>
    <input type="submit" value="Search" />
</form>

and the method in controller:

public async Task<IActionResult> Search(string search,string type)      
{       
    var allRsv = from m in _db.Reservation
                 select m;

    var Rsv = allRsv
        .Where(x => x.ClientName.ToLower().Contains(search.ToLower()) && 
                    x.Status.ToLower().Contains(type.ToLower()));

    return View(Rsv);
}

What I want: to send in search page something like 'You searched for @search and type: @type.

return View has no option to do this ,neither return to action ..

Can I do it in a simple way ?

My single idea it is to send query string and then request query in search view

Upvotes: 1

Views: 1057

Answers (2)

David Liang
David Liang

Reputation: 21526

Using ViewData to pass data between controllers and views is fine as long as there are not many pieces of data in between. If you have lots of going, it will make everybody else harder to understand what's going on with ViewData because it's a weak-typed and you have no idea what it contains, what's available to get, etc. And then you have to go back to the controller and see what's being passed. What if there are multiple controllers returning this same view...yucky!

In addition, it's not a good practice to display what you have from your database directly from the controller to the view.

Hence you should use one of the alternatives of ViewData, that is ViewModel, which is strongly-typed!


Fake Entity Models

Since I don't have your database, for this demo, I am setting up two fake entity models that represent the data from your persistence storage.

namespace DL.NetCore.EmptySolution.Web.UI.Models.Reservation
{
    public class FakeReservationStatusEntity
    {
        public string StatusId { get; set; }
        public string Status { get; set; }
    }

    public class FakeReservationEntity
    {
        public int ReservationId { get; set; }
        public int ClientId { get; set; }
        public string ClientName { get; set; }
        public DateTime StartTimeUtc { get; set; }
        public FakeReservationStatusEntity ReservationStatus { get; set; }
        public int CreatedByUserId { get; set; }
    }
}

There is one-to-many relationship between reservation and reservation status I assumed. And please notice I purposely made it so that it has more properties than the view model!


Viewmodels

They're just POCOs (Plain Old CLR Objects) that serve as data containers to travel between controllers and views.

namespace DL.NetCore.EmptySolution.Web.UI.Models.Reservation
{
    public class ReservationFiltersViewModel
    {
        [Display(Name = "Client name")]
        public string ClientNameSearchQuery { get; set; }

        [Display(Name = "Reservation type")]
        public string ReservationTypeSearchQuery { get; set; }

        public IDictionary<string, string> AvailableReservationTypes { get; set; }
    }

    public class ReservationViewModel
    {
        public int ReservationId { get; set; }
        public string ReservationType { get; set; }
        public string ClientName { get; set; }
        public DateTime StartTime { get; set; }
    }

    public class ReservationListViewModel
    {
        public ReservationFiltersViewModel Filters { get; set; }

        public IEnumerable<ReservationViewModel> Reservations { get; set; }
    }
}

Controller

namespace DL.NetCore.EmptySolution.Web.UI.Controllers
{
    public class ReservationController : Controller
    {
        public IActionResult Index(string c, string t)
        {
            var vm = new ReservationListViewModel
            {
                Filters = new ReservationFiltersViewModel
                {
                    ClientNameSearchQuery = c,
                    ReservationTypeSearchQuery = t,
                    // You would normally get the list from your database
                    AvailableReservationTypes = GetFakeReservationStatusesFromDb()
                        .ToDictionary(x => x.StatusId, x => x.Status)
                },
                Reservations = Enumerable.Empty<ReservationViewModel>()
            };

            // You would normally get the list of reservations from your database
            var reservationsFromDb = GetFakeReservationsFromDb();

            // Filters
            if (!String.IsNullOrWhiteSpace(c))
            {
                reservationsFromDb = reservationsFromDb
                    .Where(x => x.ClientName.Contains(c, StringComparison.InvariantCultureIgnoreCase));
            }
            if (!String.IsNullOrWhiteSpace(t))
            {
                reservationsFromDb = reservationsFromDb
                     .Where(x => x.ReservationStatus.StatusId.Contains(t, StringComparison.InvariantCultureIgnoreCase));
            }

            // See you only want to explore what you want on the view
            vm.Reservations = reservationsFromDb
                .Select(x => new ReservationViewModel
                {
                    ReservationId = x.ReservationId,
                    ClientName = x.ClientName,
                    ReservationType = x.ReservationStatus.Status,
                    StartTime = x.StartTimeUtc.ToLocalTime()
                });

            return View(vm);
        }

        [HttpPost]
        public IActionResult Search(ReservationFiltersViewModel filters)
        {
            return RedirectToAction(nameof(Index), 
                new { c = filters.ClientNameSearchQuery, t = filters.ReservationTypeSearchQuery });
        }

        ...
    }
}

Index View

@model DL.NetCore.EmptySolution.Web.UI.Models.Reservation.ReservationListViewModel
@{
    ViewBag.Title = "Reservations";

    var selectList = new SelectList(Model.Filters.AvailableReservationTypes, "Key", "Value");
}

<h2>Reservations</h2>
<p class="text-muted">
    List of reservations you can manage
</p>
<div class="row">
    <div class="col-lg-4">
        <div class="card">
            <div class="card-body">
                <form method="post" asp-area="" asp-controller="reservation" asp-action="search">
                    <div class="form-group">
                        <label asp-for="Filters.ClientNameSearchQuery"></label>
                        <input asp-for="Filters.ClientNameSearchQuery" class="form-control" />
                    </div>
                    <div class="form-group">
                        <label asp-for="Filters.ReservationTypeSearchQuery"></label>
                        <select asp-for="Filters.ReservationTypeSearchQuery" class="form-control"
                                asp-items="selectList">
                            <option value="">- select -</option>
                        </select>
                    </div>
                    <button type="submit" class="btn btn-success">Search</button>
                </form>
            </div>
        </div>
    </div>
    <div class="col-lg-8">
            <!-- This could be better optimized, i.e., only display non-empty search -->
            <div class="alert alert-info">
                <i class="fas fa-info-circle"></i>
                You searched for <strong>@Model.Filters.ClientNameSearchQuery</strong> 
                and <strong>@Model.Filters.ReservationTypeSearchQuery</strong>
            </div>
        <div class="table-responsive">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th>#</th>
                        <th>Client name</th>
                        <th>Start from</th>
                        <th>Type</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach (var reservation in Model.Reservations)
                    {
                        <tr>
                            <td>@reservation.ReservationId</td>
                            <td>@reservation.ClientName</td>
                            <td>@reservation.StartTime.ToShortDateString()</td>
                            <td>@reservation.ReservationType</td>
                        </tr>
                    }
                </tbody>
            </table>
        </div>
    </div>
</div>

With ViewModel, there is no magic string flowing around like with ViewData. Everything is strongly typed. And the form only posts back the filter model that contains only what we need to the server.


Screenshots

No search

Searched by reservation type

Searched by client name

Searched by both


Source code

My demo project's source code is at https://github.com/davidliang2008/DL.NetCore.EmptySolution. The checkin specifically for this demo is at https://github.com/davidliang2008/DL.NetCore.EmptySolution/commit/32087b989de06e316cf747ad49da6ad4b24b61b8

Upvotes: 0

Fei Han
Fei Han

Reputation: 27815

What I want: to send in search page something like 'You searched for @search and type: @type.

You can try to pass data to search page via ViewData etc, like below.

In View Page

<form method="post" asp-action="Search">
    <input type="text" name="search" placeholder="Enter here the Name " />
    <select name="type" id="type" class="form-control">
        <option value="Success">Inactive Reservation</option>
        <option value="Approved">Active Reservation</option>
        <option value="Pending">Pending Reservation</option>
    </select>
    <input type="submit" value="Search" />
</form>

<h3>You searched for "@ViewData["search"]" and type: @ViewData["type"].</h3>

In Action Method

public async Task<IActionResult> Search(string search, string type)
{
    var allRsv = from m in _db.Reservation
                    select m;

    var Rsv = allRsv
        .Where(x => x.ClientName.ToLower().Contains(search.ToLower()) &&
                    x.Status.ToLower().Contains(type.ToLower()));

    ViewData["search"] = search;
    ViewData["type"] = type;

    return View(Rsv);
}

Test Result

enter image description here

Upvotes: 0

Related Questions