Getting ViewModel Data in my MVC View - c#

I am sure this is relatively simple, I just keep running into brick walls. I have two entity classes set up like so:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
public string Content { get; set; }
public string Tags { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
And I set up my ViewModel like this:
public class PostCommentViewModel
{
public Post Post { get; set; }
public IQueryable<Comment> Comment { get; set; }
public PostCommentViewModel(int postId)
{
var db = new BlogContext();
Post = db.Posts.First(x => x.Id == postId);
Comment = db.Comments;
}
}
And I have my Controller doing this:
public ActionResult Details(int id = 0)
{
var viewModel = new PostCommentViewModel(id);
return View(viewModel);
}
And then the view looks like this:
#model CodeFirstBlog.ViewModels.PostCommentViewModel
<fieldset>
<legend>PostCommentViewModel</legend>
#Html.DisplayFor(x => x.Post.Title)
<br />
#Html.DisplayFor(x => x.Post.Content)
<br />
#Html.DisplayFor(x => x.Post.CreatedDate)
<hr />
#Html.DisplayFor(x => x.Comment)
</fieldset>
The result IS displaying data, but not quite what I want for the comments.
You see that the comments (There are two of them) and just showing the id property on each one "12"
How can I get it to go into and display the comment details specific to this particular post? I imagine a foreach loop is in order, but i cant figure out how to drill into the Model.Comment property correctly.
I tried this:
#foreach(var item in Model.Comment)
{
#Html.DisplayFor(item.DisplayName)
#Html.DisplayFor(item.Content)
#Html.DisplayFor(item.DateCreated)
}
But the error I get is "The type arguments for method 'System.Web.Mvc.Html.DisplayExtensions.DisplayFor(System.Web.Mvc.HtmlHelper, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly."
Not sure what I am supposed to do here..

Loop around the comments model:
#model CodeFirstBlog.ViewModels.PostCommentViewModel
<fieldset>
<legend>PostCommentViewModel</legend>
#Html.DisplayFor(x => x.Post.Title)
<br />
#Html.DisplayFor(x => x.Post.Content)
<br />
#Html.DisplayFor(x => x.Post.CreatedDate)
<hr />
#foreach(var comment in Model.Comment) {
#Html.DisplayFor(x => comment)
}
</fieldset>

public class PostCommentViewModel
{
public Post Post { get; set; }
public IQueryable<Comment> Comment { get; set; }
public PostCommentViewModel(int postId)
{
var db = new BlogContext();
Post = db.Posts.First(x => x.Id == postId);
Comment = Post.Comments;
}
}
And did you make Html helper method for Comment class?

Create a DisplayTemplate for your Comment class and it should work, there is no need to iterate over the collection the view engine does it for you.
As per my comment, this is all you need to do to create a display template for your Comment class:
Create a DisplayTemplates subfolder inside your view folder and inside this create a new partial view called Comment.cshtml. Your template could look like this:
#model CodeFirstBlog.ViewModels.Comment
<div class="comment">
#Html.DisplayFor(m => m.DisplayName)
....
</div>
And that's it! If you need more control over how your comment is displayed then you can simple tweak the template to suit.

Related

.NET MVC showing a variable value

I have the this set up and am trying to get lkup_1_txt to show in the page as either text or the selected item in the dropdown.
I have tried the following
#lkup1
and
#Html.DisplayFor(modelItem => item.lkup1)
and
#foreach (var item in Model)
{#Html.DisplayFor(modelItem => item.lkup1)}
This is my structure -
Model for Look Up Reference drop down selection
public class ListItem
{
public string Value { get; set; }
public string Text { get; set; }
}
public class ModelForDropDown
{
public string SelectedItemText { get; set; }
public List<ListItem> ItemListText { get; set; }
}
public class LkupResultRecord
{
public ModelForDropDown lkup_1_txt { get; set; }
public ModelForDropDown lkup_2_txt { get; set; }
}
Controller for the create new lkup1txt dropdown -
public ActionResult LkupRef_Lkup1()
{
return View(lkuprefdao.getValuesForLkupTxtDropDown());
}
[HttpPost]
public ActionResult LkupRef_Lkup1(string lkup_1_txt)
{ return RedirectToAction("LkupRef_Lkup2", "LkupRef", new
{
lkup_1_txt = lkup_1_txt
});
}
Controller for the create new lkup2txt dropdown -
public ActionResult LkupRef_Lkup2()
{
if (lkuprefdao.IsDataRefreshDowntime() == true) { return RedirectToAction("BadgerWeb_RestrictedAccess", "LkupRef"); }
return View(lkuprefdao.getValuesForLkupTxtDropDown());
}
and the view where I want the lkup_1_txt to display -
#using (Html.BeginForm())
{
<table>
<tr>
<td>lkup_1_txt</td>
<td> #Html.DisplayFor(lkup_1_txt) </td>
</tr>
<tr>
<td> lkup_2_txt </td>
<td>#Html.DropDownList("lkup_2_txt", new SelectList(Model.lkup_2_txt.ItemListText, "Value", "Text"), null, new { style = "width: 650px;" })</td>
</tr>
</table>
}
I
It's all still a bit unclear and doesn't make a great deal of sense, but I think you probably want something like this:
1) define lkup_1_txt as a string in your model, to match with the querystring value you're trying to feed to it. Defining it as a complex type like ModelForDropDown (as you did) doesn't appear to make any sense.
public class LkupResultRecord
{
public string lkup_1_txt { get; set; }
public ModelForDropDown lkup_2_txt { get; set; }
}
2) Add a parameter to allow the LkupRef_Lkup2() action method to receive the input value from the redirect (or from a direct request). Right now it is just ignoring the input. And also modify the action method code so you can add the incoming lookup text to the model before passing it to the view.
public ActionResult LkupRef_Lkup2(string lkup_1_txt)
{
if (lkuprefdao.IsDataRefreshDowntime() == true) { return RedirectToAction("BadgerWeb_RestrictedAccess", "LkupRef"); }
LkupResultRecord model = lkuprefdao.getValuesForLkupTxtDropDown();
model.lkup_1_txt = lkup_1_txt; //add the lookup text to the model
return View(model);
}
3) Modify the view so it can read the lookup text from the model property:
<td>lkup_1_txt</td>
<td> #Html.DisplayFor(m => m.lkup_1_txt) </td>

How to render a model with list of objects in view as a form? [duplicate]

This question already has answers here:
Asp.Net MVC: Why is my view passing NULL models back to my controller?
(2 answers)
Closed 5 years ago.
I need to display a specific form in a view with a model. The model is like that:
This is the final object filled what I need :
public class ObjetTotal
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Numero { get; set; }
public string Value { get; set; }
}
I choose to cut the form into two differents parts :
First a "static" part where the user can put common values for the differents ObjetsTotal.
Second a "variable" part where the user put differents values for the differents ObjetsTotal.
The final aim is that the user doest'n have to type, the same thing for all the objects ObjetTotal.
So, I create other objects (I don't know if it's a good practice) which represents the differents part of the form.
The static part with MainObjet and the variable part with Numbers. I put these two object into an other object "Mix" which contains one "MainObjet" and a list of "Numbers".
public class MainObjet
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Numbers
{
public string Numero { get; set; }
public string Value { get; set; }
}
public class Mix
{
public MainObjet obj { get; set; }
public IEnumerable<Numbers> num { get; set; }
public Mix()
{
obj = new MainObjet();
num = new List<Numbers>();
}
}
Then I want to render the model Mix in a view to have the two parts of the form.
I've try this :
#model App.Models.Mix
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Mix</legend>
<h3>First Properties</h3>
<div>
#Html.TextBoxFor(model => model.obj.Id);
#Html.TextBoxFor(model => model.obj.Name);
#Html.TextBoxFor(model => model.obj.Description);
</div>
<div>
<table>
#for (int i = 0; i < 5; i++)
{
<tr>
<td>
#Html.TextBoxFor(model => model.num[i].Numero)
</td>
<td>
#Html.TextBoxFor(model => model.num[i].Value)
</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
But after the submit I get an object Mix null in this ActionResult :
[HttpPost]
public ActionResult Test(Mix obj)
{
return View();
}
Can you explain me how to do that ? May I'm on a wrong way.
Don't consider the design of the form, and I don't know the right type to put to Numbers, Maybe a simple list be enough for that.
The thing I can see is that you are missing the initialization of your model properties in the parameter-less constructor. You should try to update your model code to be:
public class Mix
{
public MainObjet obj { get; set; }
public IEnumerable<Numbers> num { get; set; }
public Mix()
{
obj = new MainObjet();
num = new List<Numbers>();
}
}
As the model binder will instantiate your model, and it will find obj and num to null and will not be able to post the values back.
Hope this helps you.

Pass parameter from Htmldropdownlistfor to controller

I have a model passed from controller to view in my asp.net mvc5 website. Then I show the dropdownlist using the model and I want to pass an id back when submitting the form. Here is my model :
public class SiteDirectionModel
{
public int id { get; set; }
public string name { get; set; }
}
Then in the model, I use a List<SiteDirectionModel> to which I add new instances of each item I need. I fill up both these lists and then pass my model to the view.
#model List<SiteDirectionModel>
#using (Html.BeginForm("GetSiteRF", "Create", FormMethod.Post))
{
#Html.DropDownListFor(x => x.name,new SelectList(Model.name,"Sites"));
<input type="button" value="Selectionner" class="btn btn-primary"/>
}
Then how to retrieve the ids for each name ? And how to pass it as a parameter to my controller? Such that I would have :
public ActionResult GetSiteRF(int id)
{
int newId = id;
//Call method to searchId ...
return View("CreateADUser");
}
I have given how to bind and get value from dropdown. Please use your own BL in this.
Your model should be like this.
public class Something
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SiteDirectionModel
{
public SelectList MyDropDown { get; set; }
public int SelectedValue { get; set; }
}
You BL should be like this.
public List<Something> GetListofSomething()
{
//your logic.
}
Your Get method should be like this.
public ActionResult MyGetMethod()
{
SiteDirectionModel model = new SiteDirectionModel();
model.MyDropDown = new SelectList(GetListofSomething(), "key_field_name", "value_field_name", "default_value");
}
Then finally HTML
#Html.DropDownListFor(x => x.SelectedValue,Model.MyDropDown)

Search method by string value MVC 5

A a part of my project i need to find a way to search my object by a string and show a result in view. Your help is appreciated.
in my MainMedia view i have a sidesection were i manually pass a string value to a SearchMedia method:
#section SideBar{
<ul>
<li> #Html.ActionLink("Astronomy", "SearchMedia", new {searchString = "Astronomy" })</li>
<li> #Html.ActionLink("World", "SearchMedia", new { searchString = "World" })</li>
<li> #Html.ActionLink("Movies", "SearchMedia", new { searchString = "Movies" })</li>
</ul>
}
This method should check every object if TagsEnum string and then display an object in SearchMedia view.
Here is my Media class
public class Media
{
public int Id { get; set; }
public string title { get; set; }
public string description { get; set; }
public string body { get; set; }
public string ImagePath { get; set; }
public string VideoLink { get; set; }
public string Source { get; set; }
public string tags { get; set; }
public TagsEnum TagsEnum { get; set; }
}
TagsEnum Class
public enum TagsEnum
{
[Display(Name = "Astronomy and space")]
Astronomy,
[Display(Name = "World around us")]
World,
[Display(Name = "Movies, video")]
Movies
}
and finaly MediaMainController SearchMedia method
public ActionResult SearchMedia(string searchString)
{
db.Medias.Where(i => i.TagsEnum.ToString() == searchString);
return View(db.Medias.OrderBy(it => it.Title));
}
As i understand .Where() should find a match and return an object, however it is not working. How i can sort it out? Perhaps there are other ways to do it? Thank you
Update
I have changed it like this:
var result = db.Medias.Where(TagsEnum => TagsEnum.ToString() == searchString);
return View(result.OrderBy(it => it.title));
but i still dont see the results to be sorted by search
Update 2
I have a class MediaViewModel which i use to create a list of objects, it looks like this:
public class MediaViewModel
{
public List<Media> media { get; set; }
public List<Video> video { get; set; }
}
If i set up SearchMedia View like this
#model PhClub.Models.MediaViewModel
#foreach (var b in Model.media)
{}
i'm getting an error:
The model item passed into the dictionary is of type System.Linq.Enumerable+WhereListIterator 1[PhClub.Models.Media], but this dictionary requires a model item of type PhClub.Models.MediaViewModel.
If i set it up as
`#model IEnumerable<PhClub.Models.Media>
#foreach (var b in Model)
{}`
it is saying Values of type 'Media' can not be converted to string.
I think i need to change SearchMedia method to support MediaView class, but i didnt figure it out yet. Help is appreciated
You should assign it to a variable and use it,
var result = db.Medias.Where(i => i.TagsEnum.ToString() == searchString);
return View(result.OrderBy(it => it.Title));

MVC Add and remove items from subcollection

I'm having trouble binding data to a collection item's collection (I'm also having trouble wording my problem correctly). Let's just make thing easier on everyone by using an example with psudo models.
Lets say I have the following example models:
public class Month()
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Week> Weeks { get; set; }
}
public class Week()
{
public int ID { get; set; }
public int MonthID { get; set; }
public String Name { get; set; }
public virtual ICollection<Day> Days { get; set; }
}
public class Day()
{
public int ID { get; set; }
public String Name { get; set; }
}
...and an example viewmodel:
public class EditMonthViewModel()
{
public Month Month { get; set; }
public List<Week> Weeks { get; set; }
public List<Day> AllDays { get; set; }
}
The purpose of the Edit Action/View is to enable users to edit a month, the weeks assigned to the month, and add and remove days from weeks of a certain month. A view might help.
#model myProject.ViewModels.EditMonthViewModel
//...
#using (Html.BeginForm())
{
//Edit Month Stuff...
#for(int i = 0; i < Model.Weeks.Count(); i++)
{
<h2>#Model.Weeks[i].Name</h2>
#Html.EditorFor(model => Model.Weeks[i].Name)
//loop through all possible days
//Select only days that are assigned to Week[i]
#for(int d = 0; d < Model.AllDays.Count(); d ++)
{
//This is the focus of this question.
//How do you bind the data here?
<input type="checkbox"
name="I have no idea"
#Html.Raw(Model.Weeks[i].Days.Contains(Model.AllDays[d]) ? "checked" : "") />
}
}
}
Controller Action methods
public ActionResult Edit(int id)
{
var viewModel = new EditMonthViewModel();
viewModel.Month = db.Months.Find(id);
viewModel.Weeks = db.Weeks.Where(w => w.MonthID == id).ToList();
viewModel.AllDays = db.Days.ToList();
}
[HttpPost]
public ActionResult Edit(EditMonthViewModel viewModel)
{
var monthToUpdate = db.Months.Find(viewModel.Month.ID);
//...
if(viewModel.Weeks != null)
{
foreach (var week in viewModel.Weeks)
{
var weekToUpdate = monthToUpdate.Weeks.Single(w => w.ID == week.ID);
//...
/*So, I have a collection of weeks that I can grab,
but how do I know what was selected? My viewModel only has a
list of AllDays, not the days selected for Week[i]
*/
}
}
How can I ensure that when I submit the form the selected days will bind to the week?
It looks like the easiest thing to do is to make it a goal for your form to populate a data structure of the type IEnumerable<DayModel>, where DayModel is defined as:
public class DayModel
{
public int WeekId { get; set; }
public int DayId { get; set; }
public bool IsIncluded { get; set; }
}
You could keep your Razor code as is for the most part, but then when it comes to rendering the checkboxes, you can do something like this:
#{
var modelIdx = 0;
}
// ...
<input type="hidden" name="days[#modelIdx].WeekId" value="#Model.Weeks[i].Id" />
<input type="hidden" name="days[#modelIdx].DayId" value="#Model.AllDays[d].Id" />
<input type="checkbox" name="days[#modelIdx].IsIncluded" value="#(Model.Weeks[i].Days.Contains(Model.AllDays[d]) ? "checked" : "")" />
#{ modelIdx++; }
Then, your controller action you post to could have this signature:
[HttpPost]
public ActionResult Edit(IEnumerable<DayModel> days)
{
//...
}
Something that helps me is to never confuse view models, which should only be used for the model for views (GET actions generally) and non-view models (what we call plain models). Avoid having your POST actions try to bind to view models, and it will simplify your life greatly.

Categories

Resources