Forms inside foreach loop MVC - model-view-controller

I have situation where MVC controller has sent list of records.
Those records are displayed in view in order that every single record is a form, so it can be posted record by record.
#model IList<FMS.Application.Models.TransferLineUpdateResponse>
#{
ViewData["Title"] = "Transfer Lines Page";
}
<div class="row">
<div class="col-12">
#foreach (var item in Model)
{
<form method="post" class="form-inline">
<div class="form-group">
<input type="hidden" asp-for="#item.Id" readonly class="form-control" />
<input type="hidden" asp-for="#item.TransferHeaderId" readonly class="form-control" />
</div>
<div class="form-group">
<label asp-for="#item.Item"></label>
<input asp-for="#item.Item" readonly class="form-control" />
</div>
<div class="form-group">
<label asp-for="#item.ToLocationId"></label>
<select class="form-control" asp-for="#item.ToLocationId" asp-items="ViewBag.ToLocations"></select>
</div>
<div class="form-group">
<input type="submit" asp-route-status="Accepted" class="btn btn-success" /> |
<input type="submit" asp-route-status="Rejected" class="btn btn-danger" />
</div>
</form>
}
</div>
</div>
When one of two buttons is clicked and form submission is called, as parameter in controller all my records like Id, ToLocationId are null or empty guids in case if property has type guid. Why it is not taken data which has been actually changed in view ?
Regards
P.S.here is the code of controller
[HttpGet("Transfer/Details/{id}")]
public async Task<IActionResult> Details(Guid id)
{
var lines = await _transferLineService.GetTransferLines(id);
var locations = await _locationService.GetLocations();
var customLocations = locations
.Where(w=>w.Code != "Tranzit")
.Select(s => new
{
Id = s.Id,
Description = $"{s.Code} - {s.Description}"
});
ViewBag.ToLocations = new SelectList(customLocations, "Id", "Description");
return View(lines);
}
[HttpPost("Transfer/Details/{transferHeaderId}")]
public async Task<IActionResult> AcceptDetails(Guid transferHeaderId, TransferLineUpdateResponse update)
{
var result = await _transferLineService.UpdateTransferLine(update);
await _transferHeaderService.UpdateTransferHeaderStatus(update.TransferHeaderId);
return RedirectToAction("Details",new { transferHeaderId = update.TransferHeaderId });
}

Why it is not taken data which has been actually changed in view ?
Model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.In your code,the asp-for tag helper will generate the name like:item.propertyName.It could not match with backend model.You could use [Bind(Prefix ="item")] to specify the prefix.
Change like below:
[HttpPost("Transfer/Details/{transferHeaderId}")]
public async Task<IActionResult> AcceptDetails(Guid transferHeaderId,[Bind(Prefix ="item")] TransferLineUpdateResponse update)
{
//do your stuff...
//_context.Update(update);
//await _context.SaveChangesAsync();
//your get method used [HttpGet("Transfer/Details/{id}")]
//so,it should be id not transferHeaderId
return RedirectToAction("Details", new { id = update.TransferHeaderId });//also change here...
}
Result:

Related

Passing form data from View Component to Controller in .NET Core MVC

I have a Component View and I try to update data from a form in it, I call the controller but in the controller I receive null :
public class CustomerPropertyViewComponent: ViewComponent
{
private MyDBContext context;
public CustomerPropertyViewComponent(MyDBContext _contex)
{
context = _contex;
}
public async Task<IViewComponentResult> InvokeAsync(int id)
{
CustomerPropertyModelView c = new CustomerPropertyModelView();
TblCustomerProperty t = new TblCustomerProperty(context);
c = t.getAllInfo(id);
if (c.model == null)
{
c.model = new TblCustomerProperty();
c.model.CustomerId = id;
}
return View(c);
}
}
and in the view I have
#model SunSystemDotNetCoreVersion.Models.helpers.CustomerPropertyModelView
<form asp-action="Update" asp-controller="CustomerProperties"
data-ajax="true"
data-ajax-method="POST"
method="post">
<div class="row w-100">
<div class="col-6">
<div class="row align-items-center h-100">
<div class="col-5 text-right">
Number of Bedrooms
</div>
<div class="col-7 p-1 p-1">
#Html.TextBoxFor(model => model.model.Bedrooms, new { #class = "form-control", Type = "number" })
</div>
<div class="col-5 text-right">
Current Heating System
</div>
<div class="col-7 p-1">
<select asp-for="model.HeatingSystemTypeId" class="form-control"
asp-items="#(new SelectList(Model.HeatingsList,"HeatingSystemTypeId","Title"))">
<option value="0">-Plaese Select-</option>
</select>
</div>
.....
<div class="col-12">
<button type="submit" >Save</button>
</div>
</div>
</form>
I have reduced most of the view code but it contains all data that the model needs. and this is my controller:
public class CustomerPropertiesController : Controller
{
private readonly MyDBContext_context;
public CustomerPropertiesController(MyDBContextcontext)
{
_context = context;
}
public IActionResult Update(TblCustomerProperty modelView) {
//here modelView is null
return View();
}
it should be work I don't know why it keeps sending null to my controller.
You could F12 to check the html elements in browser,and you will find the name of these elements are like:model.Bedrooms.Because your main model is CustomerPropertyModelView but your input belongs to TblCustomerProperty which named model in CustomerPropertyModelView.If your backend code recieve CustomerPropertyModelView as parameter,it will not be null.But if you recieve TblCustomerProperty as parameter,you need specify the suffix.
Change like below:
public IActionResult Update([Bind(Prefix ="model")]TblCustomerProperty modelView)
{
return View();
}

Custom Model Validation in ASP.Net core v3.1 MVC ajax form not seems to be working

I'm working on an ASP.Net core 3.1 MVC project in which I have to create a custom Validator, and I want it to be working for client as well as server side (e.g. Required Attribute).
I developed a simple custom validator like below just for POC -
public class ImportantAttribute : ValidationAttribute, IClientModelValidator
{
public void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
AttributeUtils.MergeAttribute(context.Attributes, "data-val", "true");
AttributeUtils.MergeAttribute(context.Attributes, "data-val-important", FormatErrorMessage(context.ModelMetadata.GetDisplayName()));
}
public class AttributeUtils
{
public static bool MergeAttribute(
IDictionary<string, string> attributes,
string key,
string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
string val = value.ToString();
if (val.Contains("hello"))
{
return ValidationResult.Success;
}
}
return new ValidationResult("Value not valid");
}
}
and used this attribute on a property and created a View using the same model.
Them modified the form tag to become an ajax form like -
<form asp-action="Index" role="form" data-ajax="true" data-ajax-method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" value="SGSM" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
Then I added below java script -
$(document).ready(() => {
console.log('I\'m ready bro');
$.validator.addMethod("important",
function (value, element, params) {
console.log('1', value);
return value.contains('hello');
}, "Not OK");
$.validator.unobtrusive.adapters.add("important",
['important'],
function (options) {
console.log('2', options);
options.rules["important"] = options.important;
options.messages["important"] = options.message;
});
});
When I run this by providing any value to the text box and submitting the form it don't show any error message on the page, but if I put break point in the Action Method the ModelState shows correct info.
If I make the form as regular form (i.e. non-ajax form) everything works as expected.
I have searched a lot but could not find any thing related.
Based on your code and requirement, I made some some modifications on custom client-side validation code, which works well for me, you can refer it.
<div class="row">
<div class="col-md-4">
<form asp-action="Index" method="post" role="form" data-ajax="true" data-ajax-method="post" data-ajax-complete="completed">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.js"></script>
<script>
$(document).ready(() => {
console.log('I\'m ready bro');
});
completed = () => {
alert('Request completed!');
};
$.validator.addMethod("important",
function (value, element, params) {
console.log('1', value);
return value.includes('hello');
}, "Not OK");
$.validator.unobtrusive.adapters.add("important",
['important'],
function (options) {
console.log('2', options);
var element = $(options.form).find('input#Name')[0];
options.rules["important"] = [element, ''];
options.messages["important"] = options.message;
});
</script>
}
Test Result

Eloquent update doesn't work in Laravel 6

I'm trying to update a field after submitting in the following form:
<form action="{{ route("comments.update") }}" method="post">
#csrf
<input type="hidden" name="commentIDToEdit" id="commentID">
<div class="md-form mb-5">
<i class="fas fa-comment"></i>
<label for="toEditComment"></label>
<textarea name="toEditCommentary" id="toEditComment" cols="3" rows="5" style="resize: none"
class="form-control"></textarea>
</div>
<div class="modal-footer d-flex justify-content-center">
<button type="submit" class="btn btn-default">Modificar</button>
</div>
</form>
I have the CommentsController, where I process the data from the form. Here is the code:
public function updateComment()
{
request()->validate([
"toEditCommentary" => "min:10|max:500"
]);
if (Session::has("username") && getWarningCount(User::whereUsername(session("username"))->value("email")) > 0) {
Caveat::where("commentID", request("commentIDtoEdit"))
->update(["updatedComment" => request("toEditCommentary")]);
} else {
die("No se cumple la condiciĆ³n");
}
if (Comment::where("commentID", request("commentIDToEdit"))->exists()) {
Comment::where("commentID", request("commentIDToEdit"))
->update(["commentary" => request("toEditCommentary")]);
}
return back();
}
Curiosly, the comment is updated in his table, but not the warning. I was thinking in the fillable property in the model, but I don't have it, instead this, I have the following code:
protected $guarded = [];
const UPDATED_AT = null;
const CREATED_AT = null;
Your hidden input is named commentIDToEdit, but in the Controller you fetch the Caveat using request("commentIDtoEdit") (different case).
What you wrote:
Caveat::where("commentID", request("commentIDtoEdit"))
What you should have done: (note the different casing)
Caveat::where("commentID", request("commentIDToEdit"))
This is because in the view, the input name is commentIDToEdit, not commentIDtoEdit.

Passing a predefined Object from View to Controller by form in MVC6

Code in my controller:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CreateRedBlood(Donor donor)
{
if (ModelState.IsValid)
{
//create
Blood newBlood = new Blood();
if(donor != null)
{
DateTime todaysdate = DateTime.Now;
newBlood = new Blood("RBC", donor.ID);
}
_context.Blood.Add(newBlood);
_context.SaveChanges();
Code in my View:
<form asp-action="CreateRedBlood" method="post">
<div class="form-horizontal">
<h4>Make Donation</h4>
<hr />
<div class="form-group">
<input type="submit" value="Make Red Blood Cell Donation"class="btn btn-default" />
</div>
</div>
</form>
Prior to the use of this form the page has got FullDonorDetails from the controller, and uses model.donor.x to get all the useful bits of information out.
What I'd like to do is pass model.donor back into this form, so that when the button is pressed it takes all that handy information and puts it straight back into CreateRedBlood.
Any ideas?
Thanks!
EDIT: Shyju asked for the following. This is the index controller I'm working with to get fullDonorDetails.
public IActionResult Index(string searchString)
{
FullDonorDetails fullDonorDetails = new FullDonorDetails();
//Get Donor
Donor emptyDonor = new Donor();
Donor activeDonor = new Donor();
if (!string.IsNullOrEmpty(searchString)
&& searchString.Length == 10)
{
activeDonor = _context.Donor.Single(m => m.NHN == searchString);
if (activeDonor != null)
{
fullDonorDetails.Donor = activeDonor;
}
else
{
fullDonorDetails.Donor = emptyDonor;
}
}
else
{
fullDonorDetails.Donor = emptyDonor;
}
//Get History
List<Blood> donorBloodHistory = new List<Blood>();
if (activeDonor != emptyDonor)
{
//RedBlood
var BloodHistory = from r in _context.Blood
select r;
BloodHistory = BloodHistory.Where(s => s.DonorId.Equals(activeDonor.ID));
foreach (Blood currentBlood in BloodHistory)
{
donorBloodHistory.Add(currentBlood);
}
List<Blood> sortedList = donorBloodHistory.OrderBy(o => o.DateTaken).ToList();
sortedList.Reverse();
fullDonorDetails.DonorHistory = sortedList;
}
return View(fullDonorDetails);
}
Since your HttpPost action method is reading only the ID property of the Donor class, which is a parameter of your HttpPost action method, you should keep that property value in a form field with the same name (ID).
#model FullDonorDetails
<form asp-action="CreateRedBlood">
<div class="form-horizontal">
<h4>Make Donation</h4>
<hr />
<input type="hidden" asp-for="Donor.ID" name="ID" />
<div class="form-group">
<input type="submit" value="Make RBC Donation"class="btn btn-default" />
</div>
</div>
</form>
Now when user submits the form, The param of your HttpPost action method will have the value of the Donor ID (which you set in your GET action).

Passing Viewbag Data from View to Controller in ASP.Net MVC3 Razor

In my ASP.Net MVC3 Razor project i have to pass value from view to controller.The view contains one submit button that is used to pass selected image file and two other input data.This two input data is from a controller named "FileUpload"(ViewBag.Data1 = CusId;ViewBag.Data2 = Name;).When submitting the button i have to pass these three (Image,CusId,Name) to another controller to upload the image file.
Controller Code
public ActionResult FileUpload(int CusId, string Name)
{
ViewBag.Data1 = CusId;
ViewBag.Data2 = Name;
return View();
}
[HttpPost]
public ActionResult UploadPhoto(ElixiCustPro elixi, HttpPostedFileBase file)
{
//return null;
try
{
if (file != null && file.ContentLength > 0)
{
if ((file.ContentType == "image/jpeg") || (file.ContentType == "image/gif") || (file.ContentType == "image/png"))//check allow jpg, gif, png
{
elixi.Image = new byte[file.ContentLength];
file.InputStream.Read(elixi.Image, 0, file.ContentLength);
var filename = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/ElixirFiles/UploadImagesElixir/"), filename);
file.SaveAs(path);
ecp.Image = new byte[file.ContentLength];
ecp.ImageUrl = path;
ment.ElixiProData.Add(ecp);
ment.SaveChanges();
return RedirectToAction("ImageResult");
}
}
}
catch (Exception ex)
{
return View(ex.Message.ToString());
}
return View();
}
View Code
#using (Html.BeginForm("UploadPhoto", "Home", FormMethod.Post, new { #enctype = "multipart/form-data" }))
{
#* <div class="form-group">
<label class="col-lg-2 control-label">
Customer ID</label>
<div class="col-lg-10">#Html.TextBoxFor(model => model.CusId, new { #class = "form-control" })</div>
<label class="col-lg-2 control-label">
Customer Name</label>
<div class="col-lg-10">#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })</div>
</div>*#
<input type="hidden" id="id" />
<div class="col-md-6">
<div class="form-group">
<label class="col-lg-2 control-label">
DMIT Image</label>
<div class="col-lg-10">
#ViewBag.Data1
#ViewBag.Data2
<input type="file" id="file" name="file">
<input type="submit" class="btn btn-success" value="Upload" />
</div>
</div>
</div>
}
ViewBag can't pass data back to controller. You should post those values back inside the form. Easiest thing would be to not to use ViewBag and add the data to model type.
Then you can pass them with hidden inputs using HTML helpers like this:
#Html.HiddenFor(item => item.CustomerId)
#Html.HiddenFor(item => item.ImageId)
If that's not possible, you can add the hidden inputs manually. Just keep in mind that the name attributes are important for model binding.
<input type="hidden" name="CustomerId" value="#ViewBag.Data1" />
Yes you cannot pass a Viewbag from view to controller.
But you can pass them using TempData.
Add this to your View.
#{TempData["Data1"]=ViewBag.Data1}
#{TempData["Data2"]=ViewBag.Data2}
But this TempData passes the information as an object.
So typecasting is necessary in your Controller.
int x=Convert.ToInt32(TempData["Data1"]);
string y=(TempData["Data2"]).ToString();
I tried,it is working.
or you can send TempData from Controller in get method,and use the same to pass from view to post method instead of Viewbag.

Resources