I have asp.net MVC application which can retrieve data from database. It looks like this:
If my url is like this:
myurl.com/Home/Index/1
my view is like this: [https:// scontent-b-fra.xx.fbcdn.net/hphotos-xfp1/t31.0-8/10571949_782177481813619_1929089026797570629_o.jpg][1]
if url is like this :-
myurl.com/Home/Index/2
view is like this: https://fbcdn-sphotos-d-a.akamaihd.net/hphotos-ak-xpa1/t31.0-8/10519689_782177478480286_8967524557745769669_o.jpg
etc..
I'm passing and in url which is the id of data in my database :-
public ActionResult Index(int id)
{
BIOEntities db = new BIOEntities();
AddBio bio = new AddBio();
Biography biography = db.Biographies.Where(x => x.Id == id).FirstOrDefault();
bio.Id = biography.Id;
bio.Photo = biography.Photo;
bio.Name = biography.Name;
bio.Position = biography.Position;
return View(bio);
}
Now i want to make that if my url will
myurl.com/Home/Index
It must retrieve data like this: https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpa1/t31.0-8/10454247_782178191813548_1334099737795866885_o.jpg
anyone please explain me how can I do that
[1]: https://%20scontent-b-fra.xx.fbcdn.net/hphotos-xfp1/t31.0-8/10571949_782177481813619_1929089026797570629_o.jpg
and there's my view (Customhelper is for pictures..) :
#model BioMVC.Models.AddBio
#using BioMVC.CustomHtmlHelpers
#{
ViewBag.Title = "Index";
Layout = "~/Views/_layout.cshtml";
}
<div id="first">
<div class="display-field">
#Html.Image(#Model.Photo, new { #id = "FirstPhoto" })
</div>
<legend class="orangeText">#Model.Name</legend>
<legend class="orangeText">#Model.Position</legend>
</div>
<div id="second">
<div class="display-field">
#Html.Image(#Model.Photo, new { #id = "secondPhoto" })
</div>
<legend class="orangeText">#Model.Name</legend>
<legend class="orangeText">#Model.Position</legend>
</div>
<div id="third">
<div class="display-field">
#Html.Image(#Model.Photo, new { #id = "thirdPhoto" })
</div>
<legend class="orangeText">#Model.Name</legend>
<legend class="orangeText">#Model.Position</legend>
</div>
<div id="fourth">
<div class="display-field">
#Html.Image(#Model.Photo, new { #id = "fourthPhoto" })
</div>
<legend class="orangeText">#Model.Name</legend>
<legend class="orangeText">#Model.Position</legend>
</div>
george,
without seeing your current view code, i am assuming that it is something along the lines of:
#model yournamespace.models.AddBio
this will therefore only show a SINGLE instance as per the code in the current index action method. What you need to do is to make that model a List<AddBio> along the lines of:
#model List<yournamespace.models.AddBio>
you can then iterate round the list to populate as you had been before. You will also need to change your index method to reference a List<>, rather than a single entity (hint: removing the .FirstOrDefault() on the 3rd line is your starting point - the rest is all just common sense in passing back the List<> rather than a single instance).
hope this helps..
[edit] - as per stephen muecke's response, you should also test for nullable id -good point!!
Related
I'm working on asp.net MVC 5 project.I have two models Post and Comment having one to many relationship between them. In Post's Details view, I want to add comments in that Post. How can I do it without using any ViewModel? Only Post model is passed in the view. I want to access Comment and its properties by using Post model reference, after all they have one many relationship between them, Post model have Comment's reference.
Here is my code example
Details actions in Post's controller
[HttpPost]
public ActionResult Details(Comment comment, string post_id)
{
int id = int.Parse(post_id);
if (ModelState.IsValid)
{
var post = db.Posts.FirstOrDefault(u => u.id == id);
comment.post = post;
db.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(comment);
}
//
// GET: /Post/Details/5
public ActionResult Details(int id = 0)
{
Post post = db.Posts.Find(id);
if (post == null)
{
return HttpNotFound();
}
return View(post);
}
here is Details.schtml
#model Blog.Models.Post
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<fieldset>
<legend>Post</legend>
<div class="display-label">
#Html.DisplayNameFor(model => model.title)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.title)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.body)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.body)
</div>
#for (int i = 0; i <Model.comments.Count; i++)
{
<div class="display-label">
#Html.DisplayNameFor(model => model.comments[i].comment)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.comments[i].comment)
</div>
}
<h2>Comment</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Article</legend>
<div class="editor-label">
#Html.LabelFor(model =>model.comments[Model.comments.Count].comment)
</div>
<div class="editor-field">
#Html.EditorFor(model =>model.comments[Model.comments.Count].comment))
#Html.ValidationMessageFor(model => model.comments[Model.comments.Count].comment)
</div>
<p>
<input type="submit" value="Add" />
</p>
</fieldset>
}
</fieldset>
There is an error
"Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index"
how can I successfully add comments ?
The error itself pretty much explains the problem. On the line where you get the error the Model.comments.Count is out of the range. The indexation starts from 0 so the last comment should be retrieved this way .... model => model.comments[Model.comments.Count - 1].comment.
Your exception is generated by the following line in your view
#Html.LabelFor(model =>model.comments[Model.comments.Count].comment)
and will also be generated by the subsequent EditorFor() and ValidationMessageFor() methods.
Collection indexers are zero based, whereas .Count() returns the number of items in the collection. If say you collection contained 2 Comment, then you would access them as
model.Comments[0].comment
model.Comments[1].comment
but your [Model.comments.Count] equates to
model.Comments[2].comment
which throws the exception because your referring to the 3rd item in the collection (which does not exist). Even if you referred to an existing item in the collection, you code would fail because you would be posting back a collection (defined by the indexer) but you POST method only excepts a single object
It appears you wanting to add a new comment to the Post in which case you can either use a view model containing an additional property for Comment
public Comment NewComment { get; set; }
and then in you view, generate the form controls using
#Html.EditorFor(m => m.NewComment.comment)
and change the POST method signature to
public ActionResult Details([Bind(Prefix = "NewComment")]Comment comment, int post_id)
Note the use of the Bind.Prefix property which is necessary to strip the "NewComment" prefix from the name/value pair posted to the method. Note also that you can make the second parameter int and avoid the unnecessary conversion from string to int (although you do not appear to be generating a form control for property post_id so it will be null anyway).
An alternative would be to use #Html.Action() to call a child action method that returns a partial view (containing the form) based on a new Comment.
It is because of db.Posts.Find(id);. you have given initial value for id in public ActionResult Details(int id = 0). Check your code to be sure you always pass value to id.
Here is the context :
This is my first ASP.NET MVC web application.
On the categories list, I click on a category to see all infos about it in a new view.
From this view, I display its name, its description and the number of products in this category.
Below that, I want to display all products in this category.
I gave a try to the Html.Action() method.
But when I want to iterate through the foreach statement, Visual Studio tells me that Type 'System.Web.Mvc.MvcHtmlString' is not enumerable.
Here is my view (details about a category):
#model UlysseCMS.Models.category
#{
ViewBag.Title = "Details about "+ Model.category_name + " category";
var nbProducts = Html.Action("GetNumberOfProductByCategory", "Product", Model.category_id);
var productsList = Html.Action("GetListOfProductsByCategory", "Product", Model.category_id);
}
<div>
<span class="leader">
<span class="mif-chevron-thin-right fg-teal"></span>
<span>#Html.ActionLink("Categories", "Index")</span>
<span class="mif-chevron-thin-right fg-teal"></span>
<span>#Model.category_name</span>
<span class="mif-chevron-right fg-teal"></span>
<span>details</span>
</span>
</div>
<br />
<hr class="bg-teal" />
<br />
<div class="margin30">
<div class="flex-grid">
<div class="row cells7">
<div class="cell colspan3 offset1 sub-header">
Category name :
</div>
<div class="cell colspan4">
#Model.category_name
</div>
</div> <br/>
<div class="row cells7">
<div class="cell colspan3 offset1 sub-header">
Category description :
</div> <br/>
<div class="cell colspan4">
#Model.category_description
</div>
</div> <br/>
<div class="row cells7">
<div class="cell colspan3 offset1 sub-header">
Number of products in this category :
</div>
<div class="cell colspan4">
#nbProducts
</div>
</div>
</div>
</div>
#foreach (var item in productsList)
{
}
Here is my GetListOfProductsByCategory() method from the ProductController :
public IEnumerable<product> GetListOfProductsByCategory(int id)
{
return db.product.Where(x => x.product_category_id == id);
}
I still continue to find a solution with IEnumerable casting or something.
Thanks,
Hellcat.
The result of #Html.Action is a View.
When you have an action inside a controller, this action usually return a view, and that rendered view is result of Html.Action ,for displaying it you need to use an # before html.
You should write your code as below:
public ActionResult GetListOfProductsByCategory(int id)
{
return View(db.product.Where(x => x.product_category_id == id));
}
Then right click on View and from menu select Add View, then create a partial view.
inside partial view you can enumerate your list and create the rendered out put.
Finally wherever you want to show the rendered list, just call:
#Html.Action("GetListOfProductsByCategory",id)
When creating view, you need to select your model and view type as list so on top of your view you would have:
#model IEnumerable<product>
Then you must use you foreach as below:
#foreach (var item in Model)
{
}
Okay I modified my Partial View (GetListOfProductsByCategory) like that :
#model IEnumerable<UlysseCMS.Models.product>
#foreach (var item in Model)
{
<a href="#Url.Action("Details", new { id = item.product_id })">
<div class="tile tile-wide bg-white block-shadow margin30" data-role="tile">
<div class="tile-content slide-up-2">
<div class="slide fg-darkTeal">
<span class="sub-leader" style="padding-left: 5px;">#Html.DisplayFor(modelItem => item.product_name)</span>
</div>
</div>
</div>
</a>
}
And in my Category Details View I display the products like this :
#Html.Action("GetListOfProductsByCategory", "Product", Model.category_id)
It's now okay hanks to Mehrdad Babaki's answer.
I have a parameterless Index for the HttpGet which works. But when I post it the HttpPost version of Index is invoked and the viewmodel object is passed in, but there is only the value of the dropdown in it. The rest is null (products, title)
[HttpPost]
public ActionResult Index(ProductsViewModel pvm)
{
// breakpoint on line 36, shows that pvm.Title is null and Products too.
return View(pvm);
}
My compilable and running example can be downloaded from my OneDrive http://1drv.ms/1zSsMkr
My view:
#model KleinKloteProductOverzicht.Models.ProductsViewModel
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect"})
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#foreach (var item in Model.Products)
{
#Html.DisplayFor(i => item.Name);
}
</div>
}
}
If I have this view model:
public class ViewModel
{
public string Name {get;set;}
public string SelectedLocation {get;set;}
public IEnumerable<SelectListItem> Locations {get;set;}
}
And your actions look like this:
public ActionResult MyForm()
{
var vm = new ViewModel
{
Locations = context.Locations.ToList() // Some database call
}
return View(vm);
}
[HttpPost]
public ActionResult MyForm(ViewModel vm)
{
vm.Locations // this is null
}
It is null because the model binder can't find a form control that is setting its data.
The <form> must set some data in the view for the model binder to pick it up.
<form>
Name: <input type="text" id="name" />
</form>
This will set the Name property on the view model, because the model bind can see the id of the form control and uses that to know what to bind to.
So in terms of your view, you need to make sure you wrap any content that you want to post back to the server with #using(Html.BeginForm())
Anyway this is my guess.
Well, you seem to be confused as to how [HttpPost] and form tags interact with eachother.
You see, when .NET MVC binds your parameters in your controller actions, it tries to derive that data from the request. For [HttpGet] it does this by looking at the query string.
For [HttpPost] calls, it also looks at the Request.Form. This variable is populated with the values of all input fields that were inside the form you submitted.
Now, this is your view:
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect" })
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#foreach (var item in Model.Products)
{
#Html.DisplayFor(i => item.Name);
}
</div>
}
}
You only have one select tag (generated by Dropdownlistfor) but no other inputs. That's why .NET MVC cannot infer any other data for your view model.
If you change your view to this:
#model KleinKloteProductOverzicht.Models.ProductsViewModel
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect" })
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#for (var i = 0; i < Model.Products.Count; i++)
{
#Html.DisplayFor(model => model.Products[i].Name)
#Html.HiddenFor(model => model.Products[i].ID)
}
</div>
}
}
You'll see I've added a hidden input (<input type="hidden">) for the product id. Note that the product name still will be null.
I would suggest you follow a tutorial on .NET MVC and read up on some of the concepts behind it, because the very fact that you ask this question reveals that you have much to learn.
Best of luck!
P.S. One last tip: #Html.Blablabla writes directly to your view. You usually don't need that ";" at the end, because it will be inside your generated html.
Your property is not associated with a "postable" control, therefore it will not be submitted along with the form data. If your really want to get the value in your Title property, just set it as a hidden input.
#Html.HiddenFor(m => m.Title)
A label will not be posted when submitting a form but an input will. This is exactly what HiddenFor does; it creates a hidden input element which will be picked up by the form submit.
I'm writing an MVC app which ends up accessing a SQL database. On my edit page, I previously had every item available to be edited that is in the model. Recently I was asked to no longer allow the user to edit the primary keys. I did a quick change to change the primary key fields (in this example, there are 2 of them) from an EditorFor to a DisplayFor. The new view is this:
#model App.Data.Item
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Item</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<strong>ID:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID)</p>
</div>
</div>
<div class="form-group">
<strong>ID2:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID2)</p>
</div>
</div>
<div class="form-group">
<strong>Description:</strong>
<div class="col-md-10">
<p>#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)</p>
</div>
</div>
<br />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-primary">Submit <i class="fa fa-caret-right"></i></button>
</div>
</div>
</div>
}
It used to work with the full editing. Now the data is displayed properly, as expected. However, when submit is pressed, Null values are passed back to the controller for the values that are displayed.
These are the edit functions in my controller. ItemService.Edit() just saves the data to the server. It works correctly.
[Authorize]
public ActionResult Edit(string id)
{
if (id == null)
{
//return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string[] vals = id.Split('|');
ItemAttribute itemAttribute = itemAttributeService.Find(int.Parse(vals[0]), vals[1]);
if (itemAttribute == null)
{
return HttpNotFound();
}
return View(itemAttribute);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public ActionResult Edit([Bind(Include = "ID,ID2,Description")]
Item item)
{
if (item.Description == null)
{
ModelState.AddModelError("Description", "Description cannot be null.");
}
if (ModelState.IsValid)
{
itemService.Edit(item);
return RedirectToAction("../Home/Index/");
}
return View(item);
}
Lastly, my data model:
public class Item
{
public int ID { get; set; }
public string ID2 { get; set; }
public string Description { get; set; }
}
Why is the data no longer being passed back to the second function, and how do I get it to pass correctly so that I can save it to the database?
You need to have an input element generated for the items that you want returned. Currently, you are only displaying two of your model elements and have no associated input with them. As a result, they will not be POSTed back to the server.
To get them to post to the server and not "editable" from a textbox, add a Html.HiddenFor() helper for each of the items that you need returned.
<div class="form-group">
<strong>ID:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID)</p>
<p>#Html.HiddenFor(model => model.ID)</p>
</div>
</div>
<div class="form-group">
<strong>ID2:</strong>
<div class="col-md-10">
<p>#Html.DisplayFor(model => model.ID2)</p>
<p>#Html.HiddenFor(model => model.ID)</p>
</div>
</div>
However, keep in mind that anyone can edit the HTML using Firebug or Chrome console tools and submit any value that they want for any field, even if you did not want to change it. Be sure that when you are persisting the changes to the database, you are NOT including these fields as part of the update.
Try this, just before this line of code:
#Html.DisplayFor(model => model.ID)
put in this for debugging:
#Html.LabelFor(model => model.ID)
Tell us what you see...
If you see the label then check your controller, in particular the parameter it takes on the post. It should take and Item of type ITEM per your model.
Before the controller receives the data MVC has to try to populate the data... It converts name/value pairs to model types with values secretly. If you don't see any data after you are in the controller it's usually because the names were not found!
I just started working with MVC3 a few weeks ago and being young in the programming ladder I'm still learning quite a bit. I've recently been working with Models, using TextBoxFor and other helpers to populate properties for my "Character" Model.
Essentially what I'm trying to do is define a model and then pass it to my controller, however any property that I have defined as a static value in my Model is being passed as a null value on runtime.
Below are some snippets of the parts needed to understand whats going on..
Character.cs - Model
// Instances of manipulated objects.
otReal db = new otReal();
public player newPlayer = new player();
public byte[] conditions
{
get
{
return newPlayer.conditions;
}
set
{
byte[] array1 = null;
array1 = new byte[16 * 12 * 3];
newPlayer.conditions = array1;
}
}
CharacterController.cs
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(Character c)
{
// Add a new character to the database via LINQ to Entities.
otReal.AddToplayers(c.newPlayer);
otReal.players.AddObject(c.newPlayer);
otReal.SaveChanges();
return View();
}
The only helpers I have in my View are the ones that the user actually needs to interact with. If I go into my controller and set the values there they will get set to the correct value and it will insert. Any help would be greatly appreciated!
Index.cshtml - View
#using (Ajax.BeginForm("Submit", new AjaxOptions { OnComplete = "done" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>New Character Information</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name, "Character Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TownList)
</div>
<div class="editor-field">
#* #Html.DropDownList("TownList", ViewData["TownList"] as SelectList)*#
#Html.DropDownListFor(model => model.TownList, ViewData["TownList"] as SelectList)
#Html.ValidationMessageFor(model => model.TownList)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Sex)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Sex, ViewData["sex"] as SelectList)
#Html.ValidationMessageFor(model => model.Sex)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Vocation)
</div>
<div class="editor-field">
#Html.DropDownList("VocList", ViewData["VocList"] as SelectList)
#Html.ValidationMessageFor(model => model.Vocation)
</div>
<p>
<input id="button" type="submit" value="Create" />
</p>
<div style="font-family: Tahoma; font-size: large" id="completeDiv" style="display: none;">
</div>
<span></span>
</fieldset>
}
Basically what I'm trying to do here is create my model that has a bunch of base values that every 'Character' will have in the database table. This way when a player creates his/her character he/she will be able to enter a Character Name, Sex, Class(Vocation), Starting Town and all the other values will be populated behind the scenes and passed to the controller for creation.
Assuming you're trying to set values on 'newPlayer', then you can replace this
public player newPlayer = new player();
with this
public player newPlayer { get; set; }
The default model binder will create everything from the form on post-back--there's no need to instantiate it in the model.
Created a constructor for my model and inside the constructor I set the default values via the newly instantiated player model. Temp fix until I read into ataddeini's solution. Thanks everyone!