locked
Search form RRS feed

  • Question

  • User1029764681 posted

    I am new to ASP.net MVC and have a simple model of flights.  I am using EF to pull the data from the database.

    I do not want to pull back all the flights when going to the index page, so I needs to be limited to the last 10 days.

    Here is the controller...

            public ActionResult Index()
            {
                return View(db.Flights.ToList());
            }
    

    Here is the View

    @using (Html.BeginForm("Index", "Flights", FormMethod.Get))
    {
        <div class="card card-accent-info mb-3">
            <div class="card-header"><i class="fa fa-search mr-3"></i>Search</div>
            <div class="card-body">
                <div class="row">
                    <div class="col">
                        <span class="font-weight-bold">StartDate: </span>
                        @*<input id="txtStartDate" type="text" class="form-control form-control-sm" autocomplete="off" />*@
                        @Html.TextBox("txtStartDate","", new { @class = "form-control form-control-sm", autocomplete = "off", @Value = @DateTime.Now.AddDays(-10).ToShortDateString() })
                    </div>
                    <div class="col">
                        <span class="font-weight-bold">End Date: </span>
                        @*<input id="txtEndDate" type="text" class="form-control form-control-sm" autocomplete="off" />*@
                        @Html.TextBox("txtEndDate", "", new { @class = "form-control form-control-sm", autocomplete = "off", @Value = @DateTime.Now.ToShortDateString() })
                    </div>
                    <div class="col">
                        <span class="font-weight-bold">Tail: </span>
                        @*<input id="txtTail" type="text" MaxLength="6" class="form-control form-control-sm" />*@
                        @Html.TextBox("txtTail", "", new { @class = "form-control form-control-sm", autocomplete = "off", MaxLength=6 })
                    </div>
                    <div class="col">
                        <span class="font-weight-bold">DEP Station: </span>
                        @*<input id="txtDepartureStation" type="text" MaxLength="3" class="form-control form-control-sm" />*@
                        @Html.TextBox("txtDepartureStation", "", new { @class = "form-control form-control-sm", autocomplete = "off", MaxLength = 3 })
                    </div>
                    <div class="col">
                        <span class="font-weight-bold">ARR Station: </span>
                        @*<input id="txtArrivalStation" type="text" MaxLength="3" class="form-control form-control-sm" />*@
                        @Html.TextBox("txtArrivalStation", "", new { @class = "form-control form-control-sm", autocomplete = "off", MaxLength = 3 })
                    </div>
                </div>
                <div class="row">
                    <div class="col">
                        <input id="btnSearch" type="submit" value="Search" class="btn btn-primary mt-3" />
                    </div>
                </div>
            </div>
        </div>
    }

    The results are in a table below...

    The only 2 required fields will be the two dates...

    when the button is submitted the form would lose the information that was posted.... in webforms that would have been saved by the viewstate, how would you maintain their state?.  

    Whats the best way to change the index controller to use those search fields?

    Wednesday, November 28, 2018 7:12 PM

Answers

  • User1520731567 posted

    Hi fmrock164,

    I do not want to pull back all the flights when going to the index page, so I needs to be limited to the last 10 days.

    I suggest you could use Where cause to filter records in controller,like:

     public ActionResult Index()
    {
    DateTime dt10Today = Convert.ToDateTime(DateTime.Now.AddDays(-10).ToString("yyyy-MM-dd"));//or other format of date
    DateTime dtToday = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd"));//or other format of date
    return View(db.Charters.Where(_ => _.CharterDate <= dtToday && _.CharterDate >= dt10Today).ToList()); }

    when the button is submitted the form would lose the information that was posted.... in webforms that would have been saved by the viewstate, how would you maintain their state?.  

    Generally, we use model binding to pass data between controller and view,

    and you also need a httppost action,like:

    [httpPost]
    public ActionResult Index(Model md) {
    ... return View(); }

    And you should add name tag for each fields in model,because controller get their values from name.

    In addition, I suggest you could add a scaffolded item so that you could understand model binding clearly.

     You could follow the steps below:

    Right click on Controllers -> Choose Add -> Choose 'MVC5 Controller with views, using Entity Framework', then Add-> Choose the Model you want to add CRUD to in 'Model class', then Add -> The controller with CRUD function is created.

    Then the  Index,Details,Edit ..pages will be automatically generated

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, November 29, 2018 10:04 AM
  • User1520731567 posted

    Hi fmrock164,

    Is the search form a new model?  Just curious how i bind those 5 fields back to the httppost index method.

    In Model binder we  need to create the Model class with required data field and after that we  can pass this Model class’s object  into  controller’s action method for  data mapping.

    For example:

    Create a viewModel if you haven't already that contains a property for txtStartDate,txtEndDate...

    public class SModel
    {
         public int Id {get; set;}
         public DateTime txtStartDate{ get; set; }
         public DateTime txtEndDate{ get; set; }
    ... }

    Controller:

    public ActionResult Index()
    {
        return View();
    }    

    Strongly type your view:

    @model TestApplication1.xxx.xxxx.SModel

    Add a form,don't forget submit button:

    using (@Html.BeginForm("Index", "Home", FormMethod.Post))
    {   
        @Html.EditorFor(model => model.txtStartDate)

    @Html.EditorFor(model => model.txtEndDate)//It will generate an HTML with id and name both txtEndDate

    ...
    <input type="submit" value="Create" class="btn btn-default" />
    }

    Now we need to add the reference of this model in to Controller with help of using statement.

    Now we need to pass the  SModel's object  as parameter into Index  Action method, so that  our form’s data/http request data will be map, below is the code

    [HttpPost]
    public ActionResult Index(SModel model)
    {//you could add a breakpoint on this,and check the value of model
       //now model.txtStartDate,model.txtEndDate has value
    ... return View(); }

    More details and methods,you could refer to:

    https://www.codeproject.com/Articles/710776/Introduction-to-ASP-NET-MVC-Model-Binding-An-Absol

    https://www.c-sharpcorner.com/article/introduction-to-asp-net-mvc-model-binding/

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, November 30, 2018 2:11 AM
  • User1520731567 posted

    Hi fmrock164,

    So right now this view is using my Flight model from EF.  Your suggesting switching that to a defined ViewModel? So I would create a new folder called "ViewModels" and reference that instead

    No,I just gave an example.

    My focus is on the need to pass the model.

    Both Model and ViewModel can.

    You could pass Flight model directly.

    This view model would also have to have all the fields from the Original model as well? 

    According to your needs,this is not mandatory.

    But if you would like to get the value of txtStartDate and txtEndDate in httppost action,

    You need to have these two fields in model and put them in form(@using (Html.BeginForm)), and not forget named them.

    Can both the Model and VIewModel both be included?

    When the business logic is complex, you may have multiple models,

    You could create a Viewmodel to contains these models,and then pass the ViewModel between view and controller.

    For example:

    if you have two models:

    public class SModel
    {
         public int Id {get; set;}
         public DateTime txtStartDate{ get; set; }
         public DateTime txtEndDate{ get; set; }
         ...
    }
    
    public class SModel2
    {
         public int Id {get; set;}
         public DateTime txtStartDate2{ get; set; }
         public DateTime txtEndDate2{ get; set; }
         ...
    }

    Create a viewmodel:

    public class SViewModel
    {
         public int Id {get; set;}
         public SModel sModel {get; set;}
         public SModel2 sModel2 {get; set;}
    }

    Strongly type your view:

    @model TestApplication1.xxx.xxxx.SViewModel

    ...

    using (@Html.BeginForm("Index", "Home", FormMethod.Post))
    {   
        @Html.EditorFor(model => model.SModel.txtStartDate)
    
        @Html.EditorFor(model => model.SModel.txtEndDate)//in SModel
    
        ...

    @Html.EditorFor(model => model.SModel2.txtStartDate2)
    @Html.EditorFor(model => model.SModel2.txtEndDate2) //in SModel2
    <input type="submit" value="Create" class="btn btn-default" /> }

    Controller:

    [HttpPost]
    public ActionResult Index(SViewModel model)
    {//you could add a breakpoint on this,and check the value of model
       //now model.txtStartDate,model.txtEndDate has value
       ...
       return View();
    
    } 

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, December 3, 2018 6:03 AM

All replies

  • User1120430333 posted

    https://www.dotnetcurry.com/aspnet-mvc/1074/aspnet-mvc-pass-values-temp-data-session-request

    The above is how you keep data in state across requests.

    The example shows how I am using TempData to keep data is state  between requests or moving to one action method saving and redriving data I must keep in state.

    using System;
    using System.Linq;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using ProgMgmntCore2UserIdentity.Models;
    
    namespace ProgMgmntCore2UserIdentity.Controllers
    {
        public class TaskController : Controller
        {
            private readonly ITaskModel _taskModel;
            private readonly IProjectModel _projectModel;
            private readonly IModelHelper _modelHelper;
    
            public TaskController(ITaskModel taskModel, IProjectModel projectModel, IModelHelper modelHelper)
            {
                _taskModel = taskModel;
                _projectModel = projectModel;
                _modelHelper = modelHelper;
            }
    
            // GET: Task
            [Authorize]
            public ActionResult Index(int id)
            {
                var project = _projectModel.GetProjectById(id);
    
                TempData["ProjectId"] = id;
                TempData["Title"] = project.ProjectName;
    
                return View(_taskModel.GetTasksByProjectId(id));
            }
    
            [Authorize]
            public ActionResult Details(int id = 0)
            {
                return id == 0 ? null : View(_taskModel.Edit(id));
            }
    
            [Authorize]
            public ActionResult Create()
            {
                return View(_taskModel.Create());
            }
    
            [Authorize]
            [HttpPost]
            public ActionResult Create(TaskViewModels.TaskCreate task, string submit)
            {
                if (submit == "Cancel") return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
    
                ValidateddlStatuses();
                ValidateddlDurations();
                ValidateddlResources();
               
                task.Status = (Request.Form["ddlStatusTypes"]);
                task.TaskDuration = (Request.Form["ddlDurations"]);
                task.ResourceId = (Request.Form["ddlResources"]);
    
                if (!ModelState.IsValid) return View((TaskViewModels.TaskCreate)_taskModel.PopulateDropDownLists(task, "Create"));
    
                _taskModel.Create(task, (int)TempData["ProjectId"]);
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"] });
            }
    
            [Authorize]
            public ActionResult Edit(int id = 0)
            {
                return id == 0 ? null : View(_taskModel.Edit(id));
            }
    
            [Authorize]
            [HttpPost]
            public ActionResult Edit(TaskViewModels.TaskEdit task, string submit)
            {
                if (submit == "Cancel") return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
    
                if (ModelState.IsValid && _modelHelper.IsEndDateLessThanStartDate(task, "Task"))
                {
                    ModelState.AddModelError(String.Empty, "End Date cannot be less than Start Date.");
                }
    
                if (!ModelState.IsValid) return View( (TaskViewModels.TaskEdit)_taskModel.PopulateDropDownLists(task, "Edit"));
    
                var thetask = new TaskViewModels.TaskEdit();
    
                thetask = task;
    
                thetask.ProjectId = (int) TempData["ProjectId"];
    
                _taskModel.Edit(thetask);
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
            }
    
            public ActionResult Delete(int id = 0)
            {
                if (id > 0) _taskModel.Delete(id);
    
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"] });
            }
    
            public ActionResult Cancel()
            {
                var id = TempData["ProjectId"];
    
                return RedirectToAction("Index", "Home");
            }
    
            public ActionResult UploadFile(int id)
            {
                return RedirectToAction("Index", "Upload", new { id = id, type = "TM" });
            }
    
            private void ValidateddlStatuses()
            {
                if (Request.Form["ddlStatusTypes"] == string.Empty)
                   return;
    
                foreach (var key in ModelState.Keys.ToList().Where(key => ModelState.ContainsKey(key)))
                {
                    if (key != "Status") continue;
                    ModelState[key].Errors.Clear();
                    ModelState[key].ValidationState = ModelValidationState.Valid;
                }
            }
    
            private void ValidateddlDurations()
            {
                if (Request.Form["ddlDurations"] == string.Empty)
                   return;
    
                foreach (var key in ModelState.Keys.ToList().Where(key => ModelState.ContainsKey(key)))
                {
                    if (key != "TaskDuration") continue;
                    ModelState[key].Errors.Clear();
                    ModelState[key].ValidationState = ModelValidationState.Valid;
                }
            }
    
            private void ValidateddlResources()
            {
                if (Request.Form["ddlResources"] == string.Empty)
                   return;
                
                foreach (var key in ModelState.Keys.ToList().Where(key => ModelState.ContainsKey(key)))
                {
                    if (key != "ResourceId") continue;
                    ModelState[key].Errors.Clear();
                    ModelState[key].ValidationState = ModelValidationState.Valid;
                }
            }
        }
    }

    Thursday, November 29, 2018 12:14 AM
  • User-474980206 posted
    Webforms stored the data in a hidden field. You can do the same. Serialise the data, encrypt and then base64. Just reverse on postback.
    Thursday, November 29, 2018 12:58 AM
  • User1520731567 posted

    Hi fmrock164,

    I do not want to pull back all the flights when going to the index page, so I needs to be limited to the last 10 days.

    I suggest you could use Where cause to filter records in controller,like:

     public ActionResult Index()
    {
    DateTime dt10Today = Convert.ToDateTime(DateTime.Now.AddDays(-10).ToString("yyyy-MM-dd"));//or other format of date
    DateTime dtToday = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd"));//or other format of date
    return View(db.Charters.Where(_ => _.CharterDate <= dtToday && _.CharterDate >= dt10Today).ToList()); }

    when the button is submitted the form would lose the information that was posted.... in webforms that would have been saved by the viewstate, how would you maintain their state?.  

    Generally, we use model binding to pass data between controller and view,

    and you also need a httppost action,like:

    [httpPost]
    public ActionResult Index(Model md) {
    ... return View(); }

    And you should add name tag for each fields in model,because controller get their values from name.

    In addition, I suggest you could add a scaffolded item so that you could understand model binding clearly.

     You could follow the steps below:

    Right click on Controllers -> Choose Add -> Choose 'MVC5 Controller with views, using Entity Framework', then Add-> Choose the Model you want to add CRUD to in 'Model class', then Add -> The controller with CRUD function is created.

    Then the  Index,Details,Edit ..pages will be automatically generated

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, November 29, 2018 10:04 AM
  • User1029764681 posted

    So the new httpPost version of index will be the version taking the parameters from the search form correct?

    I did start with the scaffolding on this project, however this data does not get deleted/edited so i removed those views and methods from the controller.

    Is the search form a new model?  Just curious how i bind those 5 fields back to the httppost index method.

    Thursday, November 29, 2018 3:06 PM
  • User1029764681 posted

    This is working, just would need to add the other 3 options and update the query correctly.

       [HttpPost]
            public ActionResult Index(string txtDepartureStation, string txtArrivalStation)
            {
                ViewBag.Tails = new SelectList(db.Aircraft, "Tail", "Tail");
    
                DateTime dtStartDate = Convert.ToDateTime(DateTime.Now.AddDays(-10).ToString("yyyy-MM-dd"));//or other format of date
                DateTime dtEndDate = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"));//or other format of date
    
                return View(db.Flights
                    .Where(x => x.TakeOff < dtEndDate && x.TakeOff >= dtStartDate)
                    .Where(x => x.DEP == txtDepartureStation || txtDepartureStation == "")
                    .Where(x => x.ARR == txtArrivalStation || txtArrivalStation == "")
                    .ToList());
            }

    Thursday, November 29, 2018 4:26 PM
  • User1029764681 posted

    This is what I ended up with...

     [HttpPost]
            public ActionResult Index(string txtStartDate, string txtEndDate, string txtTail, string txtDepartureStation, string txtArrivalStation)
            {
                DateTime dtStartDate;
                DateTime dtEndDate;
    
                if (txtStartDate == "")
                {
                    dtStartDate = Convert.ToDateTime(DateTime.Now.AddDays(-10).ToString("yyyy-MM-dd"));//or other format of date
                }
                else
                {
                    dtStartDate = Convert.ToDateTime(txtStartDate);
                }
    
                if (txtEndDate == "")
                {
                    dtEndDate = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"));//or other format of date
                }
                else
                {
                    dtEndDate = Convert.ToDateTime(txtEndDate).AddDays(1);
                }
    
                ViewBag.txtTail = txtTail;
                ViewBag.txtArrivalStation = txtArrivalStation;
                ViewBag.txtDepartureStation = txtDepartureStation;
                ViewBag.txtStartDate = dtStartDate.ToShortDateString();
                ViewBag.txtEndDate = dtEndDate.ToShortDateString();
                //ViewBag.Tails = new SelectList(db.Aircraft, "Tail", "Tail");
    
                return View(db.Flights
                    .Where(x => x.BlockOut < dtEndDate && x.BlockOut >= dtStartDate)
                    .Where(x => x.Tail == txtTail || txtTail == "")
                    .Where(x => x.DEP == txtDepartureStation || txtDepartureStation == "")
                    .Where(x => x.ARR == txtArrivalStation || txtArrivalStation == "")
                    .ToList());
            }
    

    Any suggestions on code clean up from you experienced MVC peeps?

    Thursday, November 29, 2018 8:11 PM
  • User1520731567 posted

    Hi fmrock164,

    Is the search form a new model?  Just curious how i bind those 5 fields back to the httppost index method.

    In Model binder we  need to create the Model class with required data field and after that we  can pass this Model class’s object  into  controller’s action method for  data mapping.

    For example:

    Create a viewModel if you haven't already that contains a property for txtStartDate,txtEndDate...

    public class SModel
    {
         public int Id {get; set;}
         public DateTime txtStartDate{ get; set; }
         public DateTime txtEndDate{ get; set; }
    ... }

    Controller:

    public ActionResult Index()
    {
        return View();
    }    

    Strongly type your view:

    @model TestApplication1.xxx.xxxx.SModel

    Add a form,don't forget submit button:

    using (@Html.BeginForm("Index", "Home", FormMethod.Post))
    {   
        @Html.EditorFor(model => model.txtStartDate)

    @Html.EditorFor(model => model.txtEndDate)//It will generate an HTML with id and name both txtEndDate

    ...
    <input type="submit" value="Create" class="btn btn-default" />
    }

    Now we need to add the reference of this model in to Controller with help of using statement.

    Now we need to pass the  SModel's object  as parameter into Index  Action method, so that  our form’s data/http request data will be map, below is the code

    [HttpPost]
    public ActionResult Index(SModel model)
    {//you could add a breakpoint on this,and check the value of model
       //now model.txtStartDate,model.txtEndDate has value
    ... return View(); }

    More details and methods,you could refer to:

    https://www.codeproject.com/Articles/710776/Introduction-to-ASP-NET-MVC-Model-Binding-An-Absol

    https://www.c-sharpcorner.com/article/introduction-to-asp-net-mvc-model-binding/

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, November 30, 2018 2:11 AM
  • User1029764681 posted

    Thank you Yuki...

    So right now this view is using my Flight model from EF.  Your suggesting switching that to a defined ViewModel?  So I would create a new folder called "ViewModels" and reference that instead.  This view model would also have to have all the fields from the Original model as well?  Can both the Model and VIewModel both be included?

    Friday, November 30, 2018 4:09 PM
  • User1520731567 posted

    Hi fmrock164,

    So right now this view is using my Flight model from EF.  Your suggesting switching that to a defined ViewModel? So I would create a new folder called "ViewModels" and reference that instead

    No,I just gave an example.

    My focus is on the need to pass the model.

    Both Model and ViewModel can.

    You could pass Flight model directly.

    This view model would also have to have all the fields from the Original model as well? 

    According to your needs,this is not mandatory.

    But if you would like to get the value of txtStartDate and txtEndDate in httppost action,

    You need to have these two fields in model and put them in form(@using (Html.BeginForm)), and not forget named them.

    Can both the Model and VIewModel both be included?

    When the business logic is complex, you may have multiple models,

    You could create a Viewmodel to contains these models,and then pass the ViewModel between view and controller.

    For example:

    if you have two models:

    public class SModel
    {
         public int Id {get; set;}
         public DateTime txtStartDate{ get; set; }
         public DateTime txtEndDate{ get; set; }
         ...
    }
    
    public class SModel2
    {
         public int Id {get; set;}
         public DateTime txtStartDate2{ get; set; }
         public DateTime txtEndDate2{ get; set; }
         ...
    }

    Create a viewmodel:

    public class SViewModel
    {
         public int Id {get; set;}
         public SModel sModel {get; set;}
         public SModel2 sModel2 {get; set;}
    }

    Strongly type your view:

    @model TestApplication1.xxx.xxxx.SViewModel

    ...

    using (@Html.BeginForm("Index", "Home", FormMethod.Post))
    {   
        @Html.EditorFor(model => model.SModel.txtStartDate)
    
        @Html.EditorFor(model => model.SModel.txtEndDate)//in SModel
    
        ...

    @Html.EditorFor(model => model.SModel2.txtStartDate2)
    @Html.EditorFor(model => model.SModel2.txtEndDate2) //in SModel2
    <input type="submit" value="Create" class="btn btn-default" /> }

    Controller:

    [HttpPost]
    public ActionResult Index(SViewModel model)
    {//you could add a breakpoint on this,and check the value of model
       //now model.txtStartDate,model.txtEndDate has value
       ...
       return View();
    
    } 

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, December 3, 2018 6:03 AM