I was trying to implement Listview control of Kendo UI for MVC. I am trying to bind the list view with my model but I am getting this error :
"CS1977: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type"
I have checked some other questions on stackoverflow with the same error but I am unable to know the cause for this error as this is kendo Syntax and there is nothing wrong with my code as far as I know.
The error is in this line::.DataSource(ds => ds
View Page:
#{
ViewBag.Title = "Courses";
}
#using Kendo.Mvc.UI
<h2>Courses</h2>
Back
<div class="bodywrap">
<div class="CommonClass">
#( Html.Kendo().ListView<K_SampleProject.Models.CourseModel>(Model)
.Name("listView")
.TagName("div")
.ClientTemplateId("template")
.DataSource(ds => ds
.Model(model =>
{
//The unique identifier (primary key) of the model is the ProductID property
model.Id(p => p.ProductID);
// Declare a model field and optionally specify its default value (used when a new model instance is created)
model.Field(p => p.ProductName).DefaultValue("N/A");
// Declare a model field and make it readonly
model.Field(p => p.UnitPrice).Editable(false);
})
)
.Pageable()
)
</div>
</div>
<script type="text/x-kendo-tmpl" id="template">
<div class="product">
<img src="#Url.Content("~/content/web/foods/")${ProductID}.jpg" alt="${ProductName} image" />
<h3>${ProductName}</h3>
<dl>
<dt>Price:</dt>
<dd>${kendo.toString(UnitPrice, "c")}</dd>
</dl>
</div>
</script>
Model
namespace K_SampleProject.Models
{
public class CourseModel
{
public List<tbl_Courses> CourseList { get; set; }
public string ProductID { get; set; }
public string ProductName { get; set; }
public string UnitPrice { get; set; }
}
}
Controller
public ActionResult Courses()
{
CourseModel Model = new CourseModel();
RegistrationService ObjService = new RegistrationService();
Model.CourseList = ObjService.GetCourses();
return View(Model);
}
The main error in your code is that you passing single CourseModel class to the list, when it expects the List of CourseModel.
So, your Controller should looks like:
public ActionResult Courses()
{
List<CourseModel> result;
CourseModel Model = new CourseModel();
RegistrationService ObjService = new RegistrationService();
Model.CourseList = ObjService.GetCourses();
result.Add(Model);
return View(result);
}
I also advise:
Add #model List<CourseModel> in top of the View
If it is a PartialView (not main view like index) change return for: return PartialView(result);
Related
Here is the code for my Model. ListBuilder.DropDown is part of a common class of functions, which simply returns a List when provided the string name of a stored procedure that will be called on the database.
There is some more shared common class code (stored procedure related) with in the try statement, but that implementation is irrelevant to the problem I'm having. The data is successfully retrieved and stored into the model.
public class PositionViewModel
{
[Display(Name = "Series")]
public string series { get; set; }
public int seriesID { get; set; }
public List<SelectListItem> list_Series { get; set; }
}
public PositionViewModel(string id)
{
Get(id);
this.list_Series = ListBuilder.DropDown(AppConstants.StoredProc_GetSeries);
}
public Position Get(string id)
{
ExecStoredProcedure sp = null;
DataTable dt = new DataTable();
try
{
sp = new ExecStoredProcedure(AppConstants.SP_POSITION_GET, new ConnectionManager().GetConnection(), AppConstants.SP_POSITION_GET);
sp.SPAddParm("#PD_ID", SqlDbType.Char, id, ParameterDirection.Input);
dt = sp.SPselect();
if (dt.Rows.Count > 0)
{
this.pd_id = dt.Rows[0]["PD_ID"].ToString();
this.official_title = dt.Rows[0]["label"].ToString();
this.series = dt.Rows[0]["Series"].ToString();
this.grade = dt.Rows[0]["Grade"].ToString();
this.PDType = dt.Rows[0]["PDType"].ToString();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
sp.dbConnection.Close();
}
return this;
}
Here is the code for my Controller
[HttpGet]
public ActionResult PositionEdit(string id)
{
PositionViewModel model = new PositionViewModel(id);
return View("PositionEdit", model);
}
[HttpPost]
public ActionResult PositionEdit(PositionViewModel model)
{
if (ModelState.IsValid)
{
int rc = model.Update();
return RedirectToAction("PositionView");
}
else
{
return View("PositionEdit", model);
}
}
Here is the code for my view. What I'd like to have is a dropdownlist that contains the model.seriesID (a sequence number) but as the user selects an item, it will update the textbox with model.series (the name of the series)
#model Project.Models.PositionViewModel
#{
ViewBag.Title = "Edit Position Description";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.series)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => Model.seriesID, Model.list_Series, new { style = "width:550px" })
#Html.ValidationMessageFor(model => Model.seriesID)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.series, new { style = "width:250px;" })
#Html.ValidationMessageFor(model => model.series)
</div>
<div class="toppad20">
<input type="submit" value="Save" />
</div>
}
I am having trouble linking the dropdownlist with the textbox. Do I need some kind of onChange event? Thanks in advance for your help.
Your solution involves passing a string into your view model's constructor. However, on post, the model binder will be incapable of instantiating your view model with anything but the parameterless constructor. That's part of the reason, but not the only reason, that view models should not handle things like datastore access. That is the job of the controller.
On your view model, leave your list property as a auto-implemented property and then in your controller call ListBuilder.DropDown, which you can use data from your model to call, at that point.
My Model is
public class IssueEntryModel
{
public IEnumerable<SelectListItem> OrderNumbers { get; set; }
public string SelectedWorkOrder { get; set; }
public string MaterialCode
{
get; set;
}
public List<GroupedIssueData> MaterialData { get; set; }
}
And the view is
#model InventoryEasy15.Models.IssueEntryModel
#{
var issueData = Model.MaterialData;
var workorders = Model.SelectedWorkOrder;
}
#using (Html.BeginForm("SaveIssueEntry", "IssueMaster", FormMethod.Post, new { id = "issueEntryForm" }))
{
#for (int i = 0; i < issueData.Count(); i++)
{
<tr>
<td>#issueData[i].MaterialCode</td>
<td>#issueData[i].MaterialDescription</td>
<td>#issueData[i].Unit</td>
<td>#issueData[i].ReqQty</td>
<td>#Html.TextBoxFor(m => issueData[i].IssueQty, new { style = "width:70px" })#Html.ValidationMessageFor(m => issueData[i].IssueQty)</td>
<td class="text-center">#Html.CheckBoxFor(m => issueData[i].isSavings)</td>
</tr>
}
And I have post method as
public ActionResult SaveIssueEntry(IssueEntryModel model)
{
var result = new Dictionary<string, string>();
And the get contains the details to fill the view as
//Method Get the material details based on the work order id
public async Task<ActionResult> GetWorkOrderMaterialDetails(IssueEntryModel m)
{
During post to a new method , the model is becomes null, Any thoughts?
Razor uses the expression passed to the HTML helpers in order to build the proper name for the inputs that will allow the modelbinder to bind them properly on post. That means the expression needs to match the access method of the property exactly. By saving Model.MaterialData to the issueData variable and utilizing that, you're disrupting this. In other words, you're ending up with inputs named like issueData[0].IssueQty, instead of MaterialData[0].IssueQty. The modelbinder doesn't know what to do with issueData on post, because nothing on your model matches that.
Long and short, your textbox needs to be declared like:
#Html.TextBoxFor(m => m.MaterialData[i].IssueQty, ...)
Similarly for your checkbox:
#Html.CheckBoxFor(m => m.MaterialData[i].isSavings)
Getting an error while trying to add a grid to my detail page. The error is:
The model item passed into the dictionary is of type 'GridMvc.Html.HtmlGrid1[MyApp.Models.RecipientActivityMetadata]', but this dictionary requires a model item of type 'System.Collections.Generic.List1[MyApp.Models.RecipientActivityMetadata]'.
MVC4 View is a combination of a detail page and a list. I am using a viewmodel that looks like this:
public class FormViewModel()
{
public RecipientMetadata Recipient { get; set; }
public StudentActivityMetadata StudentActivity { get; set; }
public List<RecipientActivityMetadata> RecipientActivites { get; set; }
}
The view top is:
#model MyApp.Models.ViewModels.FormViewModel
and it renders a partial view which contains the list:
#Html.Partial("_grid", Model.RecipientActivites)
and the partial looks like this:
#using GridMvc.Html
#model List<MyApp.Models.RecipientActivityMetadata>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<div>
#Html.Grid(Model).Columns(columns =>
{
columns.Add(c => c.ActCount).Titled("Activity Num");
columns.Add(c => c.ActivityType).Titled("Activity Type");
columns.Add(c => c.FundCode).Titled("FundCode");
columns.Add(c => c.Hours).Titled("Hours");
}).WithPaging(10)
</div>
From Comment to Answer
According to the documentation provided by Grid.Mvc, #Html.Grid uses a partial view _Grid.cshtml. Because your partial view also has same name, the solution is to use a different name for your partial view.
I have this in a partial view
#using (Html.BeginForm(MVC.Inventory.ActionNames.AddVehicles, MVC.Inventory.Name, new { model = Model.Items }))
{
<div><button>#AuctionControllerResource.AddToBiddingProcess</button></div>
}
The post method is this
[HttpPost]
public virtual ActionResult AddVehicles(List<VehicleViewModel> model)
{
return null;
}
When I put a breakpoint in the view I can see that Model.Items has 1 item in it as it should. However, when I hit the Post action method on button click, there are no items in the model.
I have added this in the form
#Html.HiddenFor(m => m.Items)
but it doesn't help.
What am I doing wrong?
thanks,
Sachin
EDIT
Additional code
public class ListViewModel<T> : IQuery
where T : class
{
public List<T> Items { get; set; }
...
}
The following doesn't do what you think it does:
new { model = Model.Items }
You cannot pass complex objects like that. You will have to generate hidden fields in the form if you want this to work.
I have added this in the form
#Html.HiddenFor(m => m.Items)
No, it's normal that it doesn't help. The hidden field works only with simple types. You wil have to loop through the items in the collection and generate corresponding fields for each property of each element:
#using (Html.BeginForm(MVC.Inventory.ActionNames.AddVehicles, MVC.Inventory.Name))
{
for (var i = 0; i < Model.Items.Count; i++)
{
#Html.HiddenFor(x => x.Items[i].Prop1)
#Html.HiddenFor(x => x.Items[i].Prop2)
#Html.HiddenFor(x => x.Items[i].ComplexProp3.Prop1)
#Html.HiddenFor(x => x.Items[i].ComplexProp3.Prop2)
...
}
<div>
<button>#AuctionControllerResource.AddToBiddingProcess</button>
</div>
}
But this seems quite a waste. Since the user cannot modify those values anyway in the form, I would recommend you simply passing an id which will allow you to retrieve the corresponding items from your data store in the POST action:
#using (Html.BeginForm(MVC.Inventory.ActionNames.AddVehicles, MVC.Inventory.Name, new { id = Model.ItemsId }))
{
<div>
<button>#AuctionControllerResource.AddToBiddingProcess</button>
</div>
}
and then:
[HttpPost]
public virtual ActionResult AddVehicles(int id)
{
List<VehicleViewModel> model = GetItemsFromDataStore(id);
...
}
Is it possible to use two different controllers for the same url?
This is needed because I need the URL to always remain the same, but it should use different controllers. My controllers (Apples, Bananas, etc.) and views are separated into each own project.
I need a action in my main MVC project to return a action/view from either the Bananas or Apples project depending on some logic.
So how would I go ahead to always have the same url but return actions/views from different controllers?
I'm using MVC 4
Your URLs should be where the logic for selecting your controller is. Maybe you need to reorganise your project to have a single controller and put the other logic in the controller action for filling the model?
However, if you insist on going this route you will likely need to override CreateController in the DefaultControllerFactory, this is the class that instantiates your controller, usually based on your controller name. Here is an example in one of my projects:
public class ErrorHandlingControllerFactory : DefaultControllerFactory
{
/// <summary>
/// Injects a custom attribute
/// on every action that is invoked by the controller
/// </summary>
/// <param name="requestContext">The request context</param>
/// <param name="controllerName">The name of the controller</param>
/// <returns>An instance of a controller</returns>
public override IController CreateController(
RequestContext requestContext,
string controllerName)
{
var controller =
base.CreateController(requestContext,
controllerName);
var c = controller as Controller;
if (c != null)
{
c.ActionInvoker =
new ErrorHandlingActionInvoker(
new HandleErrorWithELMAHAttribute());
}
return controller;
}
}
You will need to set your route up to pass a known controller name (horrible magic strings...), test for this controller name, and if detected run your logic to get the actual controller name and pass this in to base.CreateController.
I wrote these codes. I hope that it helps you. I used hidden field to understand which method will run.
these are my models:
namespace MvcSameController.Models
{
public class RouteModel
{
public SampleModel1 SampleModel1 { get; set; }
public SampleModel2 SampleModel2 { get; set; }
}
public class SampleModel1
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SampleModel2
{
public int Id { get; set; }
public string Surname { get; set; }
}
}
this is controller:
using System.Web.Mvc;
using MvcSameController.Models;
namespace MvcSameController.Controllers
{
public class SameController : Controller
{
//
// GET: /Same/
public ActionResult Index()
{
return View();
}
[HttpPost]
public void Index(RouteModel routeModel, string type)
{
if (type == "1")
{
//Code for type 1
}
else if (type == "2")
{
//Code for type 2
}
}
}
}
and view :
#{
ViewBag.Title = "Index";
}
#model MvcSameController.Models.RouteModel
<section id="loginForm">
<h2>Type1 </h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.Hidden("type",1)
<fieldset>
<legend>Type1 Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.SampleModel1.Name)
#Html.TextBoxFor(m => m.SampleModel1.Name)
#Html.ValidationMessageFor(m => m.SampleModel1.Name)
</li>
</ol>
<input type="submit" value="Run Method1" />
</fieldset>
}
</section>
<section id="loginForm">
<h2>Type2</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.Hidden("type",2)
<fieldset>
<legend>Type2 Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.SampleModel2.Surname)
#Html.TextBoxFor(m => m.SampleModel2.Surname)
#Html.ValidationMessageFor(m => m.SampleModel2.Surname)
</li>
</ol>
<input type="submit" value="Run Method2" />
</fieldset>
}
</section>
you can download my sample from here