Here are my models
public class AddressBook
{
public string UserId { get; set; }
public IList<Address> Addresses { get; set; }
public AddressBook()
{
UserId = "";
Addresses = new List<Address>();
}
}
public class Address
{
public string Company { get; set; }
public string Title { get; set; }
public string FName { get; set; }
...
}
The controller builds the AddressBook with a list of addresses.
The main page uses the AddressBook model (#model mymodel.AddressBook) and I can access the different addresses using Model.Addresses[index].
On the page I display the list of addresses each with an Edit button (I stripped the html code off for clarity):
#model mymodel.AddressBook
...
#for (int i = 0; i < Model.Addresses.Count; i++)
{
#Model.Addresses[i].Company
#Model.Addresses[i].FName
...
#:<input type="image" src="/images/edit.gif" onclick="addressEdit('#i'); return false;" title="Edit" />
}
When the user clicks on the edit button I call javascript addressEdit and pass it the index of the selected address.
<script type="text/javascript">
function addressEdit(index) {
$('#FName').val('#Model.Addresses[index].FName');
$('#Addr1').val('#Model.Addresses[index].Company');
...
}
</script>
The problem is on the jQuery lines $('#FName').val('#Model.Addresses[index].FName'); Variable index is underlined in red in VS2012 with message "the name 'index' does not exist in the current context".
How do you pass the value on 'index' to extract the data I need?
Wrap your elements in a spans with some class name. Wrap everything in the loop a div too. I am also removing the onclick method from the markup because we will do it in the unobtrusive way.
#for (int i = 0; i < Model.Addresses.Count; i++)
{
<div class='entry'>
<span class='spanAddress'> #Model.Addresses[i].Company </span>
<span class='spanFName'> #Model.Addresses[i].FName </span>
<img src="/images/edit.gif" class='editLink' />
</div>
}
Now in your javascript
$(function(){
$(".editLink").click(function(e){
e.preventDefault();
var _this=$(this);
var fname=_this.closest("div.entry").find(".spanFName").html();
var add=_this.closest("div.entry").find(".spanAddress").html();
$('#FName').val(fname);
$('#Address').val(add);
});
});
Well, this is client side code remember. So the best place to look is what is generated client side. This means commenting out your javascript event and looking at what is actually rendered to get an idea of what is going on.
When you do that, you will see that the helper autogenerates the names and ids based on your models names. Keep in mind that the name attribute on the inputs is what allows for model binding on post.
So, with all of that in consideration, I would assume it would be something along these lines:
function addressEdit(index) {
$('#FName').val($("#Addresses_"+index+"__FName").val());
//..etc
}
Related
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>
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).
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);
}
}
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:#.#}")
I was wondering if anyone could explain how to manipulate content for various sections of a page depending on if a button is clicked. I think what I am looking for is similar to an include in php. I wasnt sure if asp.net had a way to do the same (partial view?) or if bootstrap/jquery is the way to go. I've included a .png to illustrate what I am trying to do.
I would like section b's content to change based on what button is selected in section A. While not necessarily relevant to this question.. I then would like various user inputs in section B to manipulate existing content in section C.
In your controller, have an action that returns a PartialView:
public PartialViewResult MyPartial(string someText)
{
var model = new MyPartialModel {SomeStuff = someText};
return PartialView(model);
}
Create the model and partial view as you would any other:
public class MyPartialModel
{
public string SomeStuff { get; set; }
}
Partial View:
#model ExampleApp.Models.MyPartialModel
#Html.TextBoxFor(m => m.SomeStuff)
Then on your page you can load in your partial via ajax with jQuery:
<div>
<button type="button" id="load-partial">Load The Partial!</button>
</div>
<div id="section-b"></div>
#section Scripts{
<script>
$(document).ready(function () {
$('#load-partial').click(function () {
$.get('MyPartial', { sometext: "Hello!" }).done(function (data) {
$('#section-b').html(data);
});
});
});
</script>
}
Edit to answer comment:
If you don't want to instantiate a new model in the controller each time, you can pass the model (more or less) directly from the view.
In you controller, have a very simple action that accepts a model as a parameter and returns the partial view. Note the HttpPost attribute.
[HttpPost]
public PartialViewResult MyPartial(MyPartialModel model)
{
return PartialView(model);
}
The model's got more than one property this time:
public class MyPartialModel
{
public string Name { get; set; }
public int Age { get; set; }
}
The partial's pretty much the same, except it now displays the new properties of the model.
#model MVCPlayGround.Models.MyPartialModel
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.Age)
The jquery on the main page/view is very also similar, but uses POST instead of GET.
// these could be anything, from control on the page, or whatever
var name = "James";
var age = 30;
$(document).ready(function () {
$('#load-partial').click(function () {
// note that Name and the Age are the names of the properties in our model
$.post('MyPartial', { Name: name, Age: age }).done(function (data) {
$('#section-b').html(data);
});
});
});
This works because when data transmitted via POST, it's treated as form data, and when the controller's deciding which action to use it'll look at the parameters for the actions, and compare them to the form data available. The MyPartialModel contains properties that match the form data, so it chooses that action. There are other subtle rules, but that's basically it. Behind the scenes it'll still be instantiating a model in the controller, it's just in the framework, not in the code you've written.
Another edit
Having just re-read your comment I don't think I've answered it fully.
If you want to save the changes you've made in a partial view to the main view, have some hidden fields in the main view to hold this data.
<input type="hidden" id="name-holder" />
<input type="hidden" id="age-holder" />
And then when you want to store a value to them, just set the values with jquery:
$('#some-save-button-maybe').click(function(){
$('#name-holder').val($('id-of-name-on-partial').val());
$('#age-holder').val($('id-of-age-on-partial').val());
});
When you click on a the button to show a partial, send the appropriate data to the controller to render in the partial:
$('#load-partial').click(function () {
$.post('MyPartial', { Name: $('#name-holder').val(), Age: $('#age-holder').val() }).done(function (data) {
$('#section-b').html(data);
});
});
Hopefully that's what you need...
Yes there are partial views in MVC, and they are usually belong in the Views/Shared folder of your project and are prefixed with a _ (i.e. _MyPartial.cshtml.
As #AdamHeeg pointed out in the comments, there are many tutorials on the web about this kind of setup and many different ways to achieve what you are after.
Here is roughly how I might tackle it...
HTML
<nav>
#Html.ActionLink("Button 1", "GetSectionB")
</nav>
<section id="sectionB">
<!-- Content here -->
</section>
JavaScript
$('nav a').on('click', function (e) {
e.preventDefault();
$.get(this.href, function (html) {
$('#sectionB').html(html);
});
});
Controller
public PartialViewResult GetSectionB()
{
var vm = new MyViewModel();
//do stuff
return PartialView("_SectionB", vm);
}