Manipulating model and then reflecting that within a view - asp.net-mvc-4

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.

Related

How do I persist all querystring values in asp.net core 3.1 mvc?

I have an asp.net core 3.1 mvc project where I have a search page (view). I am persisting search, filter and sort criteria values via Session data. This works fine, and persists data correctly, but the querystring does not contain all the information I need it to.
Values for the filters and search are selected via a form, and submit button, but sorting is selected by clicking the column headers in the search results. e.g.:
<form asp-controller="Companies" asp-action="Index" method="get">
<p>
<label>Company Name: </label>
<select asp-for="CompanyName" asp-items="Model.CompanyName">
<option value="">All</option>
</select>
<input type="submit" value="Find" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
<a asp-action="Index" asp-route-sortOrder="CompanyName">#Html.DisplayNameFor(model => model.CompaniesListViewModelList[0].CompanyName)</a>
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.CompaniesListViewModelList)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="#item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
If the clicks in the header to change the sort order, none of the querystring values for the filters etc appear in the querystring.
The reason I need this is so that a link can be sent to someone else.
So the question is, how do I get all the used querystring values to appear in the querystring?
In the Index action method, you could use ViewBag or ViewData to store the current search value (the CompanyName), then transfer it to the view page:
Then, for the sort links, you could use asp-route-{value} to add the current search value as the route parameter, and bind values via the ViewData or ViewBag. Code like this:
<a asp-action="Index" asp-route-CompanyName="#ViewData["CurrentFilter"]" asp-route-sortOrder="#ViewData["CompanyNameSortParm"]">CompanyName</a>
The sample code as below:
Models:
public class Employee
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public string CompanyName { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public List<Employee> Employees { get; set; }
}
public class EmployeeVM
{
public string CompanyName { get; set; }
public List<Company> AllCompanies { get; set; }
public List<Employee> Employees { get; set; }
}
Controller:
public class CompaniesController : Controller
{
public IActionResult Index(string sortOrder, string CompanyName)
{
ViewData["CompanyNameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "companyname_desc" : "";
ViewData["CurrentFilter"] = CompanyName;
var employees = from s in repo.GetEmployees()
select s;
if (!String.IsNullOrEmpty(CompanyName))
{
employees = employees.Where(s => s.CompanyName.Contains(CompanyName));
}
switch (sortOrder)
{
case "companyname_desc":
employees = employees.OrderByDescending(s => s.CompanyName);
break;
default:
employees = employees.OrderBy(s => s.EmployeeId);
break;
}
EmployeeVM vm = new EmployeeVM();
vm.Employees = employees.ToList();
vm.AllCompanies = repo.GetAllCompanies().ToList();
return View(vm);
}
}
Index Views:
#model netcore3.Data.EmployeeVM
<form asp-controller="Companies" asp-action="Index" method="get">
<p>
<label>Company Name: </label>
<select asp-for="CompanyName" asp-items="#(new SelectList(Model.AllCompanies, "CompanyName","CompanyName"))">
<option value="">All</option>
</select>
<input type="submit" value="Find" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
EmployeeName
</th>
<th>
<a asp-action="Index" asp-route-CompanyName="#ViewData["CurrentFilter"]" asp-route-sortOrder="#ViewData["CompanyNameSortParm"]">CompanyName</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Employees)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.EmployeeName)
</td>
<td>
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.EmployeeId">Edit</a> |
<a asp-action="Details" asp-route-id="#item.EmployeeId">Details</a> |
<a asp-action="Delete" asp-route-id="#item.EmployeeId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
The result like this:
Reference:
Tutorial: Add sorting, filtering, and paging - ASP.NET MVC with EF Core
Anchor Tag Helper in ASP.NET Core

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.

My list will be empty when POST the form

I have some problem with the MVC, I'll try to describe. I have 2 class in my model.
public class ApplicationPermissionVM
{
public ApplicationPermission Permission { get; set; }
public bool IsSelected { get; set; }
}
public class RoleAndPermissonsModel
{
//Constructor skipped
public ApplicationRole ApplicationRole { get; set; }
public IEnumerable<ApplicationPermissionVM> Permissions { get; set; }
}
The second model is the main model, and I initialize the model in controller. The ApplicationRole is null and I have got 19 element in the list. When I POST the form the ApplicationRole member will be created, but the Permissions list will be empty so will lost all selection. If anyone knows what is the problem please write to me.
Controller:
[HttpGet]
public ActionResult NewRole()
{
_model = new RoleAndPermissonsModel();
return View(_model);
}
[HttpPost]
public ActionResult NewRole(RoleAndPermissonsModel newRole)
{
if (ModelState.IsValid)
{
var id = _applicationRoleService.AddNewRole(newRole.ApplicationRole);
_applicationRoleService.AssignPermissionsToRole(newRole.SelectedItems, id);
}
return RedirectToAction("Index");
}
View:
#model EO.Core.WebUI.Models.RoleAndPermissonsModel
#using (Html.BeginForm("NewRole", "PermissionRole", FormMethod.Post, new { id = "frmNewRole" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ApplicationRole</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ApplicationRole.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ApplicationRole.Name)
#Html.ValidationMessageFor(model => model.ApplicationRole.Name)
</div>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.IsSelected)
</th>
<th></th>
</tr>
#foreach (var item in Model.Permissions)
{
<tr>
<td>
#Html.EditorFor(modelItem => item.IsSelected)
</td>
<td>
#Html.DisplayFor(modelItem => item.Permission.Name);
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
you need manually to loop through the list and output it like
<input type="text"
id="Permissions[0].IsSelected"
name="Permissions[0].IsSelected" value="" />
so, change your code to:
#for(int i = 0; i < Model.Permissions.Count; i++)
{
<tr>
<td><input type="checkbox"
id="Permissions[#(i)].IsSelected"
name="Permissions[#(i)].IsSelected" /></td>
<td><input type="text"
id="Permissions[#(i)].Permission.Name"
name="Permissions[#(i)].Permission.Name" /></td>
</tr>
}
because you have an object inside your type Permissions remember to initialiate it or you will get a null reference.
public class RoleAndPermissonsModel
{
public RoleAndPermissonsModel() {
this.Permissions = new List<ApplicationPermissionVM>();
}
public ApplicationRole ApplicationRole { get; set; }
public IEnumerable<ApplicationPermissionVM> Permissions { get; set; }
}
or you can easily create a ViewModel instead and use that to interlink your Model to your View.
Note: I do wonder, if your list is always null (as you are passing an empty/null list) why are you looping through Model.Permissions ?
In order to do the "EDIT" view, the loop code should be:
#for(int i = 0; i < Model.Permissions.Count; i++)
{
<tr>
<td><input type="checkbox"
#(Permissions[i].IsSelected ? "checked='checked'" : "")
id="Permissions[#(i)].IsSelected"
name="Permissions[#(i)].IsSelected" /></td>
<td><input type="text"
id="Permissions[#(i)].Permission.Name"
name="Permissions[#(i)].Permission.Name"
value="#(Permissions[i].Permission.Name)" /></td>
</tr>
}
all you need to add is checked to the checkbox if it's selected and fill the permission name to the other input.

Postback checkboxes from table rows on MVC Razor Strongly Typed View

I have strongly typed view showing data from
ViewModel
public class GoldSetnUsers
{
bool Public { get; set; }
public List<GSUsers> gsUsers { get; set; }
public GoldSetnUsers()
{
UsersContext _dbm = new UsersContext();
this.gsUsers = _dbm.UserProfiles.Select(n => new GSUsers { UserName = n.UserName, isEditor = false, isReviewer = false }).ToList();
}
public class GSUsers
{
public string UserName { get; set; }
public bool isEditor { get; set; }
public bool isReviewer { get; set; }
}
}
Controller Httpget method display this view
Problem is, post-back model returns all rows check boxes as false. The check-box outside table, Public, returns correct post-back value though.
Controller Postback code
[HttpPost]
public ActionResult Create(GoldSetnUsers newGS)
{
if (ModelState.IsValid)
{ // newGS gets me value
}
}
View
#model mvc2db.Models.GoldSetnUsers
#using BootstrapSupport;
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.BeginControlGroupFor(model=>model.Public)
#Html.LabelFor(model => model.Public,new {#class="control-label"})
<div class="controls">
#Html.EditorFor(model => model.Public,new {#class="input-xlarge"})
#Html.ValidationMessageFor(model => model.Public,null,new{#class="help-inline"})
</div>
<div class="controls">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Reviewer</th>
<th>Editor</th>
</thead>
<tbody>
#foreach (var item in Model.gsUsers) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.EditorFor(modelItem => item.isEditor)
</td>
<td>
#Html.EditorFor(modelItem => item.isReviewer)
</td>
</tr>
}
</tbody>
</table></div>
#Html.EndControlGroup()
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
<button class="btn">Cancel</button>
</div>
</fieldset>
}
I guess since you are generating the checkboxes using a foreach loop all the checkboxes will have the same id. Hence there will be an ambiguity so as to which is checked and which is not.
You can try giving the username as the id of the checkbox.