ASP.NET MVC 4 - Partial views and Ajax.Actionlink - javascript

I am trying to put on my homepage some link that render partial views - I want to display some info from the database when users click a link: the link should be replaced by text on the same page. I followed a tutorial but I cannot get it to work in my project. Here is what I have:
My Home/Index view:
<div id="NeZaman">
#Ajax.ActionLink("Ne Zaman Gelcekmiş?", "NeZaman", new AjaxOptions {
UpdateTargetId="NeZaman",
InsertionMode = InsertionMode.Replace,
HttpMethod="GET" })
</div>
My HomeController:
private CaglaContext db = new CaglaContext();
public PartialViewResult NeZaman()
{
var neZaman = db.Caglas.Where(c => c.Id == 1).Select(c => c.NeZamanGelcek).FirstOrDefault();
return PartialView("_NeZaman", neZaman);
}
My partial view (_NeZaman.cshtml):
#model caglageldimi.Models.Cagla
<p>
#Model.NeZamanGelcek
</p>
My Model(Cagla.cs):
public class Cagla
{
public int Id { get; set; }
public bool GeldiMi { get; set; }
public string NeZamanGelcek { get; set; }
public string Nerdeymis { get; set; }
}
So I'm passing in a neZaman value that the partial view is supposed to use, but how?

You've set your patial view's model to your class:
caglageldimi.Models.Cagla
But you're passing a string:
db.Caglas.Where(c => c.Id == 1).Select(c => c.NeZamanGelcek).FirstOrDefault();
Your select statement is only grabbing the "NeZamanGelcek" string property value to send to your partial view.
Changing your partial view's model to System.String should fix it:
#model System.String

Related

Loading data into Bootstrap Dual Listbox in ASP.NET Core MVC

I'm working on an ASP.NET Core 2.2 MVC app and am trying to implement Bootstrap Dual Listbox plugin for role selection. I have _UserEditorPartial.cshtml view that can be used to assign roles to a user (the listbox on the right would contain roles assigned):
(The partial has more editable fields, but I think those are irrelevant)
Now, the partial contains data associated with the user I had selected on the parent page. When I select the user, I just pass UserID to ViewUserEditorPartial and query the database to retrieve the whole list of roles available, marking the IsAssigned property to true if the user belongs to the role. So now, I have a list of roles and I know which role belongs to the user.
What I'm struggling with is figuring out how to make sure that the roles belonging to the user end up in in the listbox on the right. If the user belongs to Role2 and Role4, I want my view to be generated like this:
I've found this solution but it's not obvious to me how the two listboxes are correctly populated. I'm thinking after loading the partial I could probably do something with JavaScript, where I separately retrieve the List<RoleUserAssignment> with AJAX and, depending on the value of IsAssigned property for each role, generate the <option> tag in the correct listbox. But I'm wondering is there a better approach?
Also, I'll implement the ability to create users and assign them roles using this solution that I found.
Models:
public class Role
{
public int RoleID { get; set; }
public string RoleName { get; set; }
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
}
public class RoleUserAssignment
{
public RoleUserAssignment()
{
Role = new Role();
User = new User();
}
public Role Role { get; set; }
public User User { get; set; }
public bool IsAssigned { get; set; } //true if user has role
}
public class UserEditing
{
public UserEditing()
{
RoleUserAssignments = new List<RoleUserAssignment>();
}
public List<RoleUserAssignment> RoleUserAssignments { get; set; }
}
HTML
#model UserEditing
<script>
$(document).ready(function () {
$('#rolesSelection').bootstrapDualListbox({});
});
</script>
<form id="userEditorForm">
<div>Roles</div>
<select id="rolesSelection" class="form-control" class="form-control" asp-for="#Model.RoleUserAssignments" asp-items="#(new SelectList(Model.RoleUserAssignments, "Role.RoleID", "Role.RoleName"))"
multiple="multiple">
</select>
</form>
ViewUserEditorPartial action:
[HttpGet]
public IActionResult ViewUserEditorPartial(int UserID)
{
UserEditing userEditing = new UserEditing();
userEditing.RoleUserAssignments = _userAdmin.GetRoleUserAssignmentsByUserID(_appSettings.MOPConfigConnectionString, UserID);
return PartialView("_UserEditorPartial", userEditing);
}
What I'm struggling with is figuring out how to make sure that the
roles belonging to the user end up in in the listbox on the right.
To achieve this function, you can avoid implementing it in js, it will be easier to implement it in the controller.
You can first get the RoleID data lists that the user belongs to the role, then put the lists into the ViewData in the ViewUserEditorPartial action, and then bind the value of ViewData to the asp-for attribute when binding the select.
Here is my demo:
[HttpGet]
public IActionResult ViewUserEditorPartial(int UserID)
{
UserEditing userEditing = new UserEditing();
var roleUserAssignData = _userAdmin.GetRoleUserAssignmentsByUserID(_appSettings.MOPConfigConnectionString, UserID);
userEditing.RoleUserAssignments = roleUserAssignData;
// get the data which IsAssigned field are true and select the RoleID of each data.
ViewData["SelectedRoles"] = roleUserAssignData.Where(x => x.IsAssigned).Select(x => x.Role.RoleID).ToList();
return PartialView("_UserEditorPartial", userEditing);
}
_UserEditorPartial view:
#model UserEditing
<script>
$(document).ready(function () {
$('#rolesSelection').bootstrapDualListbox({});
});
</script>
<form id="userEditorForm">
<div>Roles</div>
<select id="rolesSelection" class="form-control" class="form-control"
asp-for="#ViewData["SelectedRoles"]"
asp-items="#(new SelectList(Model.RoleUserAssignments, "Role.RoleID", "Role.RoleName"))"
multiple="multiple">
</select>
</form>
Update
If allowed, add List<int> type field named SelectedRoles in UserEditing model to store the selected roles is better.
public class UserEditing
{
public UserEditing()
{
RoleUserAssignments = new List<RoleUserAssignment>();
}
public List<int> SelectedRoles { get; set; }
public List<RoleUserAssignment> RoleUserAssignments { get; set; }
}
In ViewUserEditorPartial action, change ViewData sentence to the following:
userEditing.SelectedRoles = roleUserAssignData.Where(x => x.IsAssigned).Select(x => x.Role.RoleID).ToList();
_UserEditorPartial view:
<form id="userEditorForm">
<div>Roles</div>
<select id="rolesSelection" class="form-control" asp-for="#Model.SelectedRoles"
asp-items="#(new SelectList(Model.RoleUserAssignments, "Role.RoleID", "Role.RoleName"))"
multiple="multiple">
</select>
</form>

ASP.NET Core - Updateing Table Row Using Drop-Down List

I have a table containing a list of games. Each row contains a game ID, a drop-down list containing all the versions of the game and a status which currently shows the status of the latest version.
I want to update a single row on the table based on what the version drop-down list value contains which should change the value of the status cell.
This change should also change the ActiveVersion field in the view model.
I think this is achievable by using AJAX, model binding and potentially partial views but I'm unsure on how to do it.
I have attempted to simplify my problem by using versioning of games with strings and integers as data types as an example as I am using complex models and viewmodels in my webapp.
I have an MVC view as follows
#model IEnumerable<GameViewModel>
...
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => #item.Versions.Where(x => x.Version == item.ActiveVersion).FirstOrDefault().Status)
</td>
<td>
<select asp-for="#item.ActiveVersion" asp-items="#item.VersionsList"></select>
</td>
</tr>
}
</tbody>
My view model is as follows
public class GameViewModel
{
public string Id { get; set; }
public List<GameVersion> Versions { get; set; }
public string ActiveVersion { get; set; }
//Constructor - initialises active version to highest version
public GameViewModel(Game game)
{
Id = game.Id;
Versions = game.Versions;
ActiveVersion = game.Versions.Max(x => x.Version).ToString();
}
//Returns a list of all versions to be used in dropdown
public List<SelectListItem> VersionsList
{
get
{
List<SelectListItem> versionList = new List<SelectListItem>();
foreach (GameVersion gv in Versions)
{
versionList.Add(new SelectListItem { Value = gv.Version.ToString(), Text = gv.Version.ToString() });
}
return versionList;
}
}
}
My models are as follows
public class GameVersion
{
public int Version { get; set; }
public string Status { get; set; }
}
public class Game
{
public string Id { get; set; }
public List<GameVersion> Versions { get; set; }
}
I am using ASP.NET Core 3.1 to develop a MVC webapp.
You can use jQuery to control the implementation of the drop-down list to dynamically update the value of the state, according to the model you provide.
Here is a working demo like below:
Model:
public class GameVersion
{
public int Version { get; set; }
public string Status { get; set; }
}
public class Game
{
public string Id { get; set; }
public List<GameVersion> Versions { get; set; }
}
public class GameViewModel
{
public string Id { get; set; }
public List<GameVersion> Versions { get; set; }
public string ActiveVersion { get; set; }
//Constructor - initialises active version to highest version
public GameViewModel(Game game)
{
Id = game.Id;
Versions = game.Versions;
ActiveVersion = game.Versions.Max(x => x.Version).ToString();
}
//Returns a list of all versions to be used in dropdown
public List<SelectListItem> VersionsList
{
get
{
List<SelectListItem> versionList = new List<SelectListItem>();
foreach (GameVersion gv in Versions)
{
versionList.Add(new SelectListItem { Value = gv.Version.ToString(), Text = gv.Version.ToString() });
}
return versionList;
}
}
}
View(Index.cshtml):
#model IEnumerable<GameViewModel>
<table>
<tbody>
#foreach (var item in Model)
{
<tr id="#item.Id">
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
<span>
#*change this line*#
#Html.DisplayFor(modelItem => #item.Versions.Where(x => x.Version.ToString() == item.ActiveVersion).FirstOrDefault().Status)
</span>
</td>
<td>
<select class="activeVersion_#item.Id" asp-for="#item.ActiveVersion" asp-items="#item.VersionsList"></select>
</td>
</tr>
}
</tbody>
</table>
#section Scripts{
<script>
$(function () {
var gameModel =#Json.Serialize(Model);
$('*[class^="activeVersion"]').change(function () {
var vers = $(this).find('option:selected').val()
console.log(vers)
var stat = $(this).parent().prev().find('span');
stat.empty();
var nodeid = $(this).attr('class').split('_')[1]
$.each(gameModel, function (index, game) {
if (nodeid == game['id']) {
console.log(game['versions'])
$.each(game['versions'], function (indx, version) {
if (vers == version['version'])
stat.text(version['status'])
})
}
})
})
})
</script>
}
Controller(For easy testing,I set the value manually):
public IActionResult Index()
{
var games = new Game
{
Id = "game",
Versions = new List<GameVersion> {
new GameVersion{ Version=1,Status="Status1"},
new GameVersion{ Version=2,Status="Status2"},
new GameVersion{ Version=3,Status="Status3"},
},
};
var games2 = new Game
{
Id = "game2",
Versions = new List<GameVersion> {
new GameVersion{ Version=4,Status="Status4"},
new GameVersion{ Version=5,Status="Status5"},
new GameVersion{ Version=6,Status="Status6"},
},
};
var gameviewModels = new List<GameViewModel> {
new GameViewModel(games),
new GameViewModel(games2)
};
return View(gameviewModels);
}
Result:
There are a number of ways to achieve this.
A simple one that doe snot require Ajax is to wrap each select within a form tag which posts to an action that takes an ID and VERSION. Within this form add a hidden field for the ID.
Add an OnChange event to the select and use javascript to post back the form.
At the action, perform your update and redirect back to your index action that displays the list
If you want to use ajax then create a javascript method passes your select control as a parameter instead of posting back. From this parameter you can get the select value. You can get the previous control (the hidden ID input) to get that value then pass these back to your action.
Your action can pass back a JSON result of OK or an error or even the table cell html. Assuming success, either with HTML passed back by the action (you can use a partial view which replaces the original table cell content) or already knowing having the correct select action already displayed then you are done. If there is an error you have to decide to show that message and whether to put the select back to the original value

How to create a custom validation attribute with parameters consisting of model properties

I am currently trying to make a site on asp.net core. When i press the submit button, i want all the validation messages to come up if there is incorrect input.
For now, i have the name and the phone number validation messages working using 'asp-validation-for=Client.Name' and Client.ContactNumber. Now i want to be able to validate from the server side if the user has checked at least one of the boxes off, or filled in the 'Other' field. So i tried to see if i can make a custom attribute which would validate it but havent gotten any luck. I have tried sending in the properties of the Client class but it throws an error saying "An object reference is required for the non-static field, method, or property 'Client.'". I have also tried the top solution here CS0120: An object reference is required for the nonstatic field, method, or property 'foo' , but I think in my case those solutions are not possible.
My code for the files that i am working with are below
Code for Client.cs (model class)
public class Client
{
//some attributes
[Required]
public string Name { get; set; }
[Required]
[DisplayName("Bookkeeping")]
public bool Bookkeeping { get; set; }
[Required]
[DisplayName("Personal Income Taxation")]
public bool Personal_Income_Taxation { get; set; }
[Required]
[DisplayName("Self-Employed Business Taxes")]
public bool Self_Employed_Business_Taxes { get; set; }
[Required]
[DisplayName("GST/PST/WCB Returns")]
public bool GST_PST_WCB_Returns { get; set; }
[Required]
[DisplayName("Tax Returns")]
public bool Tax_Returns { get; set; }
[Required]
[DisplayName("Payroll Services")]
public bool Payroll_Services { get; set; }
[Required]
[DisplayName("Previous Year Filings")]
public bool Previous_Year_Filings { get; set; }
[Required]
[DisplayName("Govt. Requisite Form Applicaitons")]
public bool Government_Requisite_Form_Applications { get; set; }
public string Other { get; set; }
[CheckboxAndOtherValidation(bookkeeping: Bookkeeping,
personal_Income_Taxation: Personal_Income_Taxation,
self_Employed_Business_Taxes: Self_Employed_Business_Taxes,
gST_PST_WCB_Returns: GST_PST_WCB_Returns,
tax_Returns: Tax_Returns,
payroll_Services: Payroll_Services,
previous_Year_Filings: Previous_Year_Filings,
government_Requisite_Form_Applications: Government_Requisite_Form_Applications,
other: Other)]
public bool AreCheckboxesAndOtherValid { get; set; }
FreeConsultation.cshtml (view page)
<div class="container" style="padding:30px;">
<br />
<h1 class="text-info">Create New Client</h1><br />
<form method="post">
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
<div class="form-group row">
<div class="col-3">
<label asp-for="Client.Name"></label>
</div>
<div class="col-6">
<input type="text" asp-for="Client.Name" class="form-control" />
</div>
<span class="text-danger col-3" asp-validation-for="Client.Name"></span>
</div>
<!-- More code -->
My utility class i created for custom validation attribute - CheckboxAndOtherValidation.cs
public class CheckboxAndOtherValidation : ValidationAttribute
{
private readonly bool Bookkeeping;
private readonly bool Personal_Income_Taxation;
private readonly bool Self_Employed_Business_Taxes;
private readonly bool GST_PST_WCB_Returns;
private readonly bool Tax_Returns;
private readonly bool Payroll_Services;
private readonly bool Previous_Year_Filings;
private readonly bool Government_Requisite_Form_Applications;
private readonly string Other;
public CheckboxAndOtherValidation(bool bookkeeping,
bool personal_Income_Taxation,
bool self_Employed_Business_Taxes,
bool gST_PST_WCB_Returns,
bool tax_Returns,
bool payroll_Services,
bool previous_Year_Filings,
bool government_Requisite_Form_Applications,
string other)
{
this.Bookkeeping = bookkeeping;
this.Personal_Income_Taxation = personal_Income_Taxation;
this.Self_Employed_Business_Taxes = self_Employed_Business_Taxes;
this.GST_PST_WCB_Returns= gST_PST_WCB_Returns;
this.Tax_Returns = tax_Returns;
this.Payroll_Services = payroll_Services;
this.Previous_Year_Filings = previous_Year_Filings;
this.Government_Requisite_Form_Applications = government_Requisite_Form_Applications;
this.Other = other;
}
public override bool IsValid(object value)
{
return base.IsValid(value);
}
}
Is there any other way to tackle this? (Preferably with custom attribute). First time posting a question so pardon me if there is anything missing. Thanks
EDIT
I was able to make the checkbox error come from the server side with the help of a solution, but i still have the issue that when i press the submit button, the checkbox error doesn't come at the same time as the other two. By following a tutorial, i was able to show the other two errors from the client side by including this piece of code in my view which comes with the creation of an asp.net core project
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
_ValidationScriptsPartial.cshtml
<environment include="Development">
<script src="~/Identity/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment exclude="Development">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/Identity/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="SOME INFO">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="SOME INFO">
</script>
</environment>
Some references are here
Article custom-data-annotation-validation-in-mvc
Source code of CompareAttribute.cs and ComparePropertyAttribute.cs
3rd-party ExpressiveAnnotations
So in a similar way, a custom ValidationAttribute can use reflection to get runtime value by its property name (the name from declaration or argument) to see if the property have the required value or format you need.
public class CheckboxAndOtherValidation : ValidationAttribute
{
readonly object TRUE = true;
string[] _alltheOtherProperty;
public CheckboxAndOtherValidation(params string[] alltheOthersProperty)
{
_alltheOtherProperty = alltheOthersProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (_alltheOtherProperty?.Count() > 0 != true)
{
return ValidationResult.Success;
}
var otherPropertyInfo = validationContext.ObjectType.GetProperty(nameof(Client.Other));
if (otherPropertyInfo != null)
{
object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue != null && !string.IsNullOrEmpty(otherPropertyValue.ToString()))
{
return ValidationResult.Success;
}
}
for (var i = 0; i < _alltheOtherProperty.Length; ++i)
{
var prop = _alltheOtherProperty[i];
var propertyInfo = validationContext.ObjectType.GetProperty(prop);
if (propertyInfo == null)
{
continue;
}
object propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
if (Equals(TRUE, propertyValue))
{
return ValidationResult.Success;
}
}
return new ValidationResult("Must exist at least one field is true", _alltheOtherProperty);
}
}
Cus I choose passing property name array list as argument, now class Client could have a simplified and clearer usage like this,
public class Client
{
[CheckboxAndOtherValidation(nameof(Bookkeeping),
nameof(Personal_Income_Taxation),
nameof(Self_Employed_Business_Taxes),
nameof(GST_PST_WCB_Returns),
nameof(Tax_Returns),
nameof(Payroll_Services),
nameof(Previous_Year_Filings),
nameof(Government_Requisite_Form_Applications))]
public bool AreCheckboxesAndOtherValid { get; set; }
}

RadioButtonFor in mvc shows required validation even though not applied required validation

I have one view in which I put two radio button for attribute in my model
I just put data annotation for other field but not the radiobutonfor field but steel it show required validation.Below is my code.I the attribute is int type in model.I used javascript unobtrusive library inn view as well.
<td>
<label>#Html.RadioButtonFor(m => m.OneToOne, 1) Hours </label>
<label>#Html.RadioButtonFor(m => m.OneToOne, 2) Unit </label>
</td>
I am using Html.begin from to post this value.
The RadioButtonFor helper method generates html markup for the radio button input with data-val-required attribute unless you specify the property as nullable type! The jQuery validate plugin does validation on this input because of the existence of this attribute.
If you do not want client side validation on this input, You should change the property type from int to nullable int(int?).
public class YourViewModel
{
// Other properties
public int? OneToOne { set; get; }
}
If radio buttons are not required to select, I personally like to use mutually exclusive checkboxes.
Mainly, if a user accidental selects a radio button, s/he won't be able to uncheck it back unless the user refreshes the entire page. I feel like it is really annoying.
Sample at jsfiddle.net
Model
public class ViewModel
{
public bool OneToOneHours { get; set; }
public bool OneToOneUnit { get; set; }
}
View
#using (Html.BeginForm("Index", "Home", null, FormMethod.Post))
{
<div class="form-control">
#Html.CheckBoxFor(model => model.OneToOneHours, new {#class = "mutually-exclusive"}) Hours
#Html.CheckBoxFor(model => model.OneToOneUnit, new {#class = "mutually-exclusive"}) Unit
</div>
<button id="btnSubmit" type="submit">Submit</button>
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$('input.mutually-exclusive').click(function () {
var checkedState = $(this).val();;
$('input.mutually-exclusive').attr("checked", false);
$(this).prop("checked", checkedState);
});
</script>
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ViewModel model)
{
int? oneToOne;
if (model.OneToOneHours)
oneToOne = 1;
else if (model.OneToOneUnit)
oneToOne = 2;
return View(model);
}
}

can we pass entire model to javascript asp.net mvc

I have a problem that on javascript call for the form submit , the model gets updated from controller ,but it is not updating in the view. I am thinking to update the model to new model values in javascript . so that the view shows the latest model values
can that be done?
thanks,
michael
Your question is extremely unclear and you provided no source code which makes things even more unclear. From the various comments you may have posted I assume that you are trying to update some model value inside the POST action without removing it from the model state and when the same view is rendered again the old values are displayed.
So I suppose you have a view model that looks something close to this:
public class MyViewModel
{
public HttpPostedFileBase File { get; set; }
public string SomeValue { get; set; }
}
and a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
SomeValue = "initial value"
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// Notice how the SomeValue property is removed from the
// model state because we are updating its value and so that
// html helpers don't use the old value
ModelState.Remove("SomeValue");
model.SomeValue = "some new value";
return View(model);
}
}
and a view:
<% using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" })) { %>
<div>
<%= Html.LabelFor(x => x.SomeValue) %>
<%= Html.EditorFor(x => x.SomeValue) %>
</div>
<div>
<label for="file">Attachment</label>
<input type="file" name="file" />
</div>
<input type="submit" value="OK" />
<% } %>

Categories

Resources