Update label depending on selected value on dropdownlist with knockout - javascript

In ASP.NET MVC, I have a form. In this form the user selects a country, and then the ID is posted back to the server, using a normal #using(Html.BeginForm) .
However, for some UX reasons, I use Knockout. Therefore I need to get my observable value countryId to have the value of a dropdownlist.
I want my label in the markup, to show the countryId, depending on the selected value on the dropdownlist.
Markup:
#Html.DropDownListFor(model => model.SelectedCountry, Model.Countries, new Dictionary<string, object> { { "class", "form-control sendaletter_countrylist" }, { "data-bind", "value:countryId" }})
<label data-bind="text: countryId"></label>
ViewModel:
public class CreateSingleLetterModel
{
public CreateSingleLetterModel()
{
this.Countries = new List<SelectListItem>();
}
public string SelectedCountry { get; set; } // expected to be the ID of the SelectListItem
public List<SelectListItem> Countries { get; set; }
}
Then my question is:
How do I modify my DropDownListFor, so the countryId is being set automatically? :-)
Thanks a lot! I really enjoy learning Knockout, but this one has taken me a long time!

All you've done looks correct.
Now you need to define the client-side view model (where countryId will be ko.observable) and call ko.applyBindings
var viewModel = {
countryId: ko.observable(0)
};
ko.applyBindings(viewModel);
example: http://jsfiddle.net/tabalinas/xJ7mm/

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>

Pass one extra parameter along with HttpPostedFileBase for Single File Upload

In my MVC application I have a View containing
1. One drop down list
2. One File Upload
3. One button for submitting the form.
form code (View)
<form id="upload" enctype="multipart/form-data" action="#Url.Action("ExcelUpload", "NBL")" method="POST">
#Html.DropDownList("CustomerName", new List<SelectListItem>
{
new SelectListItem() {Text = "Customer 1", Value="CM1"},
new SelectListItem() {Text = "Customer 2", Value="CM2"}
}, new { #class = "form-control", #style = "width:auto" })
<input type="file" name="fileUpload" id="fileUpload" size="23" style="margin-top:5px"/>
<button class="btn btn-primary">Upload</button>
</form>
I am able to pass my File in Controller successfully when I click the button.
Controller code
public ActionResult ExcelUpload(HttpPostedFileBase FileUpload)
{
if (FileUpload != null)
{
// Do Stuff here.
}
}
My Problem is I also want the drop down selected value in the controller when I click the button. How can I pass both the Drop down Selected value and the file together in the controller?
The name of the dropdown element is CustomerName and it's within the form. The browser will post it to the server as a key-value pair with the key being the name of the dropdown and the value will be the value the user has selected.
The MVC framework's default binder will look for an action named ExcelUpload that either has a parameter string customerName or the action has a complex type (Model) that has CustomerName as the property.
Change your action to:
ExcelUpload(HttpPostedFileBase FileUpload, string customerName)
The problem is your model as it is does not represent the view or data you want to use. So make a model like so:
using System.ComponentModel.DataAnnotations;
namespace Models
{
public class MyUploadModel
{
[Required]
public HttpPostedFileBase File { get; set; }
[Required]
public string CustomerName { get; set; }
}
}
Use that model in your views to generate the form and then in controller:
public ActionResult ExcelUpload(Models.MyUploadModel myModel)
{
if (ModelState.IsValid)
{
// all good, save
}
return View(myModel);
}
Of course you could just add a string-parameter to your action, but this is much more robust and represents the MVC-pattern (Model/View/Controller).

Struggling with filtering dropdown with another dropdown ASP.NET MVC

This is my first ASP.NET MVC application, and I'm really struggling with some stuffs, right now I have big issue and I'm stucked here for over 5 hours allready, I'm trying to filter my dropdown with selection from another dropdown, before I post my code I want to say that I've followed this post:
How to filter the options of a drop down list using another drop down list
So let's start:
Regardless of the others fields, I have a lets say two dropdowns also, one represent MAIN CATEGORY, another represent SUB CATEGORY, and on MAIN CATEGORY selection, SUB CATEGORY should be loaded in dropdown and user should be able to choose it.
I want to create view where user should be able to fill some date and post it back to server, but before he post it back he need to choose date properly MAINCATEG -> SUBCATEG so this is how I did it so far:
My action result 'Create' method:
public ActionResult Create()
{
// I did not wrote other stuffs because they are not important in my question and code will be clearer.
//First I'm filling MAIN CATEGORY dropdown with data from my database
List<Groups> articleGroups = GroupController.GetActiveMainGroups();
// Attach list on ViewBag, because this view Bag will be used to populate main dropdown
ViewBag.articleGroups = articleGroups;
//Here is second list which should populate second dropdown, right now I get all subgroups from database, because it didn't let me
//run my application if list was empty (probably I don't need this in future because I will filter second dropdown by selecting something from dropdown above)
List<SubGroups> subGroups = GroupController.GetAllSubGroups();
// Attach list on ViewBag, it will be used for generating dropdown list.
ViewBag.subGroups = subGroups;
return View(model);
}
Here is also one method which I thought I can call throught javascript by triggering event 'change' on first (main) dropdown:
public ActionResult GetSubgroupByMainGroup(Guid id)
{
List<SubGroups> subGroups = GroupController.GetAllSubGroupsByMainGroup(id);
return Json(subGroups, JsonRequestBehavior.AllowGet);
}
Here is my VIEW:
#model MyProject.Web.Models.ArticleNewViewModel
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
#using MyProject.Model
#{
ViewBag.Title = "Add new Article";
}
<h3>Adding new article to database</h3>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.MainGroupID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.MainGroupID, new SelectList(ViewBag.articleGroups , "MainGroupID", "Group.Name"))
#Html.ValidationMessageFor(model => model.MainGroupID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SubGroupID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SubGroupID, new SelectList(ViewBag.subGroups , "SubGroupID", "SubGroup.Name"))
#Html.ValidationMessageFor(model => model.SubGroupID)
</div>
</div>
//I don't understand this code really, because I have so little experience with javascript&jquery
<script type="text/javascript">
$(function () {
$("#MainGroupID").change(function () {
var val = $(this).val();
var subItems="";
$.getJSON("#Url.Action("GetSubgroupByMainGroup", "Article")", {id:val} ,function (data) {
$.each(data,function(index,item){
subItems+="<option value='"+item.Value+"'>"+item.Text+"</option>"
});
$("#SubGroupID").html(subItems)
});
});
});
</script>
}
<div>
#Html.ActionLink("Go back", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
It is interesting that when I set breakpoint in my method public ActionResult GetSubgroupByMainGroup(Guid id) it's not being hitted, that means this code is not executed, and here is my
ArticleNewViewModel
:
public class ArticleNewViewModel
{
[Required]
[Display(Name = "Article code")]
public string Code { get; set; }
[Required]
[Display(Name = "Article Title")]
public string Title { get; set; }
//Here is ID's which should represent value from MAIN and value from SUBGROUP
[Required]
[Display(Name = "Main group")]
public Guid MainGroupID { get; set; }
[Required]
[Display(Name = "Sub Group")]
public Guid SubGroupID { get; set; }
}
I probably did few mistakes here but I really don't know where and what, because I'm not familiar with javascript & jquery, and I don't know is there another way to do it :/
Whatever I would like to fix this so if someone of you guys might help me I would appreciate so much!
Thanks guys
Cheers

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);
}
}

How to clear textboxes besides using JS

I am building an app using MVC, and this question pertains to the Create page and action.
Lets say my model has 2 decimal properties along with other properties but aren't necessary for this example:
public class TestClass
{
public int ID { get; set; }
public decimal DecimalProperty { get; set; }
public decimal SecondDecimalProperty { get; set; }
// more properties below this, but deemed unnecessary for this question
}
Obviously these properties are non-nullable, so in my Create View they appear as so on page load (ignore the 2nd textbox):
Now my goal is to clear those textboxes out, so they are just blank.. so I used JS to achieve that by doing:
$(".clear-textbox").val("");
I put a class called clear-textbox on those input fields.. works perfectly.. but now in my HttpPost Create Action I have conditional statements checking to see if other fields are valid, and if not return the object.. like so:
if (object.property== 0)
{
ModelState.AddModelError("property", "This field is required!");
return View(object);
}
This results in the Create view to be redisplayed with the values that the user has already entered, along with an error message below the one property that needs to be changed.. and this is where the problem lies. Once the Create view is reloaded.. then so are the scripts for clear-textbox, resulting in DecimalProperty and SecondDecimalProperty to be empty text-boxes.. instead of keeping what the user originally entered for them.
So my question, is there another way to clear out textboxes for decimal properties other than using javascript?
Any help is appreciated.
UPDATE
Here is the cshtml.
<div class="form-group">
#Html.LabelFor(model => model.DecimalProperty, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.DecimalProperty, new { htmlAttributes = new { #class = "form-control clear-textbox" } })
#Html.ValidationMessageFor(model => model.DecimalProperty, "", new { #class = "text-danger" })
</div>
</div>
Either you have to do it via Javascript on load like following
$(".clear-textbox").each(function(){
if($(this).val() <= 0 )
$(this).val("");
});
OR
You can create your own MVC Html Helper which will do things as you need for your special needs. Let me know if you want code for that...
You can also refer this link
You can set the default value as a data- attribute of the textbox and clear it only if they match. Like:
$(".clear-textbox").each(function(){
var $this = $(this);
if( $this.val() == $this.data().defaultvalue ) $this.val('');
});
It's hard to come up with an answer without knowing how the text boxes are being rendered. However, I'm assuming you are using something like
#Html.TextBoxFor
or
#Html.EditorFor
There are two ways to do this.
1. Add a DisplayFormat attribute to the model fields and use EditorFor:
public class TestClass
{
public int ID { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:#.#}")]
public decimal DecimalProperty { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:#.#}")]
public decimal SecondDecimalProperty { get; set; }
}
#Html.EditorFor(model => model.DecimalProperty)
2. Use the inline format attribute:
#Html.TextBoxFor(model => model.DecimalProperty, "{0:#.#}")

Categories

Resources