I have some viewModels similar to these:
public class ResturantViewModel
{
public ResturantViewModel()
{
Clients.Add(new ClientViewModel());
}
public string MyProperty {get;set;}
public IList<ClientViewModel> Clients = new List<ClientViewModel>();
}
public class ClientViewModel
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
In my View I have something like:
#foreach(var client in Model.Clients)
{
<tr>
<td>First Name: #Html.EditorFor(item => client.FirstName)</td>
<td>Last Name: #Html.EditorFor(item => client.LastName)</td>
</tr>
}
I'd like to have a button which can add some new blank ClientViewModels to the ResturantViewModel.Clients list so that it could be rendered in the View and posted to the Resturant/Create action.
Thanks in advance
You may take a look at the following blog post. It uses the WebForms view engine but it could be very easily adapted to Razor.
You need to implement a list of objects inside your view
<input type="text" name='Clients[#index].FirstName' value='#c.FirstName' />
<input type="text" name='Clients[#index].LastName' value='#c.LastName' />
#index++;
after that you have to clone these fields with next index value, so you have to got inputs like these:
<input type="text" name='Clients[0].FirstName' value='#c.FirstName' />
<input type="text" name='Clients[0].LastName' value='#c.LastName' />
<input type="text" name='Clients[1].FirstName' value='#c.FirstName' />
<input type="text" name='Clients[1].LastName' value='#c.LastName' />
<input type="text" name='Clients[2].FirstName' value='#c.FirstName' />
<input type="text" name='Clients[2].LastName' value='#c.LastName' />
In controller you will accept a list of these objects:
List<Client> Clients
Ok thank to you all here is what I've done to resolve.
first I created a new partial View _ClientRow.cshtml:
#using Extensions.CollectionItemsHtmlExtension
#using ClientViewModel
#model ClientViewModel
<div class = "clientEditRow">
#using(Html.BeginCollectionItem("Clients"))
{
#First Name: #Html.TextBoxFor(c=>c.FirstName)
#Last Name: #Html.TextBoxFor(c=>c.LastName)
}
</div>
This partial view renders a new line for a client.
BeginCollectionItem is an Html Extension downloaded following the blog post Darin mentioned.
Then in my view I set:
<legend>Clients</legend>
<fieldset>
<div id="clientEditorRows">
#foreach(var client in Model.Clients)
{
Html.RenderPartial("_ClientRow", client);
}
</div>
#Html.ActionLink("New Client", "NewClientRow", null, new {id = "addItem"})
</fieldset>
...
<script type="text/javascript" scr="../Scripts/listEditor.js"></script>
the foreach loops through all the clients and renders the partial view for every client.
Then in my Controller I wrote this method:
public PartialViewResult NewClientRow()
{
return PartialView("_ClientRow", new ClientViewModel())
}
This method is called by the ActionLink and returns the html for a new line appending it to the previous lines.
Eventually I added this javascript file code from the blog post and modified it to my case:
listEditor.js
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#clientEditorRows").append(html); }
});
return false;
});
This js code appends the html for the new line to the page.
Hope this can help, thank you all again.
alex
P.S.: the HttpPost method receiving the values hasn't been modified and has this signature
[HttpPost]
public ActionResult Create(ResturantViewModel resturantVM)
{
...
}
to note that resturantVM.Clients receives all the values, no need to add a IList Clients parameter
Related
First off I am learning Asp.net core. I have a View as follows, when I the blogItemId I retrieve an item and send it as a model to the DetailedView.cshtml as follows:
[HttpGet]
public IActionResult DetailedView(int blogItemId)
{
var item = _mainBlogHelperModel.BlogDataItems.FirstOrDefault(x => x.Id == blogItemId);
if (item == null)
{
return RedirectToAction("MainBlog", new { page = 1, blogCategory = "" });
}
return View(item);
}
Now in the DetailedView.cshtml, I have a form. When I click the "Post Comment" button within the form, I would like to save some model record within the database and update some section with in the view.
Problem number 1: Please see my Model class below, I have a navigation property and foreign key. How do I specify these values within jQuery from my Model.
Problem number 2: I have a breakpoint in the PostComment method, but jQuery is not binding any values. Everything is null. please help.
Here is my DetailedView.cshtml:
#model KGSBlog.Models.BlogData
#SOME CODE HERE #
<form id="formPostComment" method="post" action="BlogData/PostComment">
<div class="row">
<div class="col-md-6">
<input type="text" placeholder="Name *" id="name" name="name">
</div>
<div class="col-md-6">
<input type="email" placeholder="Email *" id="email" name="email">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<input type="text" placeholder="WebSite" name="website">
</div>
</div>
<textarea name="message" placeholder="Message *" id="message" cols="45" rows="10"></textarea>
<button id="btnPostComment" class="btn btn-main m-bottom-40">Post Comment</button>
</form>
#section Scripts {
<!-- jQuery -->
<script src="~/js/jquery.min.js"></script>
<!-- Bootstrap -->
<script src="~/bootstrap/js/bootstrap.min.js"></script>
<!-- Components Plugin -->
<script src="~/js/jquery.easing.1.3.js"></script>
<script type="text/javascript">
$(function () {
$("#btnPostComment").click(function () {
var name = $("#name").val();
var email = $("#email").val();
var message = $("#message").val();
var dataToPost = {};
dataToPost.Name = name;
dataToPost.Email = email;
dataToPost.Comment = message;
//How can I populate the BlogId and Blog properties here?
$.ajax({
url: '#Url.Action("PostComment", "BlogData")',
data: JSON.stringify(dataToPost),
type: "POST",
dataType: 'JSON',
contentType: "application/json",
success: function(data) {
//do stuff with json result
},
error: function(passParams) {
console.log("Error is " + passParams);
}
});
});
});
</script>
}
Here is my BlogDataController PostComment action method where the properties in the blogComment parameter are all null.
[HttpPost]
public async Task<IActionResult> PostComment(BlogComments blogComment)
{
if (blogComment != null)
{
await _context.BlogComments.AddAsync(blogComment);
await _context.SaveChangesAsync();
}
var item = _mainBlogHelperModel.BlogDataItems.FirstOrDefault(x => x.Id == blogComment.Id);
return new JsonResult(new { comment = blogComment.Comment, name = blogComment.Name });
}
The BlogComments model class
public class BlogComments
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Comment { get; set; }
[ForeignKey("BlogID")]
public int BlogId { get; set; }
public BlogData Blog { get; set; }
}
Problem number 1: Ideally, you don't want to pass the Blog property, you just want to pass the BlogId. It's not causing your issue but in my opinion, you shouldn't use the entity as the model, you should have another class that acts as a DTO. Is it good practice to use entity objects as data transfer objects?
You can do what you're looking for with something like this:
dataToPost.BlogId = #Model.Id;
Problem number 2: It might be fixed after you've resolved problem number 1. The problem might be that your action is expecting an int for BlogId but you're currently not passing BlogId.
If that doesn't work, open the network tab in the browser and make sure it's passing the POST body. Then check for ModelState errors in your action to see if any of the properties are failing validation.
Since in your original post you were using contentType: "application/json", you just needed to add a [FromBody] attribute and everything would be working
public async Task<IActionResult> PostComment([FromBody]BlogComments blogComment)
in your second post the code is working because in this case your content type is form-data (by default). In this case you don't neeed [FromBody] attribute
I don't know whats the difference between what I wrote and this, except for some attributes, but i changed the jquery code as follows, after referring to this post and it works. If someone can tell me why my old code was not working and this one is working, that will be great. (May be I shouldn't stringify the object)
First off I made the form like this,
<form id="formPostComment" method="post" enctype="multipart/form-data" asp-action="PostComment" asp-controller="BlogData">
<div class="row">
<div class="col-md-6">
<input type="text" placeholder="Name *" id="name" >
</div>
<div class="col-md-6">
<input type="email" placeholder="Email *" id="email">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<input type="text" placeholder="WebSite" name="website">
</div>
</div>
<textarea placeholder="Message *" id="message" cols="45" rows="10"></textarea>
<submit id="btnPostComment" class="btn btn-main m-bottom-40">Post Comment</button>
</form>
#This is the jquery code.#
#section Scripts{
<!-- jQuery -->
<script src="~/js/jquery.min.js"></script>
<!-- Bootstrap -->
<script src="~/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function () {
$("#btnPostComment").click(function () {
var url = $("#formPostComment").attr("action");
var formData = new FormData();
formData.append("Name", $("#name").val());
formData.append("Email", $("#email").val());
formData.append("Comment", $("#message").val());
formData.append("BlogId",#Model.Id);
$.ajax({
type: 'POST',
url: url,
data: formData,
processData: false,
contentType: false
}).done(function (response) {
if (response.Status === "Success") {
alert("HELLO");
}
});
});
});
</script>
The Controller code, I just changed the parameter like this. Even with the old jquery code, I tried Bind like this and it didn't work.
public async Task<IActionResult> PostComment([Bind("Name,Email,Comment,BlogId")] BlogComments blogComment)
I am trying to reuse a model in different views in asp.net mvc. But if the model is passed to the next view and new attributes are submited the old values are lost.
Here the code example I'am using:
Model
public class ExampleModel
{
public string Attribut1 { get; set; }
public string Attribut2 { get; set; }
}
Controller
public class HomeController : Controller
{
public IActionResult Page1()
{
return View(new ExampleModel());
}
public IActionResult Page1Check(ExampleModel model)
{
return View("Page2", model);
}
public IActionResult Page2Check(ExampleModel model)
{
return View("Page2", model);
}
}
View1
#model Example.Models.ExampleModel
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form asp-controller="Home" asp-action="Page1Check">
<label asp-for="Attribut1"></label>
<input asp-for="Attribut1" placeholder="Attribut1" />
<button type="submit">Submit</button>
</form>
View2
#model Example.Models.ExampleModel
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<form asp-controller="Home" asp-action="Page2Check">
<label asp-for="Attribut2"></label>
<input asp-for="Attribut2" placeholder="Attribut2" />
<button type="submit">Submit</button>
</form>
If I open Page1 a new model is created. If the attribut1 is entered in Page1 and submited Page1Check is called and the attribut1 is filled. Now the model is passed to page2. Here the attribut2 is filled and submited. But if I check the model inside of Page2Check the attribut1 is null and only attribut2 is filled.
Strangely if I check the attribut1 in the page2 view its still there. Only after the submit the attribut1 is gone.
Do I miss something, how do I prevent that the filled attributs are empty after the submit?
In order for a piece of data to be sent from a user's browser to the server when the corresponding form is submitted, it must be contained within an HTML element that will be sent to the server (e.g. an <input> or a <select>).
In your first view, you have an input for Attribut1 but not for Attribut2, so Attribut2 won't be part of the data sent to the server when the form in view 1 is submitted. The same is true for Attribut1 in view 2.
To solve this, you can add hidden fields to each view, contained by the <form> tag, to store the data:
For view 1, add <input type="hidden" asp-for="Attribut2" />.
For view 2, add <input type="hidden" asp-for="Attribut1" />.
As an example, view 1's form would now look like this:
<form asp-controller="Home" asp-action="Page1Check">
<label asp-for="Attribut1"></label>
<input asp-for="Attribut1" placeholder="Attribut1" />
<input type="hidden" asp-for="Attribut2" />
<button type="submit">Submit</button>
</form>
I have problem with passing values by POST in ASP.NET MVC 4
This is my action in User controller:
[HttpPost]
public string Show(int? uid, string uname)
{
return uname + uid.ToString();
}
And this is how I tried to pass values in view:
#using (Html.BeginForm("Show", "User"))
{
Html.Hidden("uid", Model.Id);
Html.Hidden("uname", Model.UserName);
<input type="submit" value="+"/>
}
html:
<form action="/User/Show" method="post"> <input type="submit" value="+"/> </form>
and:
#using(Html.BeginForm("Show", "User", FormMethod.Post, new { uid = 1, uname = "user1" }))
{
<input type="submit" value="+"/>
}
html:
<form action="/User/Show" method="post" uid="1" uname="user1"> <input type="submit" value="+"/></form>
In both ways Show action receives null instead real values.
Your HtmlHelpers are not being rendered. Use Razor syntax.
#using (Html.BeginForm("Show", "User"))
{
#Html.Hidden("uid", Model.Id);
#Html.Hidden("uname", Model.UserName);
<input type="submit" value="+"/>
}
Explanation:
Calling Html.Hidden (or Html.[anything]) is a method and usually returns an IHtmlString. Without using # infront, the engine doesn't know that you're trying to output the returned string. It just thinks you're calling a method.
This is not a good approach for an action that receives data. This approach can offer many security breaches, like data injection., essentially lots of fields.
The right thing is create a Model (or a ViewModel, if you don't want to persist the data) to make the correct guidance between View and Controller:
ViewModel:
public class MyViewModel {
public int? uid { get; set; }
public string uname { get; set; }
}
View:
#model MyProject.ViewModels.MyViewModel
#using (Html.BeginForm("Show", "User"))
{
Html.HiddenFor(model => model.uid);
Html.HiddenFor(model => model.uname);
<input type="submit" value="+"/>
}
Controller:
public ActionResult Show(int modelId) {
var model = context.Model.SingleOrDefault(m => m.ModelId == modelId);
var viewModel = new MyViewModel();
viewModel.uid = model.Id;
viewModel.uname = model.UserName;
return View(viewModel);
}
[HttpPost]
public string Show(MyViewModel viewModel)
{
return viewMode.uname + viewModel.uid.ToString();
}
You're not actually creating hidden fields in your form. You need the # in front of you Hidden helper, and drop the semi-colon at the end.
#using (Html.BeginForm("Show", "User"))
{
#Html.Hidden("uid", Model.Id)
#Html.Hidden("uname", Model.UserName)
<input type="submit" value="+"/>
}
The reason your hard-coded test didn't work is that your HTML wasn't correct. You can't just put the values you want to post on the <form> element, you need to have them as hidden fields in the form.
<form action="/User/Show" method="post">
<input type="hidden" name="uid" value="1" />
<input type="hidden" name="uname" value="user1">
<input type="submit" value="+"/>
</form>
It would be better if you could use a view model.
public class MyViewModel
{
public int? Id {get;set;}
public string UserName {get;set;}
}
public ActionResult Show()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Show(MyViewModel model)
{
return Content(string.format("{0} - {1}", model.Id, model.UserName));
}
(Coded in notepad so untested, but should work.)
I have an Action result method that is supposed to add data into a database when a button is clicked. Although, when I click on the button to add the data using the AddDetails Action method I get the following Error:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /home/AddDetails
Controller:
namespace //not included
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Details()
{
return View();
}
[HttpPost]
public ActionResult AddDetails(Customer customer)
{
if (ModelState.IsValid)
{
var db = new CustomerContext();
db.Customers.Add(new Customer
{
ItemName = customer.ItemName,
Price = customer.Price
});
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
}
}
View:
#{
ViewBag.Title = "Details";
}
<h2>Add Items</h2>
<div class="container">
<form class="form-signin" role="form">
<h2>Please type in Item Name and price paid</h2>
<input type="text" class="form-control" placeholder="Name" required autofocus />
<input type="number" pattern="[0-9]+([\.|,][0-9]+)?" step="0.01" min="0" class="form-control" placeholder="Price" required autofocus />
<input type="button" class="btn btn-lg btn-primary btn-block" value="Add" onclick="location.href='#Url.Action("AddDetails", "home")'" />
</form>
</div>
You're posting to /Customers/Details but your action is /Customers/AddDetails. You can tell your form to specifically post to /Customers/AddDetails or you can rename your AddDetails action to Details. It's fine to have both a GET and POST action called Details. When you issue a post request, the action with the HttpPost attribute will be hit. When you issue a get request, the action with no attribute or a HttpGet attribute will be hit.
If you want to keep the name 'AddDetails', your form should look like this (I'm using Razor instead of plain HTML):
#using (Html.BeginForm("AddDetails", "Home", FormMethod.Post) {
<h2>Please type in Item Name and price paid</h2>
<input type="text" class="form-control" placeholder="Name" required autofocus />
<input type="number" pattern="[0-9]+([\.|,][0-9]+)?" step="0.01" min="0" class="form-control" placeholder="Price" required autofocus />
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Add" />
}
I guess, I don't type fast enough... As Neil mentioned, use the Html Helper, though I recommend using named parameters so that you don't confuse which is the Action and Controller:
#using (Html.BeginForm(
actionName: "AddDetails",
controllerName: "Customer",
method: FormMethod.Post,
htmlAttributes: new { #class = "myclass" }))
Also, to help keep the routes correct (and not need to rely on "magic strings"), I highly recommend using T4MVC. Then your form will look like this:
#using (Html.BeginForm(MVC.Customer.AddDetails(), ...)
And the Razor view will not even render if the Action does not exist. It is also easier writing the form because Intellisense will help you find the Controller and Action you want.
Make These changes in your code.
namespace //not included
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult AddDetails()
{
Customer newCustomer = new Customer();
return View(newCustomer);
//right click this action and choose addview and use strongly typed
//view by choosing Customer class
}
[HttpPost]
public ActionResult AddDetails(Customer customer)
{
//whatever you want here
}
}
}
You will get a view with all the syntax for form and input textBoxes. Just few minor additions to meet your UI requirement will be needed.
Feel free to ask more doubts if any.
You need to give Action name in Html.BeginForm like this #using (Html.BeginForm("AddDetails", "Home", FormMethod.Post)
Or
change you controller action name to Details instead of AddDetails
This is what I've had in mind but of course it doesn't work.
#{
var textBoxData = form.find('input[name="textboxList"]').val();
}
<input type="button" value="Add" title="Add" onclick="location.href='#Url.Action("Create_Add", "Controller", new { textboxList = textBoxData })'" />
How should I pass this? Controller action name and parameter are correct. Just that I don't know how to get the value entered in textbox...
I have trouble with saving a form within a form, so someone suggested this solution. Proxy code would be:
<firstForm>
textboxfor Name
dropdownfor DType
If DTypeDDL value is "List" then
<secondForm>
textboxfor nameOfItem
submitSecondForm (using that method i mentioned above)
</secondForm>
End If
submitFirstForm
</firstForm>
I've been trying to save 2 forms for quite a while now but no luck. This is basically my last resort.
First of all, you should go with a viewmodel oriented html file since you are using MVC (Model, View, Controller):
Create a viewModel:
public class ExampleViewModel
{
public ExampleViewModel()
{
}
public virtual string TextBoxData { get; set; }
}
After, code your html using the viewmodel as model:
#model Models.Views.ExampleViewModel
#using (Html.BeginForm())
{
<div class="editor-row">
<div class="editor-label">
#Html.LabelFor(model => model.TextBoxData)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TextBoxData)
</div>
</div>
<input type="submit" value="submit" />
}
and your controller:
public ActionResult Example()
{
ExampleViewModel model = new ExampleViewModel();
return This.View(model);
}
[HttpPost]
public ActionResult Example(ExampleViewModel model)
{
string infoEntered = model.TextBoxData;
// Do something with infoEntered
}
Hope this will help you!
If you're using view models, check out this answer: MVC sending data from View to Controller
If you're only interested in sending the data from an input to the action method without view models, you can do that as well:
View:
#using (Html.BeginForm("Edit", "Some", FormMethod.Post))
{
<input type="text" id="myTextBox" name="myTextBox" />
<input type="submit" value="Submit" />
}
Notice the BeginForm line. The first parameter is the Action I want the data to go to, which I named Edit. The next parameter is the Controller I am using, which I named SomeController. You don't add the Controller bit to the name when you're referencing the Controller in BeginForm. The third parameter is telling the form to use the POST method when sending the data to the server.
Controller:
public class SomeController
{
[HttpPost]
public ActionResult Edit(string myTextBox)
{
// Do what you want with your data here.
}
}
If you added more inputs (again, without a view model here), you can add them as parameters to the Edit method. This isn't really the preferred method, though. Look into using a view model. ScottGu has a nice blog post on doing what you need, using view models:
http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx