knockout.js viewModel not displaying retrieved json from controller - model-view-controller

Im using a $.getJson to retrieve json objects into my viewmodel and applybindings. It will not display the databindings in the foreach come what may.
[code] - index.cshtml
#{
ViewData["Title"] = "Home Page";
}
#section Scripts {
<script>
require(['knockout', 'jquery', 'bootstrap', 'components/clients-list.component'], function (ko) {
ko.applyBindings();
});
</script>
}
<!DOCTYPE html>
<head>
<title>Index</title>
</head>
<body>
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Machine Grid</p>
<div>
<table>
<tbody data-bind="foreach: machines">
<tr style="border-bottom: 1px solid #000000;">
<td>
<span data-bind="text: machineid"></span>
</td>
<td>
<span data-bind="text: ports"></span>
</td>
<td>
<span data-bind="value: Weight"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
</div>
</body>
<script type="text/javascript">
var AppViewModel = function () {
var self = this;
self.machines = ko.mapping.fromJS([]);
$.getJSON('/Home/Index/', function (data) {
self.machines = ko.mapping.fromJS(data);
});
};
$(document).ready(function () {
var viewModel = new AppViewModel();
ko.applyBindings(viewModel);
});
</script>
//Models.MachineModel
public class MachineModel
{
public int MachineId { get; set; }
public string HardDrive { get; set; }
public string Ports { get; set; }
public string GraphicsCard { get; set; }
public string Weight { get; set; }
public string Power { get; set; }
public string Processor { get; set; }
public string Ram { get; set; }
}
//Controller
public async Task<IActionResult> Index()
{
IEnumerable<MachineModel> machineList;
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.GetAsync("https://localhost:44336/api/Machine"))
{
string apiResponse = await response.Content.ReadAsStringAsync();
machineList = JsonConvert.DeserializeObject<IEnumerable<MachineModel>>(apiResponse);
}
}
return View(machineList);
}
There is a a populated object I have checked when debugging. I have also tried using Json(machineList) but that comes up with a dialog box to download json and then terminates.

A couple of things I can see,
Object property name casing matters, so check the casing on the data being returned from server and make sure that the data-binds match.
as a general rule, only use the data-bind="value: someProperty" for elements that you can enter data into. usually this the <input /> element.
check the browser console (F12) for binding errors as this is a big help for discovering where the errors are.
If you get really stuck, add <pre data-bind="text: ko.toJSON($root)"></pre> to the html to help get a view of the state of the data in your view-model
var data = [{
"machineId": 1,
"hardDrive": "2TB",
"ports": "USB Type-C",
"graphicsCard": "3080",
"weight": "5.3 kg",
"power": "650w",
"processor": "Intel I7",
"ram": "32GB"
}, {
"machineId": 2,
"hardDrive": "2TB",
"ports": "USB Type-C x2",
"graphicsCard": "3080",
"weight": "5.3 kg",
"power": "650w",
"processor": "Intel I7",
"ram": "32GB"
}]
var AppViewModel = function() {
var self = this;
self.machines = ko.mapping.fromJS(data);
};
var viewModel = new AppViewModel();
ko.applyBindings(viewModel);
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<pre data-bind="text: ko.toJSON($root)"></pre>
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Machine Grid</p>
<div>
<table class='table'>
<thead>
<tr>
<th>Machine Id</th>
<th>Ports</th>
<th>Weight</th>
</tr>
</thead>
<tbody data-bind="foreach: machines">
<tr style="border-bottom: 1px solid #000000;">
<td>
<span data-bind="text: machineId"></span>
</td>
<td>
<span data-bind="text: ports"></span>
</td>
<td>
<span data-bind="text: weight"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>

public async Task Index()
{
return View();
}
public async Task<JsonResult> Machine()
{
IEnumerable<MachineModel> machineList;
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.GetAsync("https://localhost:44336/api/Machine"))
{
string apiResponse = await response.Content.ReadAsStringAsync();
machineList = JsonConvert.DeserializeObject<IEnumerable<MachineModel>>(apiResponse);
}
}
return Json(machineList);
}

Related

how to load data in table and bind the same to view Model class in .net core mvc (without refreshing the page)

I'm trying to add data in table via server but unable to succeed. I tried many things but nothing worked. I'm getting the data from the server but unable to populate that in table.. can someone please help me out?
I'm getting this :
"DataTables warning: table id=particlarsTable - Invalid JSON response. For more information about this error, please see http://datatables.net/tn/1"
Let me explain my code first:
(In View)
When I click on "Add New Particular" button, bootstrap modal will popup with input fields, those fields will send to the server then again back to view and populate the table with the same data without refreshing the whole page.
I'm stuck on the last stage, I'm getting the data on UI/View in ajax success handler but unable to load that data in table.
here is my view :
<form class="form-horizontal" method="post" id="createAdHocForm">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label>Select Flat</label>
<select asp-for="AdHocInvoice.FlatRid" asp-items="#(new SelectList(Model.Flats,"FlatRid","FlatNumber"))" class="form-control form-control-sm selectpicker" data-live-search="true">
<option selected disabled value="">Select One</option>
</select>
</div>
</div>
</div>
<table class="table table-sm table-responsive-md nowrap w-100" id="particlarsTable">
<thead class="thead-light">
<tr>
<th>Particulars</th>
<th>Amount</th>
<th></th>
</tr>
</thead>
<tbody class="bg-white">
#foreach (var item in Model.AdHocInvoice.FlatInvoiceItems)
{
<tr>
<td>#item.Particular</td>
<td>#item.Amount</td>
<td></td>
</tr>
}
</tbody>
</table>
<button class="btn btn-sm btn-outline-info" type="button" onclick="showParticularForm()">Add New Particular</button>
<hr />
<div class="row text-center">
<button class="btn btn-sm btn-outline-success mx-auto" type="submit">Submit</button>
</div>
</form>
bootstrap modal to fill the table :
<div class="modal fade" id="particularWindow">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Invoice Items</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" id="particularForm">
<div class="row">
<div class="col-md-5">
<div class="form-group">
<label>Enter Particular</label>
<input id="particularName" name="particular" class="form-control form-control-sm" required />
</div>
</div>
<div class="col-md-5">
<label>Amount</label>
<input id="particularAmount" name="amount" class="form-control form-control-sm" required />
</div>
<div class="col-md-2">
<button class="btn btn-sm btn-outline-success mt-4" id="btnParticularSubmit" type="button" onclick="addParticular()">Add</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
Scripts:
<link rel="stylesheet" href="~/DataTables/datatables.min.css" />
<script type="text/javascript" src="~/DataTables/datatables.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#particlarsTable').DataTable();
});
function showParticularForm() {
$('#particularWindow').modal('show');
}
function addParticular() {
var particular = $('#particularName').val();
var amount = $('#particularAmount').val();
$.ajax({
url: '/FlatInvoice/AddParticular',
data: 'particular=' + particular + '&amount=' + amount,
success: function (response) {
$('#particlarsTable').DataTable().ajax.reload()
}
});
}
</script>
Action Method on controller:
public JsonResult AddParticular(string particular, decimal amount)
{
_flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems.Add(new FlatInvoiceItem { Particular = particular, Amount = amount });
return Json(_flatInvoiceViewModel);
}
Class/Model:
public class FlatInvoiceItem
{
public Guid FlatInvoiceItemRid { get; set; }
public Guid FlatInvoiceRid { get; set; }
public Guid FundRuleRid { get; set; }
public string Particular { get; set; }
public decimal Amount { get; set; }
}
NOTE: this code is in initial phase, I need that input data on server for further process(will add code later) so don't want to use $.('#table_body_id').append("<tr>..</tr>"); type of code.
1.Firstly,reload() is used to send request back to the method which display the DataTable initially(e.g. name this method Test).
2.Secondly,From your AddParticular method,you just add a data to the list but the lifetime is just one request,so when you reload to Test method,the list still contains the initial data without new data.
Conclusion: I suggest that you could save data to database.And get data from database.
Here is a working demo about how to use DataTabale:
1.Model:
public class Test
{
public int Id { get; set; }
public AdHocInvoice AdHocInvoice { get; set; }
}
public class AdHocInvoice
{
public int Id { get; set; }
public string Name { get; set; }
public List<FlatInvoiceItem> FlatInvoiceItems { get; set; }
}
public class FlatInvoiceItem
{
public int Id { get; set; }
public Guid FlatInvoiceItemRid { get; set; }
public Guid FlatInvoiceRid { get; set; }
public Guid FundRuleRid { get; set; }
public string Particular { get; set; }
public decimal Amount { get; set; }
}
public class ViewModel
{
public string Particular { get; set; }
public decimal Amount { get; set; }
}
2.View:
<form class="form-horizontal" method="post" id="createAdHocForm">
<table class="table table-sm table-responsive-md nowrap w-100" id="particlarsTable">
<thead class="thead-light">
<tr>
<th>Particulars</th>
<th>Amount</th>
<th></th>
</tr>
</thead>
#*<tbody class="bg-white">
#foreach (var item in Model.AdHocInvoice.FlatInvoiceItems)
{
<tr>
<td id="particular">#item.Particular</td>
<td id="amount">#item.Amount</td>
<td></td>
</tr>
}
</tbody>*# //DataTable no need to add this tbody
</table>
<button class="btn btn-sm btn-outline-info" type="button" onclick="showParticularForm()">Add New Particular</button>
<hr />
<div class="row text-center">
<button class="btn btn-sm btn-outline-success mx-auto" type="submit">Submit</button>
</div>
</form>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<script>
$(document).ready(function () {
//$('#particlarsTable').DataTable();
$('#particlarsTable').DataTable({
ajax: {
type: 'GET',
dataType: 'JSON',
url: '#Url.Action("Test", "Home")',
},
columns: [
{ 'data': 'particular' },
{ 'data': 'amount' }
]
})
});
function showParticularForm() {
$('#particularWindow').modal('show');
}
function addParticular() {
var particular = $('#particularName').val();
var amount = $('#particularAmount').val();
$.ajax({
url: '/Home/AddParticular',
data: 'particular=' + particular + '&amount=' + amount
}).done(function (data) {
$('#particularWindow').modal('hide');
$('#particlarsTable').DataTable().ajax.reload();
});
}
</script>
3.Controller:
public class HomeController : Controller
{
private readonly MyDbContext _context;
public HomeController(MyDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public JsonResult Test()
{
var _flatInvoiceViewModel = _context.Test.Include(i=>i.AdHocInvoice)
.ThenInclude(a=>a.FlatInvoiceItems).Where(i => i.Id == 1).FirstOrDefault();
var list = new List<ViewModel>();
foreach (var item in _flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems)
{
var model = new ViewModel() { Amount = item.Amount, Particular = item.Particular };
list.Add(model);
}
return Json(new { data = list });
}
public void AddParticular(string particular, decimal amount)
{
var _flatInvoiceViewModel = _context.Test.Include(i => i.AdHocInvoice)
.ThenInclude(a => a.FlatInvoiceItems).Where(i => i.Id == 1).FirstOrDefault();
_flatInvoiceViewModel.AdHocInvoice.FlatInvoiceItems.Add(new FlatInvoiceItem { Particular = particular, Amount = amount });
_context.SaveChanges();
}
4.Result:
Reference:
https://datatables.net/examples/data_sources/ajax.html
https://stackoverflow.com/a/59449895/11398810

MVC with AJAX to Remove Table Row

I want to Remove a table row in my index page and before removing the row i have to modify in data base and show a pop to confirm the delete action. But while i delete the row it deleted in database but my page is not refreshed after executing the Success action in Ajax. There the code below i write for my view and controller and also giving my definition of model as reference.
// Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data.Entity;
using LAMS_Master.Models;
namespace LAMS_Master.Controllers
{
public class Acquisition_Lease_Case_RecordController : Controller
{
private ApplicationDbContext db;
public Acquisition_Lease_Case_RecordController()
{
db = new ApplicationDbContext();
}
protected override void Dispose(bool disposing)
{
db.Dispose();
}
// GET: Acquisition_Lease_Case_Record
public ActionResult Index()
{
List<acquisition_lease_case_record_master> listdata = new List<acquisition_lease_case_record_master>();
foreach(var d in db.acquisition_lease_case_record_master.Where(c => c.int_deleted_sts == 0).Include(c => c.vill_code).ToList())
{
d.chrv_land_class_cd = db.landclasslistmaster.Where(c => c.chrv_landclass_cd == d.chrv_land_class_cd).Select(c => c.chrv_landclass).SingleOrDefault();
listdata.Add(d);
}
return View("Index",listdata);
}
public ActionResult Create()
{
ViewBag.village = new SelectList(db.village_master.Where(c => c.int_deleted_sts == 0).ToList(), "chrv_vill_code", "chrv_vill_name");
ViewBag.landclass = new SelectList(db.landclasslistmaster.ToList(), "chrv_landclass_cd", "chrv_landclass");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(acquisition_lease_case_record_master alcrm)
{
if (ModelState.IsValid)
{
alcrm.fk_chrv_proj_code = "LAMS111811PCB06";
alcrm.int_created_by = 0;
alcrm.int_deleted_sts = 0;
alcrm.chrv_ip_address = UilityMaster.GetLocalIPAddress();
db.acquisition_lease_case_record_master.Add(alcrm);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.village = new SelectList(db.village_master.Where(c => c.int_deleted_sts == 0).ToList(), "chrv_vill_code", "chrv_vill_name");
ViewBag.landclass = new SelectList(db.landclasslistmaster.ToList(), "chrv_landclass_cd", "chrv_landclass");
return View();
}
[HttpGet]
public ActionResult Edit(string case_no)
{
var data = db.acquisition_lease_case_record_master.Include(c=>c.vill_code).Where(c=>c.chrv_case_no==case_no).SingleOrDefault();
data.chrv_land_class_cd = db.landclasslistmaster.Where(c => c.chrv_landclass_cd == data.chrv_land_class_cd).Select(c => c.chrv_landclass).SingleOrDefault();
return View("EditALCRM", data);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(acquisition_lease_case_record_master alcrmedit)
{
if (alcrmedit.num_case_area!=null && alcrmedit.tms_case_dt!=null && alcrmedit.chrv_case_no!=null)
{
var casefromdb = db.acquisition_lease_case_record_master.Find(alcrmedit.chrv_case_no);
casefromdb.num_case_area = alcrmedit.num_case_area;
casefromdb.tms_case_dt = alcrmedit.tms_case_dt;
db.Entry(casefromdb).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View("EditALCRM", alcrmedit);
}
public JsonResult DeleteCaseRecord(string case_no)
{
var deletedata = db.acquisition_lease_case_record_master.Find(case_no);
bool result = false;
if (deletedata != null)
{
deletedata.int_deleted_sts = 1;
db.Entry(deletedata).State = EntityState.Modified;
db.SaveChanges();
result = true;
}
return Json(result, JsonRequestBehavior.AllowGet);
}
}
}
//Index page
#model IEnumerable<LAMS_Master.Models.acquisition_lease_case_record_master>
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="wrapper">
<div class="panel panel-default">
<h3 class="panel-heading">Case Details</h3>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Village</th>
<th>Land Class</th>
<th>Case No</th>
<th>Case Date</th>
<th>Case Area</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var v in Model)
{
<tr id="#v.chrv_case_no">
<td>
#v.vill_code.chrv_vill_name
</td>
<td>
#v.chrv_land_class_cd
</td>
<td>
#v.chrv_case_no
</td>
<td>
#v.tms_case_dt.ToString("dd-MMM-yyyy")
</td>
<td>
#v.num_case_area
</td>
<td>
#Html.ActionLink("Edit", "Edit", "Acquisition_Lease_Case_Record", new { case_no = v.chrv_case_no }, null) |
Delete
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<input type="hidden" id="hiddencaseno" value="" />
</div>
#*Modal Defination*#
<div class="modal fade" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
×
<h3 class="modal-title">Delete</h3>
</div>
<div class="modal-body">
<h4>Are you sure to delete ?</h4>
<div style="text-align:center;display:none" id="loaderDiv">
<img src="~/Images/loader.gif" width="150" />
</div>
</div>
<div class="modal-footer">
Cancel
Confirm
</div>
</div>
</div>
</div>
// Script for index page
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
var ConfirmDelete = function (actid) {
$("#hiddencaseno").val(actid);
$("#myModal").modal('show');}
var DeleteItem = function () {
$("#loaderDiv").show();
var actid = $("#hiddencaseno").val();
$.ajax({
type: 'POST',
url: '#Url.Action("DeleteCaseRecord")',
data: { case_no: actid },
success: function (result) {
$("#loaderDiv").hide();
$("#myModal").modal('hide');
alert('#' + actid);
$('#'+actid).remove(); }})}
</script>

How to pass a list of objects instead of one object to a POST action method

I have the following GET and POST action methods:-
public ActionResult Create(int visitid)
{
VisitLabResult vlr = new VisitLabResult();
vlr.DateTaken = DateTime.Now;
ViewBag.LabTestID = new SelectList(repository.FindAllLabTest(), "LabTestID", "Description");
return View();
}
//
// POST: /VisitLabResult/Create
[HttpPost]
public ActionResult Create(VisitLabResult visitlabresult, int visitid)
{
try
{
if (ModelState.IsValid)
{
visitlabresult.VisitID = visitid;
repository.AddVisitLabResult(visitlabresult);
repository.Save();
return RedirectToAction("Edit", "Visit", new { id = visitid });
}
}
catch (DbUpdateException) {
ModelState.AddModelError(string.Empty, "The Same test Type might have been already created,, go back to the Visit page to see the avilalbe Lab Tests");
}
ViewBag.LabTestID = new SelectList(repository.FindAllLabTest(), "LabTestID", "Description", visitlabresult.LabTestID);
return View(visitlabresult);
}
Currently the view display the associated fields to create only one object,, but how i can define list of objects instead of one object to be able to quickly add for example 10 objects at the same “Create” request.
My Create view look like:-
#model Medical.Models.VisitLabResult
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#section scripts{
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>VisitLabResult</legend>
<div class="editor-label">
#Html.LabelFor(model => model.LabTestID, "LabTest")
</div>
<div class="editor-field">
#Html.DropDownList("LabTestID", String.Empty)
Your viewModel
public class LabResult
{
public int ResultId { get; set; }
public string Name { get; set; }
//rest of the properties
}
Your controller
public class LabController : Controller
{
//
// GET: /Lab/ns
public ActionResult Index()
{
var lst = new List<LabResult>();
lst.Add(new LabResult() { Name = "Pravin", ResultId = 1 });
lst.Add(new LabResult() { Name = "Pradeep", ResultId = 2 });
return View(lst);
}
[HttpPost]
public ActionResult EditAll(ICollection<LabResult> results)
{
//savr results here
return RedirectToAction("Index");
}
}
Your view
#model IList<MvcApplication2.Models.LabResult>
#using (Html.BeginForm("EditAll", "Lab", FormMethod.Post))
{
<table>
<tr>
<th>
ResultId
</th>
<th>
Name
</th>
</tr>
#for (int item = 0; item < Model.Count(); item++)
{
<tr>
<td>
#Html.TextBoxFor(modelItem => Model[item].ResultId)
</td>
<td>
#Html.TextBoxFor(modelItem => Model[item].Name)
</td>
</tr>
}
</table>
<input type="submit" value="Edit All" />
}
Your view will be rendered as follows, this array based naming convention makes it possible for Defaultbinder to convert it into ICollection as a first parameter of action EditAll
<tr>
<td>
<input name="[0].ResultId" type="text" value="1" />
</td>
<td>
<input name="[0].Name" type="text" value="Pravin" />
</td>
</tr>
<tr>
<td>
<input name="[1].ResultId" type="text" value="2" />
</td>
<td>
<input name="[1].Name" type="text" value="Pradeep" />
</td>
</tr>
If I understand your question correctly,
you want to change your view to be a list of your model object #model List, then using a loop or however you wish to do it, create however many editors you need to for each object
then in your controller your receiving parameter of create will be a list of your model instead too.

MVC Dropdown list persting for each requests

I am working on a web app using MVC, where all pages have header section consiting of three dropdowns one text box and an image button. So no matter on which page user is, he/she has ability to select a combination out of three dropdowns, enter some text in text box and hit image button to search in database.
So i am using a strongly typed(BaseViewModel) master _Layoutthought, and then add two partial views on it, one to display user info and another one to display all dropdowns, text box and imaage button. Now, Models i have 3 models for each dropdown option/item and then a BaseViewModel which will have 3 properties as a list of each model and 3 int properties to hold selected item ID. And plan is to use this baseViewModel as parent class for all viewmodels so that i can fullfil my strongly tyed _Layout for each request and responce. OK i am not sure that if i have explained my requirement clearly :(.
so here is the code
all models:
namespace XYZNameSpace.Models
{
public class ManageCompany
{
public long ManageCompanyID { get; set; }
public string ManageCompanyName { get; set; }
}
}
namespace XYZNameSpace.Models
{
public class SuperCompany
{
public long SuperCompanyID { get; set; }
public string SuperCompanyName { get; set; }
}
}
public class User
{
public long UserID { get; set; }
public string UserName { get; set; }
public string UserLoginName { get; set; }
public string UserLevel { get; set; }
public int UserLevelId { get; set; }
}
namespace XYZNameSpace.Models
{
public class SearchResult
{
public string CompanyName { get; set; }
public string SubCompanyName { get; set; }
public string UserName { get; set; }
}
}
ViewModel
namespace XYZNameSpace.ViewModels
{
public class BaseViewModel : User
{
public long SelectedManageCompanyID { get; set; }
public List<SelectListItem> ManageCompanyList { get; set; }
public string SelectedManageCompanyName { get; set; }
public long SelectedSuperCompanyID { get; set; }
public List<SelectListItem> SuperCompanyList { get; set; }
public string SelectedSuperCompnayName { get; set; }
public int SelectedSearchOptionID { get; set; }
public List<SelectListItem> SearchOptionsList { get; set; }
public string SelectedSearchOptionName { get; set; }
public string SearchWord { get; set; }
}
public class SearchOption
{
public int SearchOptionID { get; set; }
public string SearchOptionName { get; set; }
}
}
namespace XYZNameSpace.ViewModels
{
public class SearchResultsViewModel : BaseViewModel
{
public List<SearchResult> ResultsList { get; set; }
}
}
Controller
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Home()
{
BaseViewModel bVM = new BaseViewModel()
{
ManageCompanyList = (new List<ManageCompany>() {
new ManageCompany()
{
ManageCompanyID = 1,
ManageCompanyName = "MC 1"
},
new ManageCompany()
{
ManageCompanyID = 2,
ManageCompanyName = "MC 2"
},
new ManageCompany()
{
ManageCompanyID = 3,
ManageCompanyName = "MC 3"
},
new ManageCompany()
{
ManageCompanyID = 4,
ManageCompanyName = "MC 4"
},
new ManageCompany()
{
ManageCompanyID = 5,
ManageCompanyName = "MC 5"
}
}).Select(x => new SelectListItem { Value = x.ManageCompanyID.ToString(), Text = x.ManageCompanyName }).ToList(),
SuperCompanyList = (new List<SuperCompany>(){
new SuperCompany(){
SuperCompanyID=6,
SuperCompanyName="SC 6"
},
new SuperCompany(){
SuperCompanyID=7,
SuperCompanyName="SC 7"
},
new SuperCompany(){
SuperCompanyID=8,
SuperCompanyName="SC 8"
},
new SuperCompany(){
SuperCompanyID=9,
SuperCompanyName="SC 9"
},
new SuperCompany(){
SuperCompanyID=10,
SuperCompanyName="SC 10"
}
}).Select(y => new SelectListItem { Value = y.SuperCompanyID.ToString(), Text = y.SuperCompanyName }).ToList(),
SearchOptionsList = (new List<SearchOption>(){
new SearchOption(){
SearchOptionID=11,
SearchOptionName="SO 11"
},
new SearchOption(){
SearchOptionID=12,
SearchOptionName="SO 12"
},
new SearchOption(){
SearchOptionID=13,
SearchOptionName="SO 13"
},
new SearchOption(){
SearchOptionID=14,
SearchOptionName="SO 14"
},
new SearchOption(){
SearchOptionID=15,
SearchOptionName="SO 15"
}
}).Select(y => new SelectListItem { Value = y.SearchOptionID.ToString(), Text = y.SearchOptionName }).ToList()
};
return View(bVM);
}
[HttpPost]
public ActionResult Search(BaseViewModel bVM)
{
SearchResultsViewModel sRVM = new SearchResultsViewModel();
sRVM.ResultsList = new List<SearchResult>(){
new SearchResult()
{
CompanyName = bVM.SelectedSuperCompanyID.ToString(),
SubCompanyName = bVM.SelectedManageCompanyID.ToString(),
UserName = "Duh"
}
};
return View("SearchResults", sRVM);
}
}
Views /Home/Home.cshtml
#{
ViewBag.Title = "Home";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div style="float: left; vertical-align: top;">
</div>
<br />
/Home/SearchResults.cshtml
#model XYZNameSpace.ViewModels.SearchResultsViewModel
<table width="100%">
<thead>
<tr>
<td>
COMPANY
</td>
<td>
SUB COMPANY
</td>
<td>
USER NAME
</td>
</tr>
</thead>
<tbody>
#foreach (XYZNameSpace.Models.SearchResult sr in Model.ResultsList)
{
<tr>
<td>
#sr.CompanyName
</td>
<td>
#sr.SubCompanyName
</td>
<td>
#sr.UserName
</td>
</tr>
}
</tbody>
</table>
/Shared/SearchDDLPartialView.cshtml
#model XYZNameSpace.ViewModels.BaseViewModel
<div>
<div style="float: left">
<span class="Label">Search: </span>
#using (Html.BeginForm("Search", "Home", FormMethod.Post))
{
#Html.DropDownListFor(x => x.SelectedSuperCompanyID, Model.SuperCompanyList)
#Html.DropDownListFor(x => x.SelectedManageCompanyID, Model.ManageCompanyList)
#Html.DropDownListFor(x => x.SelectedSearchOptionID, Model.SearchOptionsList)
<input type="image" src="../../Content/images/search.gif" />
}
</div>
</div>
/Shared/_Layout.cshtml
#model XYZNameSpace.ViewModels.BaseViewModel
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
<link href="#Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Content("~/Content/BEClientProfile.css")" rel="Stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
</head>
<body>
<table>
<tr>
<td>
<img src="../../Content/images/logo124.gif" alt="Logo" />
</td>
<td valign="top">
<div style="float: left; vertical-align: top; text-align: left">
<img src="../../Content/images/user-info.gif" alt="User Infor" />
<span class="Label">Welcome </span><span class="HeaderLabelText">#Model.UserLevel</span>
</div>
<div>
<div style="float: left; width: 100%;">
<u><span class="Label">Currently Editing:</span> </u>
</div>
<div style="float: left; width: 100%;">
<span class="Label">Company:</span> <span class="HeaderLabelText">#Model.SelectedManageCompanyID</span>
</div>
<div style="float: left; width: 100%;">
<span class="Label">User:</span> <span class="HeaderLabelText">#Model.UserLoginName</span>
</div>
</div>
</td>
<td>
</td>
<td>
#{Html.RenderPartial("SearchDDLPartialViews", Model);}
</td>
</tr>
<tr>
<td colspan="4">
#RenderBody()
</td>
</tr>
</table>
</body>
</html>
/Shared/UserPartialView.cshtml
#model XYZNameSpace.Models.User
<div style="float: left; margin-left: .5em;">
<img src="../../Content/images/user-info.gif" alt="User Infor" />
<span style="font-weight: bold; float: left;">Welcome </span>
</div>
OK, i was able to select one item from each dropdown but when i click on image button at search Action in home control, the BaseViewModel has nothing other than selectedIDs, so when action is trying to display SearchResults view back, them _Layout.cshtml throughs error saying there nothing in lists that are used by dropdowns.
so how do we persist this information for every request...?
Quick solution is store the List < SelectListItem > or the viewmodel itself in session and reintroduce them to the view every time you return the viewmodel. However for cloud based solutions, same concept, but you'll need to use other caching storage such as appfabric, memcache or other addons allowed by your PaaS.

Parameters from view not getting to controller action method

I'm implementing Troy Goode's PagedList in one of my views (ASP.NET MVC 3 Razor). The challenge I'm having is when I click on a page number link, the request is routed to my HttpGet method, which just returns the empty page (ready for input).
My View Model:
public class SearchViewModel
{
public SelectList IndustrySelectList { get; set; }
public IPagedList<KeyValuePair<string, SearchResult>> SearchResults { get; set; }
public PagingInfo PagingInfo { get; set; }
}
Controller:
[HttpGet]
public ViewResult Search(string searchTerm = "")
{
SearchViewModel vm = new SearchViewModel
{
IndustrySelectList = new SelectList(_Industries.AsEnumerable(), "IndustryId", "IndustryName"),
PagingInfo = new PagingInfo
{
CurrentPage = 1,
ItemsPerPage = 25,
TotalItems = 0
}
};
return View(vm);
}
[HttpPost]
public ActionResult Search(string[] industries, string searchTerm = "", int page = 1)
{
SearchViewModel vm = null;
_url = "http://localhost/MasterNode/masternode.cgi?zoom_query={" + searchTerm + "}&zoom_xml=1&zoom_page={startPage?}&zoom_per_page=1000";
StringBuilder sb = new StringBuilder();
int pageSize = 5;
if (string.IsNullOrEmpty(searchTerm))
{
vm = new SearchViewModel
{
IndustrySelectList = new SelectList(_Industries.AsEnumerable(), "IndustryId", "IndustryName")
};
}
else
{
_request = new SearchRequest(SearchRequest.EnvironmentTypes.Development, "", _url, searchTerm, SearchRequest.SearchType.AllWords, 1000);
sb.Append(GetResults(_url));
_results = new Dictionary<string, SearchResult>();
ParseResults(sb);
GetDetailInformationForResults(searchTerm);
vm = new SearchViewModel
{
IndustrySelectList = new SelectList(_Industries.AsEnumerable(), "IndustryId", "IndustryName"),
SearchResults = _results.ToList<KeyValuePair<string, SearchResult>>().ToPagedList(1, 25),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = pageSize,
TotalItems = _results.Count()
}
};
}
return View(vm);
}
View:
#model MultiView.OmniGuide.ViewModels.SearchViewModel
#using MultiView.OmniGuide.HtmlHelpers
#using PagedList
#using PagedList.Mvc
#{
ViewBag.Title = "Search";
}
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
#using (Html.BeginForm("Search", "Home"))
{
#Html.HiddenFor(c => c.IndustrySelectList)
#Html.HiddenFor(c => c.PagingInfo)
#Html.HiddenFor(c => c.SearchResults)
<table width="70%">
<tr>
<td colspan="2" style="background: #fff">
<input id="searchTerm" name="searchTerm" type="text" class="SearchBox" style="width: 450px" />
<input type="submit" class="SearchButton" value=" " />
</td>
</tr>
<tr align="left">
<td align="left" style="background: #fff">
#Html.ActionLink("MultiView corporate site", "Search")
</td>
</tr>
<tr>
<td colspan="1" align="center" style="width: 450px">
#{
Html.Telerik().PanelBar()
.Name("searchPanel")
.Items(title =>
{
title.Add()
.Text("Filter by Industry")
.Content(() =>
{
#Html.RenderPartial("_Industry", #Model);
});
})
.Render();
}
</td>
</tr>
<tr><td colspan="2"></td></tr>
</table>
<br />
if (Model.SearchResults != null)
{
<table width="70%">
<tr>
<th>
Company Image
</th>
<th class="tableHeader">
Company Name Here
</th>
<th class="tableHeader">
Website
</th>
</tr>
#foreach (KeyValuePair<string, MultiView.OmniGuide.Models.SearchResult> itm in Model.SearchResults)
{
<tr>
<td align="left" style="width: 15%">
#itm.Value.DetailedInfo.LogoURL
</td>
<td align="left" style="width: 60%">
<p style="text-align: left">
#itm.Value.DetailedInfo.DescriptionAbbreviated
<br />
</p>
#Html.AnchorLink(itm.Value.FoundURL, itm.Value.FoundURL)
</td>
<td style="width: 25%">
#itm.Value.FoundURL
</td>
</tr>
}
</table>
#Html.PagedListPager((IPagedList)Model.SearchResults, page => Url.Action("Search", "Home", new { page }))
}
}
When text is supplied in the input box and the button is clicked, the requested is routed to the HttpPost method. In looking at the request.form values, all expected data but paging information is present.
?HttpContext.Request.Form.AllKeys
{string[5]}
[0]: "IndustrySelectList"
[1]: "PagingInfo"
[2]: "SearchResults"
[3]: "searchTerm"
[4]: "industries"
Any help with this would be very much appreciated!
By clicking the button you are submitting the form which is why it is doing the httppost. The next page link is hitting the httpget correctly but you are not passing it any information to so that it knows what to get. The get needs other information, like what page you are wanting.
The page number links fire a GET request, so you'll need to make sure that your GET action can handle the full search as well, so will need to get the page number and industries array - using defaults for when those parameters aren't available.
e.g.
[HttpGet]
public ViewResult Search(string searchTerm = "", int page = 1,
string industries = "")
{
//.....
}
You'll need to modify the pager link like this to pass industries to the get action.
#Html.PagedListPager((IPagedList)Model.SearchResults, page => Url.Action("Search", "Home", new { page, industries = string.Join(",", Model.IndustrySelectList.Where( x => x.Selected).Select( x => x.Text)) }))
It's not clear to me from your code where the post action is getting string[] industries from, or what it is doing with it, but you will need some way of passing this same this to your get action, probably as a single string that is comma separated. The example I've provided assumed you are taken it from the select list on the viewmodel

Resources