This is my View:
#model test2.Models.ChatModel
#{
ViewBag.Title = "Channel";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<center>
<h2>Channel: #Model.channelName</h2>
#{
foreach (string line in Model.chatLog) {
<div>#line</div>
}
}
<br />
#using (Html.BeginForm("sendMessage", "Home", FormMethod.Post)) {
#Html.TextBoxFor(model => model.message)
<button type="submit"> Send Message </button>
}
</center>
Here is my Controller:
public ActionResult sendMessage(ChatModel model) {
//send message somewhere
//this is not working
return RedirectToAction("Channel", "Home", new { channel = model.channelName });
//this is working
return RedirectToAction("Channel", "Home", new { channel = "test" });
}
The error happens in the redirectToAction method. Somehow "model.channelName" is empty, but #Model.channelName in my view is correctly displaying the channel name.
It looks like when you send a Model to a view, and "resend" this model back to a controller, the informations are lost.
Is there an easy way to solve this?
PS Step by step:
Model gets channelName
Model is send to view
View correctly displays data from model
adding message to Model
sending model to controller
model does NOT contain information from step 1
You need to include model.channelName in the form. Try adding a:
#Html.HiddenFor(model => model.channelName)
Anything not posted by the form, will be null in your model (including your chatlog)
Actually the values model properties should be rendered as input elements within the form that is posted back to controller action. The properties which are not included would loose their values.
What you can do is create a hidden field for those to post :
#using (Html.BeginForm("sendMessage", "Home", FormMethod.Post)) {
#Html.TextBoxFor(model => model.message)
#Html.HiddenFor(model => model.channelName)
<button type="submit"> Send Message </button>
}
You would need to add same way other properties too that are posting null at action and you need those for some processing.
Hope it helps.
Related
I have 2 ASP.NET MVC action methods, I call the first method by passing and load some initial data, then I get some additional details from UI and call the second action method (Post action method from .cshtml). The data I received from the first call is missing in the post method. can anyone help me what am I doing wrong or missing here?
Action methods:
[Route("setprofile")]
public ActionResult SetProfile(string id)
{
ProfileData data = new ProfileData();
//do something
data.name= getData(id);
return this.View(data);
}
[Route("setprofile")]
[HttpPost]
public ActionResult SetProfile(ProfileData data)
{
// Here I'm not missing the data.name field value
}
View .cshtml file:
<div class="panel-body">
#using (Html.BeginForm("SetProfile", "Home", FormMethod.Post))
{
<div>
<h3> Name: #(this.Model.name)</h3>
</div>
<h3>
Comments:#Html.TextBoxFor(m => m.comments)
</h3>
}
I get the comments value but not getting the name field value from the model here.
Note: I need to display the value I received from the first action method as a label, not text box.
There are two things, Name is writen as text and in order to send back to server, you need to put it inside input element.
IF you dont want to show it #Html.HiddenFor(m => m.name) creates hidden input element.
Other than this, check ModelState for validation errors..
if (!ModelState.IsValid)
return BadRequest(ModelState);
.... your code here
if your model is not valid, the ProfileData returns result
You haven't added an input element for it to be sent back to the server when the form is submitted. If you don't want it to be visible, whilst still being posted back, add a hidden field for it:
#Html.HiddenFor(m => m.name)
Without that, all you're doing is rendering name to the markup but, once the form is submitted, it won't be sent back. Alternatively, you could render a textbox for the value whilst setting its readonly attribute. That would allow it to be visible, not changed, and still be sent back to the server.
#Html.TextBoxFor(m => m.name, new { #readonly = "readonly" })
What I want to do
I am very new to MVC.
I'm trying to create a page that allows users to perform the following actions on the same page:
View the list (table)
Add a new item (Filling the form and clicking the Add button should update the table)
Delete an item from the list (Clicking the Delete button in a row should update the table)
A simple example looks like this but I actually have two lists on one page (Fees and Costs):
Question
What would be the best way to achieve this?
Should I go with Dylan Beattie's method posted here which would look something like this?
public ActionResult MyAction(string submitButton, MyViewModel form)
{
switch (submitButton)
{
case "AddFee":
return (AddFee(form));
case "AddCost":
return (AddCost(form));
case "RemoveFee":
return (RemoveFee(form));
case "RemoveCost":
return (RemoveCost(form));
}
}
public ActionResult AddFee(MyViewModel form)
{
Fee newFee = ....; // Get entered data from `form`
_repository.InsertFee(newFee);
return View("Create"); //Back to the original page
}
Or is there any other recommended methods to handle this such as using JavaScript?
You could create the table as a partial view and re render this via ajax.
Wrap the partial view in a div and Wrap the form in #using (Ajax.BeginForm(.... and target the wrapper div. Your controller action that is targeted by the ajax request will need to return a partial view.
Here is a simple example
public class HomeController : Controller
{
public ActionResult Index()
{
MYvm vm = new MYvm() { id = 1, name = "This is my View Model" };
return View(vm);
}
public ActionResult DA(MYvm vm)
{
vm.name = "CHANGED";
return PartialView("Part", vm);
}
View:
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "Home Page";
}
#using (Ajax.BeginForm("DA", "Home", new AjaxOptions() { UpdateTargetId = "cont", HttpMethod = "Get" }))
{
<div>
Id: #Html.EditorFor(model => model.id)
</div>
<div>
Name: #Html.EditorFor(model => model.name)
</div>
<input type="submit" value="SubmitForm" />
}
<div id="cont">
#{Html.RenderPartial("part", Model);}
</div>
Partial View
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "part";
}
<h2>part</h2>
#Model.name
Should I go with [previous SO answer]
No. That answer was for a different scenario where the question had a form with two submit buttons that wanted to do two different actions (and wasn't even the accepted answer to that question).
Your sample screenshot indicates that some javascript/jquery and ajax would solve the issue cleanly.
As you're new to MVC, try to keep it relatively simple. Break up the page into separate parts:
the containing page
the edit form
the list with remove
the edit/list work independently and should be written in a way that they could be put on any other page - the page is just there to contain them and doesn't do much else (obviously your real page will contain more, but add those parts as separate components as well).
1 Create actions for your list and edit forms that return partialviews - just the parts that are needed for that view (self-contained)
controller:
[HttpGet]
public ActionResult AddCost()
{
var model = new Cost();
return PartialView(model);
}
[HttpPost]
public void AddCost(Cost model)
{
if (ModelState.IsValid) {
db.SaveCost(model);...
}
}
form Views/Home/AddCost.cshtml:
#using (Ajax.BeginForm(...
{
<div class='editor-label'>#Html.LabelFor(model=>model.Description)</div>
...etc...
}
I'll leave you to set the Ajax.BeginForm properties. But make sure the on-success calls reloadCostList() (see below)
controller
public ActionResult CostList()
{
var model = db.loadCosts(); ...
return PartialView(model);
}
list, Views/Home/CostList.cshtml
#model IEnumerable<ViewModels.Cost>
<table>
<thead>
<tr>
<th>Cost Description</th>
...
<tbody>
#foreach (var cost in Model.Costs)
{
<tr data-id='#cost.Id'>
<td>#Html.DisplayFor(x=>cost.Description)</td>
...
<td><a href='#' class='remove-button'>Remove</a></td>
}
...
2 Create an action + view for the main page with placeholder for the form and calls the list partial-action, eg:
<div id="body">
<div id="formWrapper">
#Html.Action("AddCost")
</div>
<div id="listWrapper">
#Html.Action("ListView")
</div>
</div>
if you already load the data for the page, you can pass it directly to the partial, but there's no need:
#Html.Partial("ListView", Model.Costs)
this allows you to refresh the list via an ajax call, something like:
function reloadCostList() {
$(".listWrapper").load("Home/CostList");
}
(ideally, $.ajax and add some fancy UI to indicate loading)
3 Add a remove action to your controller
[HttpPost]
public void RemoveCost(int id)
{
}
4 Wire up the Remove link
$(function() {
$(".remove-button").click(function() {
var id = $(this).closest("tr").attr("id");
$.post("/Home/RemoveCost/" + id, null, function() {
$(".listWrapper").load("Home/CostList");
// or reloadCostList(); from above
// or:
//$(".listWrapper tr[id=" + id + "]").hide();
});
});
}
rather than re-load the entire list, you could just remove the row (add some fancy UI like fade-out...)
I have a page which represents data from ICollection<> model, it generates #Html.BeginForm() for each item in ICollection<> and shows data with #Html.Labels, and I want to create a link from each form to item details, so it will be like, when I press form with item with id=4, it sends Model.ElementAt(4) as a model to new page, and display it. How can I do that?
EDIT: I guess I need to add something like #Html.ActionLink("DetailsPage","Shops",shop)
#using WebShops
#model ICollection<WebShops.Shop>
#foreach (Shop shop in Model)
{
using (Html.BeginForm())
{
#Html.Label(shop.name)
#Html.Display(shop.name)
<br />
#Html.Label(shop.image)
#Html.Display(shop.image)
<hr />
}
}
To display a specific item there is no need for Html.BeginForm because it makes a POST request and you need to make a GET request.
You need to create a new Action that will make use of GET request.
public ActionResult Shop(string id)
{
var shop = //get shop by id from database
return View(shop)
}
You call the new action like below.
#using WebShops
#model ICollection<WebShops.Shop>
#foreach (Shop shop in Model)
{
#Html.Label(shop.name)
#Html.Display(shop.name)
<br />
#Html.Label(shop.image)
#Html.Display(shop.image)
<hr />
#Html.ActionLink("Display", "Shop","ControllerName", new {id = shop.id})
}
You can do that using the object routeValues from this overload of Html.ActionLink:
#Html.ActionLink("DetailsPage","Shops", new { id = shop.ID })
This doesn't "send the model to the new page", it makes a link to Shops/DetailsPage/4, causing GET request when clicked.
So in the DetailsPage action method you'll have to look up the shop on ID again in order to display it.
I'm currently studying asp.net mvc and I just started, I decided to move away from web forms to mvc.
I'm just curious because I have this code and I want to know the difference between passing the model in the return View(data) and not passing it.
Here's the code:
The Controller
/* Even if I comment/remove the lines ViewBag.GenreId....... and ViewBag.ArtistId
and just return View(); everything seems to work fine. I'm following this music store tutorial from codeplex
*/
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
//this will assign the values of the dropdownlist of the View
//it will assign the values on the dropdownlist that has a name GenreId
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
The Code for the View
#model CodeplexMvcMusicStore.Models.Album
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<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>Album</legend>
<div class="editor-label">
#Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
#Html.DropDownList("GenreId", String.Empty)
#Html.ValidationMessageFor(model => model.GenreId)
</div>
I would also like to know the difference between passing in the object model in View(album) vs not passing it View().
As far as I am aware, if you do not pass the model through then your page will not be populated.
Also it will not know where to bind the values back to when you post the form back.
If you dont pass the data then you cannot access the data in the razor. You need to pass your model to the return View(model) in order to use it on the Razor View. If you need to pass more than one model then you can pass you use either ViewBag or ViewData to do this.
By looking at your question. It seems to me you might be able to find your answer in this MVC DropDownListFor tutorial
If you do not pass object model in return View(album) it will not show any validation errors in your view if there are any. As you are using ViewBag for GenreId and ArtistId you can render in view without passing the object model to view (return View()) posted by Karthik
I have a basic MVC form which is accessed through a GET Action with 3 string parameters pulled from the route.
[HttpGet]
public ActionResult Application(string x, string y, string z)
{
//create model, setup form, etc...
return View(model);
}
The route to access this form is configured as follows:
routes.MapRoute("Application",
"application/{x}/{y}/{z}",
new
{
controller = "Application",
action = "Application",
x = "",
y = "",
z = ""
});
And the form is configured as follows:
Html.BeginForm("Application", "Application", FormMethod.Post)
All of this works until I click submit on the resulting form. From a routing perspective the correct POST Action is called and the Model is bound correctly. The problem is I have lost all the x/y/z route information which I still need. How can I preserve the route information?
I've tried a couple things:
Added route details to the Form in Hidden fields which are added to the form content correctly but never get returned in the Model on postback
Tried using the RouteValueDictionary overload for Html.BeginForm but can't figure out how to make it work. I may just need a proper example of how to use it and how to access the state from the Controller
Update: This adds View sample to help address comments made regarding the use of Hidden Fields
#using (Html.BeginForm("Application", "Application", FormMethod.Post, new
{
autocomplete = "on",
id = "LoanApplication",
novalidate = string.Empty,
name = "Application"
}))
{
<fieldset>
#Html.HiddenFor(m => m.x)
#Html.HiddenFor(m => m.y)
#Html.HiddenFor(m => m.z)
#Html.HiddenFor(m => m.DigiCertId)
<br />
<input id="SubmitButton" type="submit" value="#Resources.SubmitApplicationButton" title="#Resources.SubmitApplicationButtonDescription" />
</fieldset>
}
You should really put these properties in the model, then have a HiddenFor for each one, like so:
#Html.HiddenFor(m => m.x)
#Html.HiddenFor(m => m.y)
#Html.HiddenFor(m => m.z)
Then in your post method (assuming it's like this), you can pass them in the RouteValueDictionary to the Get Method, like so:
[HttpPost]
public ActionResult Application(MyModel model) //whatever type your model is
{
//do whatever your post method does before redirect
return RedirectToAction("Application", new { x = model.x, y = model.y, z = model.z});
}
in addition to the ans suggested by mattytommo. i would recommend using TempData collection in asp.net MVC. this saves the data using the session storage but does it temprorarily and the data gets deleted once you access it.
this is present specifically for these purposes
so you can save the data in TempData as TempData["Values"] = new {X=x,Y=y,Z=z};
then access the TempData["Values"] after the post