Modelmetadata additional values always empty MVC4 - asp.net-mvc-4

I have the below property in my model:
[Required]
[UIHint("DropDownList")]
[AdditionalMetadata("Source", "Party.Organization.Caption")]
public int PartyId { get; set; }
I am trying to get the additional metadata value in view as follows:
object s = ViewData.ModelMetadata.AdditionalValues["Source"];
but it is always returning count 0.
not sure, why, can somebody advise pls?
complete view:
#model IEnumerable<object>
#using System.Reflection;
#using r2d2Web.Extensions;
#using d2Utils.Extensions.d2Type;
#using d2Utils.Reflection;
#using System.Collections;
#{
Type mdlType = Model.First().GetType();
PropertyInfo keyProp = mdlType.GetKeyProperty();
IEnumerable<PropertyInfo> props = mdlType.EditorProps();
Hashtable parties = (Hashtable)ViewData["Parties"];
Hashtable partyroles = (Hashtable)ViewData["Partyroles"];
}
<div class="grid">
<table>
<thead>
<tr>
#foreach (var prop in props)
{
<th>#prop.Name</th>
}
<th></th>
</tr>
</thead>
<tbody>
#foreach (var obj in Model)
{
<tr>
#foreach (var prop in props)
{
if (prop.Name == "PartyId")
{
object s = ViewData.ModelMetadata.AdditionalValues["Source"];
<td>#(obj.GetValForProp<string>(s.ToString()))</td>
}
else if (prop.Name == "PartyRoleTypeId")
{
<td>#partyroles[obj.GetValForProp<int>(prop.Name)]</td>
}
else
{
<td>#(obj.GetValForProp<string>(prop.Name))</td>
}
}
<td>
#Html.ActionLink("Edit", "Edit", new { id = obj.GetValForProp<int>(keyProp.Name) }) |
</td>
</tr>
}
</tbody>
</table>
</div>
<div id="my-dialog"></div>

You are not specifying field for getting additional metadata. Try to do it like this:
#ViewData.ModelMetadata.Properties.FirstOrDefault(n => n.PropertyName == "PartyId").AdditionalValues["Source"]
If you had strongly typed view model with type that contains PartyId better option would be to use
#ModelMetadata.FromLambdaExpression(x => x.PartyId, ViewData).AdditionalValues["Source"]

Related

How to make a viewModel to be Enumerable

I'm implementing asp.net core 3.1. I'm passing a viewmodel to the razor view which is in the following:
public class BuyRequestViewModel
{
public IEnumerable<BuyRequest> BuyRequestVM { get; set; }
public IEnumerable<string> PlatesVM { get; set; }
}
My problem is, as my viewmodel is not of type Ienumerable, I'm getting error in foreach in the razor view. The error is like the following:
ForEach statement can not operate on variables of type 'MyPanel.ViewModels.BuyRequestViewModel' because 'MyPanel.ViewModels.BuyRequestViewModel' does not contain a public instance definition for 'GetEnumerator'
And my razor view code is like below:
#model MyPanel.ViewModels.BuyRequestViewModel
<table id="myDummyTable" class="table m-table mytable table-striped table-bordered">
<thead>
<tr>
<th>
Region
</th>
<th>
Zone
<th>
MyPlate
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.BuyRequestVM.Select(x => x.Region))
</td>
<td>
#Html.DisplayFor(modelItem => item.BuyRequestVM.Select(x => x.Zone))
</td>
<td>
#Html.DisplayFor(modelItem => item.PlatesVM)
</td>
</tr>
}
</tbody>
</table>
And here below is the Index action in my controller:
public async Task<IActionResult> Index()
{
var bwrvm = new BuyRequestViewModel();
List<string> platesList = new List<string>();
var WasteAPIContext = _context.BuyRequest
.Include(b => b.UserWasteUnitNavigation).ToList();
bwrvm.BuyWasteRequestVM = WasteAPIContext;
var plateData = _context.Car.Select(x => x.Plate).ToList();
for (int i = 0; i < plateData.Count; i++)
{
//platesList.Add(plateData[i].ToString().Substring(2, 5));
string temp = getPlateCharacter(plateData[i].ToString().Substring(2, 3));
plateData[i].Remove(2, 3);
string totalPlate = plateData[i].Insert(2, temp);
platesList.Add(totalPlate);
}
bwrvm.PlatesVM = platesList;
return View(bwrvm);
}
I appreciate of any help.
#foreach (var item in Model)
should be
#foreach (var item in Model.BuyWasteRequestVM)
because Model == BuyRequestViewModel which contains 2 properties with lists.

A property of model becomes null while posting

I'm new to MVC. Now I'm trying with a simple demo of MVC that to print Customer's information to screen and update it, send back to database.
I have no idea why the Customer's ID becomes null while the others are fine.
I want to display Customer's ID to the screen but I don't want user to edit it to post to my database. I've been researching this for some hours now..
my code :
Customer.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace PeopleManagement.Models
{
public class Customer
{
[Required]
public string Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Customer()
{
}
public Customer(string id, string name, int age)
{
Id = id;
this.Name = name;
this.Age = age;
}
}
}
Index.cshtml
#using System.Web.UI.WebControls
#using PeopleManagement.Models
#model IList<Customer>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>First MVC Application</title>
<link href="#Url.Content("~/Content/css/customized-table.css")" rel="stylesheet" type="text/css" />
</head>
<body style="max-width:100%; max-height:100%">
<div id="pageTitle" style="text-align:center; color:red; font-size:24px; font-weight:bold;">Customer Management</div>
#using (#Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div id="tablePanel" style="padding-top: 15px">
<table class="customized_table" border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
</tr>
#{
for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(model => model[i].Name);
</td>
<td>
#Html.TextBoxFor(model => model[i].Name, new {#style = "min-width:100%; text-align:center", #disable = "true"})
</td>
<td>
#Html.TextBoxFor(model => model[i].Age)
</td>
</tr>
}
}
</table>
</div>
<div>
<p><input type="submit"/></p>
</div>
}
</body>
</html>
<script>
</script>
HomeController.cs
using System.Collections.Generic;
using System.Web.Mvc;
using PeopleManagement.Models;
namespace PeopleManagement.Controllers
{
public class HomeController : Controller
{
public List<Customer> CustomersList { get; private set; } = new List<Customer>(5);
[HttpGet]
public ActionResult Index()
{
CustomersList.Add(new Customer("ID_1", "Name_1", 1));
CustomersList.Add(new Customer("ID_2", "Name_2", 2));
CustomersList.Add(new Customer("ID_3", "Name_3", 3));
ModelState.Clear();
return View(CustomersList);
}
[HttpPost]
public ActionResult Index(List<Customer> postbackCustomers)
{
if (!ModelState.IsValid)
return View(CustomersList);
CustomersList = postbackCustomers;
return View(CustomersList);
}
}
}
Can anyone help ?
In MVC if you want to get some value back from view you have to have that value in the view first. It seems you have not inputted the Id in the view.
Change your view code like this,
for (var i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(model => model[i].Id)
</td>
<td>
#Html.TextBoxFor(model => model[i].Name, new { #style = "min-width:100%; text-align:center", #disable = "true" })
</td>
<td>
#Html.TextBoxFor(model => model[i].Age)
</td>
</tr>
}
Hope this helps!!
Actually you are not showing the ID on the index.html view, The customer ID has value in your example. You should try to show it in read only mode in the for each loop.
#Html.DisplayFor(model => model[i].Id)

Mvc 4 model binding not working dynamically added partial view

I am trying to create a form where in user can add controls. I have main view
#model MVCDynamicFormGenerator.Models.FormViewModel
#{
ViewBag.Title = "Create";
}
#using (#Html.BeginForm())
{
<fieldset>
#Html.HiddenFor(form => form.Form.Uid)
#Html.Hidden("ListFields", ViewData["ListFields"])
<p>
#Html.LabelFor(form => form.Form.FormName)
#Html.TextBoxFor(form => form.Form.FormName)
</p>
<div id="FormFieldList">
#foreach (var formfield in Model.FormFields)
{
switch (formfield.ControlType)
{
case ("Textbox"):
Html.RenderPartial("Textbox", formfield);
break;
}
}
</div>
<h4>
[+] Add a Field
</h4>
<div id="FieldType">
<table>
<tr>
<th>
Select a Field Type
</th>
</tr>
<tr>
<td>
#Html.DropDownList("FieldTypes", new SelectList(Model.FormFields[0].FormFieldTypes, "Value", "Text"), new { id = "SelectedFieldUid" })
#Html.ActionLink("Add Field", "NewFormField", new { formId = ViewContext.FormContext.FormId, selectedFieldType = "SelectedFieldUid" }, new { id = "newFormField" })
#Html.ValidationMessageFor(model => model.FormFields)
</td>
</tr>
</table>
</div>
<p>
<input type="submit" value="Create" />
<input type="button" value="Cancel" '#Url.Action("List")');" />
</p>
</fieldset>
}
On dropdown change I am loading a partial view which is working(User can add n number of times)
#model MVCDynamicFormGenerator.Models.FormFieldViewModel
<div class="FormField">
#using (#Html.BeginForm())
{
<table>
<tr>
<th>
Form Field
</th>
<th>
Field Type
</th>
</tr>
<tr>
<td style="width: 45%;">
#Html.TextBoxFor(formfield => formfield.FormFieldName)
#Html.ValidationMessageFor(formfield => formfield.FormFieldName)
</td>
<td style="width: 25%;">
#Html.DropDownListFor(formfield => formfield.SelectedFormFieldType,
new SelectList(Model.FormFieldTypes, "Value", "Text",
Model.SelectedFormFieldType),
new { disabled = "disabled" })
#Html.HiddenFor(formfield => formfield.SelectedFormFieldType)
#Html.ValidationMessageFor(formfield => formfield.SelectedFormFieldType)
</td>
</tr>
</table>
}
</div>
/// form models
public class FormViewModel
{
//Properties
public Form Form { get; set; }
public List<FormFieldViewModel> FormFields { get; set; }
//Constructor
public FormViewModel()
{
Form = new Form();
FormFields = new List<FormFieldViewModel>();
}
}
public class FormFieldViewModel
{
public string FormFieldName { get; set; }
public string SelectedFormFieldType { get; set; }
}
controller methods
[HttpPost]
public ActionResult Create(FormViewModel viewModel)
{
return View();
}
All the field information related to main view gets available but FormFieldViewModel list gives zero count
Any help or suggestion to fix this

MVC 4 - Return error message from Controller - Show in View

I am doing a C# project using Razor in VS2010 (MVC 4).
I need to return an error message from Controller to View and show it to the user.
What I have tried:
CONTROLLER:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
model.error_msg = model.update_content(model);
ModelState.AddModelError("error", "adfdghdghgdhgdhdgda");
ViewBag.error = TempData["error"];
return RedirectToAction("Form_edit", "Form");
}
VIEW:
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.ValidationSummary("error")
#Html.ValidationMessage("error")
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Please help me to achieve this.
The Return View(model) returns you error because you don't fill the model with the values in your post method and the model data for the dropdown is empty. Please provide the Get method to explain further how to manage displaying the error. In order to the error to be shown you should use this:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
if(ModelState.IsValid())
{
--- operations
return Redirect("OtherAction", "SomeController");
}
// here you can use a little trick
//fill the model property that holds the information for the dropdown with the data
// you haven't provided the get method but it should look something like this
model.Countries = ... some data goes here;
model.dd_value = ... some other data;
model.dd_text = ... other data;
ModelState.AddModelError("", "adfdghdghgdhgdhdgda");
return View(model);
}
and then in the view just use :
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.ValidationSummary(true)
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
This should work okay.
If you just use RedirectToAction it will redirect you to the get method --> you will have no error but the view will be just reloaded and no error would be shown.
other way around is that you can pass the error not by ModelState.AddError, but with ViewData["error"] like this:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
TempData["error"] = "someErrorMessage";
return RedirectToAction("form_Post", "Form");
}
[HttpGet]
public ActionResult form_edit()
{
do stuff here ----
ViewData["error"] = TempData["error"];
return View();
}
#model mvc_cs.Models.FormModels
#using ctrlr = mvc_cs.Controllers.FormController
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
<div>#ViewData["error"]</div>
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
Thanks for all the replies.
I was able to solve this by doing the following:
CONTROLLER:
[HttpPost]
public ActionResult form_edit(FormModels model)
{
model.error_msg = model.update_content(model);
return RedirectToAction("Form_edit", "Form", model);
}
public ActionResult form_edit(FormModels model, string searchString,string id)
{
string test = model.selectedvalue;
var bal = new FormModels();
bal.Countries = bal.get_contentdetails(searchString);
bal.selectedvalue = id;
bal.dd_text = "content_name";
bal.dd_value = "content_id";
test = model.error_msg;
ViewBag.head = "Heading";
if (model.error_msg != null)
{
ModelState.AddModelError("error_msg", test);
}
model.error_msg = "";
return View(bal);
}
VIEW:
#using (Html.BeginForm("form_edit", "Form", FormMethod.Post))
{
<table>
<tr>
<td>
#ViewBag.error
#Html.ValidationMessage("error_msg")
</td>
</tr>
<tr>
<th>
#Html.DisplayNameFor(model => model.content_name)
#Html.DropDownListFor(x => x.selectedvalue, new SelectList(Model.Countries, Model.dd_value, Model.dd_text), "-- Select Product--")
</th>
</tr>
</table>
}
If you want to do a redirect, you can either:
ViewBag.Error = "error message";
or
TempData["Error"] = "error message";
You can add this to your _Layout.cshtml:
#using MyProj.ViewModels;
...
#if (TempData["UserMessage"] != null)
{
var message = (MessageViewModel)TempData["UserMessage"];
<div class="alert #message.CssClassName" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>#message.Title</strong>
#message.Message
</div>
}
Then if you want to throw an error message in your controller:
TempData["UserMessage"] = new MessageViewModel() { CssClassName = "alert-danger alert-dismissible", Title = "Error", Message = "This is an error message" };
MessageViewModel.cs:
public class MessageViewModel
{
public string CssClassName { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
Note: Using Bootstrap 4 classes.

Manipulating model and then reflecting that within a view

I have a LogoutModel with one attribute. I want my HomeController to change this value and for the View to then show this value but it doesn't seem to be working. When the Logout View is displayed it shows: 'Goodbye Html.LabelFor(m => m.PreviouslyLoggedInUsername)'
LogoutModel:
public class LogoutModel
{
public string PreviouslyLoggedInUsername { get; set; }
}
HomeController:
public ActionResult Logout(HomeModels.LogoutModel model)
{
model.PreviouslyLoggedInUsername = previousLoggedIn;
return View(model);
}
View:
#using Ecommerce.Models
#model HomeModels.LogoutModel
#{
ViewBag.Title = "Logout";
Layout = "~/Views/Shared/Master.cshtml";
}
<h2>You have been logged out.</h2>
#using (Html.BeginForm())
{
<table>
<tr>
<td>
<label>Goodbye </label>
Html.LabelFor(m => m.PreviouslyLoggedInUsername)
</td>
</tr>
</table>
}
You need to place an # symbol before the Html.LabelFor...
<table>
<tr>
<td>
<label>Goodbye </label>
#Html.LabelFor(m => m.PreviouslyLoggedInUsername)
</td>
</tr>
</table>
For reference, here's the Razor syntax quick guide.