I have a form built using ASP.NET MVC 5 using #Html.TextBoxFor to populate the form with my model (e.g, after a form navigation or server side validation failure).
I have now introduced a client side address lookup using Angular which means some of the form fields are now decorated with ng-model to enable the Angular lookup to populate the searched address.
e.g.:
#Html.TextBoxFor(m => m.Town, new { #class="form-control", ng_model="address.town" })
Adding the ng-model now overrides the value set from the MVC model when the page is reloaded.
Viewing the source in the browser shows that the textbox value is set correctly to Town from the MVC model but Angular then comes along and populates it with address.town which is empty so the form displays no value.
How can I prevent Angular from doing this?
You can use ng-init to force a value from MVC
<input name="Town" type="text" ng-model="address.town" ng-init="address.town= #Model.Town" />
#Html.TextBoxFor(m => m.Town, new { ng_model="address.town", ng_init="address.town= Model.Town" })
Alternatively, I use a directive which I found here https://stackoverflow.com/a/22567485/2030565
app.directive('input', function ($parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, element, attrs) {
if (attrs.ngModel) {
val = attrs.value || element.text();
$parse(attrs.ngModel).assign(scope, val);
}
}
}; });
You could set a variable in a Javascript section at the bottom of the server template that your Angular controller assigns to the model on initialization.
<script>
My.Namespace.town = #Html.Raw(Model.Town);
</script>
Related
I'm developing an ASP.NET MVC app using the latest .NET Framework and VS 2019.
In my page, I have an simple textbox and a search button. When I click the search button and postback the form, in controller, I get model.Code is NULL
Here is my code - my view model:
public class UserManagementViewModel
{
public string Code { get; set; }
// some other properties including simple and complex types
}
In View:
<td style="min-width: 300px">
#Html.TextBoxFor(m => m.Code)
</td>
In Controller:
[HttpPost]
public ActionResult UpdateUserPermissions(UserManagementViewModel model)
{
// model.Code is null here!!
return RedirectToAction("UserManagement");
}
After hours of research, I tried following:
I looked into html source and found
<input id="Code" name="Code">
Using Chrome dev tool, I change the name of the input to "Model.Code"
<input id="Code" name="Model.Code">
And this time, I got the entered value in textbox after postback.
I don't know why this happens and why the element name is not generated correctly!
What can I do?
Edit:
I have some other controls like checkbox and radio-buttons which I use pure HTMl code and name them beginning with "Model." like below instead of using #HTML helper.
And it works fine and I get the value in Action.
Workaround:
I found a workaround here: ASP.NET MVC 4 override emitted html name and id so I change my code to:
#Html.TextBoxFor(m => m.Code, new { #Name = "Model.Code" })
However this code helps me to fix my problem, I still don't know why the name generated by Razor engine is not working.
The helper method #Html.TextBoxFor works absolutely correctly. Basically, you need to use this method for almost all situations (except some situations when you need to use no-standard behavior).
Regarding your case, I think you have trouble with HtmlFieldPrefix.
This property is used for the generation of HTML input's name. We can review MVC source codes in GitHub...
MVC generated HTML input (GitHub link) using this code snippet:
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
}
TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullName, true);
string valueParameter = htmlHelper.FormatValue(value, format);
bool usedModelState = false;
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
}
As we can see name property calculated by method GetFullHtmlFieldName.
This method we can also inspect on GitHub (link):
public string GetFullHtmlFieldName(string partialFieldName)
{
if (partialFieldName != null && partialFieldName.StartsWith("[", StringComparison.Ordinal))
{
// See Codeplex #544 - the partialFieldName might represent an indexer access, in which case combining
// with a 'dot' would be invalid.
return HtmlFieldPrefix + partialFieldName;
}
else
{
// This uses "combine and trim" because either or both of these values might be empty
return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
}
}
This method calculates the name using the HtmlFieldPrefix property. So if you change this property you can modify your generated name.
I think you use partial Views and you need to render property with some prefix for correct binding.
Please review next links with examples and possible solutions:
ASP.NET MVC Partial Views with Partial Models
ASP.NET MVC3 add a HtmlFieldPrefix when calling Controller.PartialView
Define a custom HTML prefix for your .NET MVC models
in other to sumbmit model back you have to use form tags. MVC sometimes counts name as model property, but it is better to do it explicitly. This code was tested and both variants of textbox are working properly. And if you use the form you don't need any name at all, other contrary in the most cases you will get null if you use the name and form together.
#model UserManagementViewModel
<form asp-action="UpdateUserPermissions" method="post">
#Html.TextBoxFor(m => m.Code)
// or
<div class="form-group">
<label asp-for="Code" class="control-label"></label>
<input asp-for="Code" class="form-control" />
</div>
<div>
<button type="submit"> submit </button>
</div>
</form>
I have 2 ASP.NET MVC action methods, I call the first method by passing and load some initial data, then I get some additional details from UI and call the second action method (Post action method from .cshtml). The data I received from the first call is missing in the post method. can anyone help me what am I doing wrong or missing here?
Action methods:
[Route("setprofile")]
public ActionResult SetProfile(string id)
{
ProfileData data = new ProfileData();
//do something
data.name= getData(id);
return this.View(data);
}
[Route("setprofile")]
[HttpPost]
public ActionResult SetProfile(ProfileData data)
{
// Here I'm not missing the data.name field value
}
View .cshtml file:
<div class="panel-body">
#using (Html.BeginForm("SetProfile", "Home", FormMethod.Post))
{
<div>
<h3> Name: #(this.Model.name)</h3>
</div>
<h3>
Comments:#Html.TextBoxFor(m => m.comments)
</h3>
}
I get the comments value but not getting the name field value from the model here.
Note: I need to display the value I received from the first action method as a label, not text box.
There are two things, Name is writen as text and in order to send back to server, you need to put it inside input element.
IF you dont want to show it #Html.HiddenFor(m => m.name) creates hidden input element.
Other than this, check ModelState for validation errors..
if (!ModelState.IsValid)
return BadRequest(ModelState);
.... your code here
if your model is not valid, the ProfileData returns result
You haven't added an input element for it to be sent back to the server when the form is submitted. If you don't want it to be visible, whilst still being posted back, add a hidden field for it:
#Html.HiddenFor(m => m.name)
Without that, all you're doing is rendering name to the markup but, once the form is submitted, it won't be sent back. Alternatively, you could render a textbox for the value whilst setting its readonly attribute. That would allow it to be visible, not changed, and still be sent back to the server.
#Html.TextBoxFor(m => m.name, new { #readonly = "readonly" })
I'm trying to create a page for admins to create new users. When these users are created they will also have a role that they are assigned, and each of those roles will have a list of associated rights. The role will be selected from a dropdownlist of roles. After the role has been selected, but before the user has been created I want the list of associated roles to be displayed on the side of the form so they can see if the rights associated with that role are what they want.
I'm not entirely sure how to approach this problem without reloading the entire page after selecting a role. I'm sure how to have a refresh on a div when the information changes
You can use AJAX to achieve this.
Here is a simple solution to start with. First, create an action method in your controller which accepts the roleId value and get's the rights associated with that role and return that as JSON array.
In the below example, I am simply hard coding 2 rights. You can replace this implementation with however you want to get the data(may be from a database) using the roleId param value.
public JsonResult RoleDetails(int roleId)
{
// Hard coded data.
// Replace with data from your db using roleId value
var rightList = new List<string> { "Admin", "Editor" };
return Json(rightList);
}
So when called with a request URL like /Home/RoleDetails?roleId=2, this action method will return data like this(JSON array).
["Admin","Editor"]
Now, Render your select element with the Roles as options. We shall store the path to the action method in a data attribute on the SELECT element. For example, your rendered HTML should be like this.
<select id="selectedRole" data-url="/Home/RoleDetails">
<option>Select one</option>
<option value="1">Role A</option>
<option value="2">Role B</option>
<option value="3">Role C</option>
</select>
<div id="role-details"></div>
We also rendered a div to show the details.
I am assuming you know how to render a SELECT element. If you do not, please refer
Select Tag Helper in ASP.NET Core MVC post
You can use the Url.Action helper to generate the correct path to the RoleDetails action method, for example,
<select id="selectedRole" data-url="#Url.Action("RoleDetails","Home")">
Now you can use JavaScript to listen to the change event of the SELECT element, read the selected option value, make an ajax call to the action method we created above, get the data ,which is a JSON array, and update the UI using that.
Here is a working sample using jQuery for DOM manipulations.
$(function () {
// Listen to "change" event on SELECT
$("#selectedRole").change(function () {
//Get the value of selected option
var roleId = $(this).val();
var url = $(this).data("url") + "?roleId=" + roleId;
//Make the AJAX call
$.getJSON(url).then(function (data) {
var list = "";
// Loop through the JSON array and build HTML for a list of P tags.
$.each(data, function (index, item) {
list += "<p>" + item + "</p>";
});
// Update the Div content
$("#role-details").html(list);
}).fail(function (e) {
$("#role-details").html("Error getting data");
console.warn(e);
});
});
})
I am using EF in an ASP.Net MVC application and also using WebApi to get an element like so:
[HttpGet]
public Student GetStudent(int id)
{
return db.Students.AsNoTracking().FirstOrDefault(n => n.Student_ID == id);
}
This all works great and I map it directly into ko for use. However when I bind the value of any DateTime properties of the element i.e. date of birth I get: 1955-04-17T11:13:56. I don't want to change the web api methods or the model for every date, is there a 3rd party library or a knockout function that can handle a datetime from asp.net and correct the value for inputs.
As Sridhar suggested in the comment. You can use moment.js to achieve this.
I've created a sample fiddle here - http://jsfiddle.net/sherin81/ordwenj6/
Knockout code
function viewModel()
{
var self = this;
self.dateInput = ko.observable("1955-04-17T11:13:56");
self.formattedDate = ko.computed(function(){
var m = moment(self.dateInput());
return m.format("DD-MM-YYYY");
});
}
ko.applyBindings(new viewModel());
HTML
<input data-bind="value : dateInput" />
<br/>
<span data-bind="text : formattedDate()" />
For demonstration, I've used the value from the input field for formatting. You can modify the code to use the value from the webapi and format it using moment.js.
Update
To do the same using ko custom binding handler, do the following
ko.bindingHandlers.dateFormattedByMoment = {
update: function (element, valueAccessor, allBindingsAccessor) {
$(element).val(moment(valueAccessor()).format("DD-MM-YYYY"));
}
};
HTML
<input id="customBinding" data-bind="dateFormattedByMoment : dateInput()" />
Working fiddle here - http://jsfiddle.net/sherin81/ujh2cg73/
Here i am using a jquery datepicker from this sample http://dev.jtsage.com/jQM-DateBox2.
It is working fine but the problem is after clicking the submit button if there is any mandatory field validation error,the next time when i click the textbox jquery datepicker is not working means the script is not loading after submit click.it is throwing the error in firebug console like
TypeError: $(...).datebox is not a function
$('#txtstartdate').datebox('open');
Here is my code
$(document).ready(function () {
$('#txtstartdate').live('click', function () {
$('#txtstartdate').datebox('open');
$("#txtstartdate").datebox("option", {
mode: "calbox",
highDatesAlt: ["2011-11-09", "2011-11-10"],
highDates: ["2011-11-02", "2011-11-03"],
pickPageOAHighButtonTheme: "b"
});
});
});
and
#Html.TextBoxFor(m => m.StartDate, new { #name = "mydate", #id = "txtstartdate", style = "height:20px; font-size:10px;", data_role = "datebox", data_options = "{\"mode\":\"calbox\",\"useButton\": false}" })
Any suggestion?
as the firebug error suggest the browser could not find the function being used within the script can you make sure that the dependencies of the datebox is availiable after the submit call.
also try to send the dependencies with the view itself so that on every rendering of the view page at the client end it hold these js file with in it.