How to implement foreach delegate in razor viewengine? - asp.net

The following code works for webform view engine.
<% Model.Categories.ForEach(x => { %>
<li>#x.Name</li>
<% }) %>
I wrote the above code as below in razor view:
#Model.Categories.ForEach(x => {
<li>#x.Name</li>
})
But this doesn't work.
Can anyone suggest, Is there any way to achieve this in razor view?
Thanks in advance.

Is there any reason you need to do that?
#foreach(var x in Model.Categories) {
<li>#x.Name</li>
}
Above does the exact same thing, and is more idiomatic.
I can't see a way to output the .ForEach() delegate result using the Razor syntax. Razor expects called methods or invoked properties to return a value, which is then emitted into the view output. Because .ForEach() doesn't return anything, it doesn't know what to do with it:
Cannot explicitly convert type 'void' to 'object'
You can have the iterator index quite tersely like so:
#foreach (var item in Model.Categories.Select((cat, i) => new { Item = cat, Index = i })) {
<li>#x.Index - #x.Item.Name</li>
}
If you want to define this as an extension method, instead of an anonymous type, you can create a class to hold the Item, Index pair, and define an extension method on IEnumerable<T> which yields the items in the original enumerable wrapped in this construct.
public static IEnumerable<IndexedItem<T>> WithIndex<T>(this IEnumerable<T> input)
{
int i = 0;
foreach(T item in input)
yield return new IndexedItem<T> { Index = i++, Item = item };
}
The class:
public class IndexedItem<T>
{
public int Index { get; set; }
public T Item { get; set; }
}
Usage:
#foreach(var x in Model.Categories.WithIndex()) {
<li>#x.Index - #x.Item.Name</li>
}

Thanks for your help. I found how to implement delegates in Razor based on the following article by Phil Haack.
Templated Razor Delegates
Here is the extension code for IEnumerable:
public static HelperResult ForEachTemplate<TEntity>(
this IEnumerable<TEntity> items,
Func<RowEntity<TEntity>, HelperResult> template)
{
return new HelperResult(writer =>
{
var index = 0;
foreach (var item in items)
{
template(new RowEntity<TEntity> {
Index = index,
Value = item }).WriteTo(writer);
index++;
}
});
}
public class RowEntity<TEntity>
{
public int Index { get; set; }
public TEntity Value { get; set; }
}
View Model is :
// IEnumerable<Person>
public class Person
{
public string Name { get; set; }
}
And the use of that extension methods:
#Model.ForEachTemplate(#<p>Index: #item.Index Name: #Item.Value.Name</p>)

Related

Problem with checkbox helper returning null values ASP.NET MVC

Model:
public class ItemModel
{
public bool IsChecked { get; set; }
}
ViewModel:
public class CategoryItemViewModel
{
public List<ItemModel> Item { get; set; }
}
Index Controller:
public List<ItemModel> GetItemModel()
{
//Get fie path
var ItemFile = Server.MapPath("~/App_Data/Items.txt");
//Read from the Categories.txt file and display the contents in the List view
List<ItemModel> item = new List<ItemModel>();
//Read the contents of Category.txt file
string txtData = System.IO.File.ReadAllText(ItemFile);
//Execute a loop over the rows.
foreach (string row in txtData.Split('\n'))
{
if (!string.IsNullOrEmpty(row))
{
item.Add(new ItemModel
{
IsChecked = Convert.ToBoolean(row.Split(',')[0])
});
}
}
return item;
}
The code above is basically reading items in a text file and then setting them to the model.
I am having an issue when wanting to change the checked value of a checkbox, as the actual checkbox is returning a null value when I try check it, Problem code is below.
Controller to add a new line item with a checkbox:
[HttpPost]
public ActionResult Item(bool ItemCheck)
{
//Get file path of Categories.txt
var ItemFile = Server.MapPath("~/App_Data/Items.txt");
var ItemData = ItemCheck + Environment.NewLine;
System.IO.File.AppendAllText(ItemFile, ItemData);
return View();
}
bool ItemCheck is returning a null value.
Index View code:
foreach (var item in Model.Item)
{
#using (Html.BeginForm("Item", "Item", FormMethod.Post))
{
#Html.CheckBoxFor(ItemModel => ItemModel.IsChecked)
}
}
It is saying that ItemModel does not contain a definition for IsChecked.
This could be due to the following reasons
Referring wrong namespace for model, ItemModel. To ensure that you are using correct model use #model yourNamespace.ItemModel
Build issue. clean and build the project.

How to use ViewDataDictionary with Html.Partial in asp.net core?

My case looks like this:
Model:
public class Book
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Comment
{
public string Id { get; set; }
public string BookId { get; set; }
public string Content { get; set; }
}
Controller:
public IActionResult Detail(string id)
{
ViewData["DbContext"] = _context; // DbContext
var model = ... // book model
return View(model);
}
View:
Detail view:
#if (Model?.Count > 0)
{
var context = (ApplicationDbContext)ViewData["DbContext"];
IEnumerable<Comment> comments = context.Comments.Where(x => x.BookId == Model.Id);
#Html.Partial("_Comment", comments)
}
Comment partial view:
#model IEnumerable<Comment>
#if (Model?.Count > 0)
{
<!-- display comments here... -->
}
<-- How to get "BookId" here if Model is null? -->
I've tried this:
#Html.Partial("_Comment", comments, new ViewDataDictionary { { "BookId", Model.Id } })
Then
#{
string bookid = ViewData["BookId"]?.ToString() ?? "";
}
#if (Model?.Count() > 0)
{
<!-- display comments here... -->
}
<div id="#bookid">
other implements...
</div>
But error:
'ViewDataDictionary' does not contain a constructor that takes 0
arguments
When I select ViewDataDictionary and press F12, it hits to:
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
public ViewDataDictionary(IModelMetadataProvider metadataProvider, ModelStateDictionary modelState);
}
I don't know what are IModelMetadataProvider and ModelStateDictionary?
My goal: Send model comments from view Detail.cshtml to partial view _Comment.cshtml with a ViewDataDictionary which contains BookId.
My question: How can I do that?
Another way to use this is to pass the ViewData of the current view into the constructor. That way the new ViewDataDictionary gets extended with the items you put in using the collection initializer.
#Html.Partial("MyPartial", new ViewDataDictionary(ViewData) { { "BookId", Model.Id } })
Use the following code to create a ViewDataDictionary
new ViewDataDictionary(new Microsoft.AspNetCore.Mvc.ModelBinding.EmptyModelMetadataProvider(), new Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary()) { { "BookId", Model.Id } }
On .NET Core I use ViewDataDictionary with a parameter, like:
#Html.Partial("YourPartial", new ViewDataDictionary(ViewData) { { "BookId", Model.Id } })

How to bind bropdown list in Razor View (If view is not bound with any model)

Can anybody suggest me how bind a dropdown list in MVC Razor view. I am using MVC 4. I have a view that is not bound with any model class.
public class Util {
public List<EmployeeType> GetEmpTypes() {
return (new List<EmployeeType>(){
new EmployeeType(){ID=101, Text="Permanent"},
new EmployeeType(){ ID=102, Text="Temporary"}
});
}
}
public class EmployeeType {
public int ID { get; set; }
public string Text { get; set; }
}
I have this sample code. I am new to MVC Now after this I don't know how to bind the collection returned by GetEmployeeTypes() Method to a dropdown list
Your class with method
public class Util {
public List<EmployeeType> GetEmpTypes() {
return (new List<EmployeeType>(){
new EmployeeType(){ID=101, Text="Permanent"},
new EmployeeType(){ ID=102, Text="Temporary"}
});
}
}
Your model class with properties
public class EmployeeType {
public int ID { get; set; }
public string Text { get; set; }
}
This is sample action
public ActionResult ViewName()
{
Util xxx=new Util();
List<SelectList> SelectedItems =new List<SelectList>();
List<EmployeeType> items =xxx.GetEmpTypes();
foreach (var t in items )
{
SelectListItem s = new SelectListItem();
s.Text = t.Text;
s.Value = t.ID;
SelectedItems.Add(s);
}
ViewBag.xxxxx= SelectedItems;
return view();
}
In View
#Html.DropDownList("xxxxx", new SelectList(ViewBag.xxxxx, "Text", "Value"))
This above code just like a key, i don't tested for that code ran successfully. you can get some idea for how to bind dropdown from my code.
I had a Class like this to get all EmployeeTypes
public class Util
{
public List<EmployeeType> GetEmpTypes()
{
return (new List<EmployeeType>(){
new EmployeeType(){ID=101, Text="Permanent"},
new EmployeeType(){ ID=102, Text="Temporary"}
});
}
}
public class EmployeeType
{
public int ID { get; set; }
public string Text { get; set; }
}
In Controller I have written code to get the List of Employee Types
Util obj = new Util();
var v = obj.GetEmpTypes();
ViewBag.EmployeeTypes = v;
return View();
In the View I have written code to bind dropdown.
#Html.DropDownList("EmployeeTypes",new SelectList(ViewBag.EmployeeTypes,"ID","Text"));
Thanks #Ramesh Rajendran ( Now I understood the concept to bind dropdown)
*strong text*you should create the model selectlist like here:
public static List<EmployeeType> GetEmpTypes() {
return (new List<EmployeeType>(){
new EmployeeType(){ID=101, Text="Permanent"},
new EmployeeType(){ ID=102, Text="Temporary"}
});
}
public static SelectList GetMyEmpTypes
{
get { return new SelectList(GetEmpTypes(), "ID", "Text"); }
}
then you access this method in dropdown list like
#Html.DropDownList("Name",yourProjectNameSpace.Util.GetMyEmpTypes())
when you will submit your form then it value bidden with Name get post to controller.
it is not necessary to bind with model class.you can receive the value on controller with the name that you have given in view like:
#Html.DropDownList("Name",yourProjectNameSpace.YourClass.GetEmpTypes())
Now you can recive the name value at controller like:
public ActionResult test(String Name)
{
return view();
}
and make your method static i.e GetEmpTypes() so that you can access it from view.

View not found when a view model is specified

I have a peculiar problem with a partial view and associated view model.
This is what the relevant function in the controller looks like:
using MyProject.ViewModels;
[ChildActionOnly]
public PartialViewResult ShowMyView(int id)
{
return PartialView(new MyModel() { ModelID = id });
}
And then I have a view model defined as follows:
namespace MyProject.ViewModels
{
[Bind(Exclude = "ModelID")]
public class MyModel: IValidatableObject
{
public MyModel()
{
Count = 1;
}
[Required]
[HiddenInput(DisplayValue = false)]
public int ModelID { get; set; }
[Required]
[Range(1, 9999)]
public int Count { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (ModelID <= 0)
yield return new ValidationResult("Model ID missing",
new[] { "ModelID" });
if (Count <= 0)
yield return new ValidationResult("Count cannot be zero",
new[] { "Count" });
}
}
}
I have a view defined in Views/Shared called ShowMyView.cshtml.
The strange thing is that when I define it as
#inherits WebViewPage
it works fine (shows the view), but when I define the actual model as well
#inherits WebViewPage<MyProject.ViewModels.MyModel>
it will not show the view and give me the error that the view cannot be found (with a list of locations it looked in, including the Views/Shared/ShowMyView.cshtml path which does exist).
This seems to happen for whichever model class I use in the project. Any clues on what I'm doing wrong here?
Try replacing:
#inherits WebViewPage<MyProject.ViewModels.MyModel>
with:
#model MyProject.ViewModels.MyModel
in the top of your ShowMyView.cshtml view. Normally the two should be equivalent but you never know. Also make sure that when you are rendering this child action you are passing the id parameter:
#Html.Action("ShowMyView", "SomeController", new { id = "123" })

EditorFor + foreach + complex type

I have 2 classes:
public class Poll
{
//...
public virtual ICollection<Answer> Answers { get; set; }
}
public class Answer
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
private int _amount = 0;
public int Amount
{
get { return _amount; }
set { _amount = value; }
}
}
And I need to make an Editor View.
If I try this:
#foreach (var answer in Model.Answers)
{
#Html.EditorFor(model => answer)
}
It shows everything for editing, but doesnt save changes. Controller recieves poll.Answers = null
That is because you are bypassing the advanced ID/Name generation that editors do. Since you are rendering each editor separately without any context/scope metadata, the editor renderer simply renders itself as if the model passed to it were the top-level model.
Check this question for more info:
using Html.EditorFor with an IEnumerable<T>

Resources