MVC 3 + knockoutjs: adding the data-bind attribute when using EditorFor for a boolean field - asp.net-mvc-3

Using #Html.EditorFor(model =>model.IsClient), where IsClient is a boolean, renders a drop down list with Not Set, Yes and No as the options.
All well and good.
Now I want to use knockoutjs with the resulting dropdownlist, that I like, so how do I add the data-bind attribute using #Html.EditorFor, that I need for knockoutjs to work with this drop down?
I have tried:
#Html.EditorFor(model => model.IsClient, new Dictionary<string, object> { { "data-bind", "value: Account.IsClient" } })
However, this uses the object additionalViewData parameter, and it doesn't render the data-bind attribute. Which is probably quite natural, as this parameter is probably nothing to do with Html Attributes for the rendered tag.
However, can't find any reasonable documentation, and none of the other overloads look likely candidates for what I want.
TIA any suggestions.

Brad Wilson blogged about display and editor templates in ASP.NET MVC 2. So you could modify the default template for boolean and add the attributes you need (~/Views/Shared/EditorTemplates/MyTemplate.cshtml):
#{
bool? value = null;
if (ViewData.Model != null)
{
value = Convert.ToBoolean(ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
}
var triStateValues = new List<SelectListItem>
{
new SelectListItem
{
Text = "Not Set",
Value = String.Empty,
Selected = !value.HasValue
},
new SelectListItem
{
Text = "True",
Value = "true",
Selected = value.HasValue && value.Value
},
new SelectListItem
{
Text = "False",
Value = "false",
Selected = value.HasValue && !value.Value
},
};
}
#if (ViewData.ModelMetadata.IsNullableValueType)
{
<!-- TODO: here you can use any attributes you like -->
#Html.DropDownList(
"",
triStateValues,
new {
#class = "list-box tri-state",
data_bind="value: " + ViewData.TemplateInfo.GetFullHtmlFieldName("") // you could also use ViewData.ModelMetadata.PropertyName if you want to get only the property name and not the entire navigation hierarchy name
}
)
}
else
{
#Html.CheckBox("", value ?? false, new { #class = "check-box" })
}
and finally:
#Html.EditorFor(model => model.IsClient, "MyTemplate")
or decorate the IsClient property on your view model with the UIHint attribute:
[UIHint("MyTemplate")]
public bool? IsClient { get; set; }
and then:
#Html.EditorFor(x => x.IsClient)
will automatically pick the custom editor template.

Addendum for knockoutjs users:
#Darin Dimitrov's answer is great, but slightly too rigid to use with knockoutjs, where complex views may lead to viewModels that don't entirely map to the #Model parameter.
So I have made use of the object additionalViewData parameter. To access the additionalViewData parameter from your Custom EditorTemplate, see the following SO question:
Access additionalViewData from Custom EditorTemplate code
Digression:
The additionalViewData param is confusing in that it does nothing with the default editor. It only comes into its own with a custom editor template.
Anyway, my amendments to Darin's code are as follows:
#if (ViewData.ModelMetadata.IsNullableValueType)
{
var x = ViewData["koObservablePrefix"];
if ((x != "") && (x != null)) { x = x + "."; }
#Html.DropDownList(
"",
triStateValues,
new {
#class = "list-box tri-state",
data_bind="value: " + x + ViewData.TemplateInfo.GetFullHtmlFieldName("") // or you could also use ViewData.ModelMetadata.PropertyName if you want to get only the property name and not the entire navigation hierarchy name
}
)
}
else
{
#Html.CheckBox("", value ?? false, new { #class = "check-box" })
}
Note the lines:
var x = ViewData["koObservablePrefix"];
if ((x != "") && (x != null)) { x = x + "."; }
koObservablePrefix is there so that I can add an arbitrary prefix to my viewModel ko.observable. You could do other things if you so choose.
I use the variable x as follows:
data_bind="value: " + x + ViewData.TemplateInfo.GetFullHtmlFieldName("")
That way, if I don't pass in the additionalViewData "koObservablePrefix" it all still works.
So, now I can write:
#Html.EditorFor(model => model.IsClient, "koBoolEditorFor", new { koObservablePrefix = "Account" })
that will render as:
<select class="list-box tri-state" data-bind="value: Account.IsBank" id="IsBank" name="IsBank">
Note the "value: Account.IsBank" data-bind attribute value.
This is useful if, for example, your views strongly typed model is of type Account, but in your accountViewModel for your page, you have a more complex structure, so you need to package your observables in an account object. EG:
function account(accountId, personId, accountName, isClient, isProvider, isBank) {
this.AccountId = ko.observable(accountId);
this.PersonId = ko.observable(personId);
this.AccountName = ko.observable(accountName);
this.IsClient = ko.observable(isClient);
this.IsProvider = ko.observable(isProvider);
this.IsBank = ko.observable(isBank);
}
function accountViewModel() {
var self = this;
this.selectedCostCentre = ko.observable('');
this.Account = new account(#Model.AccountId, #Model.PersonId, '#Model.AccountName', '#Model.IsClient','#Model.IsProvider', '#Model.IsBank');
// etc. etc
}
If you don't have this kind of structure, then, the code will pick up the structure. It is just a matter of tailoring your viewModel js to this, uhmmm, flexible convention.
Hope this isn't too confusing...

Related

hide forms tab in a form head crm dynamics 365

I have an entity which contains 2 forms, I want to prevent navagation between these 2 forms based on the value of two option field. In other words if the value of need prescoring is yes navigation is not possible and the inverse, how can I do this ?
Is it possible to simply hide the list ?
Thanks,
No, you cannot dynamically change the forms the user can select. This can only be done statically based on security roles.
Instead I suggest using a single form, where you hide and show the relevant fields/sections/tabs based on the value of your Need Processing field.
You can decide based on your project complexity wrt number of form controls/tabs/sections. We did something like this to maintain & forced navigation based on form control value.
var taskFormOptionSet = {
Form1: 1,
Form2: 2,
};
var FormNames = {
Form1: "Form1",
Form2: "Form2",
};
var myform = Xrm.Page.getAttribute("need_Prescoring").getValue();
var currentform = Xrm.Page.ui.formSelector.getCurrentItem();
if (currentform != null) {
var formId = currentform.getId();
var formLabel = currentform.getLabel();
}
if (myform == taskFormOptionSet.Form1 && formLabel != FormNames.Form1) {
var items = Xrm.Page.ui.formSelector.items.get();
for (var i in items) {
var form = items[i];
var formId = form.getId();
var formLabel = form.getLabel();
if (formLabel == FormNames.Form1) {
form.navigate();
return;
}
}
}
As it's not supported I used another solution which is to check if the boolean is true and the name of the, if the user tries to change the form he will be redirected to the right form until he changes the value of the boolean.
DiligenceSwitch: function(){
if (Xrm.Page.ui.formSelector.getCurrentItem() != null) {
var currentform = Xrm.Page.ui.formSelector.getCurrentItem();
}
if (currentform != null) {
var formId = currentform.getId();
var formLabel = currentform.getLabel();
}
var kycId = Xrm.Page.data.entity.getId();
SDK.REST.retrieveRecord(kycId, "kyc_Kycdiligence", "kyc_Needprescoring", null, //field for searching the targeted field, entity, targeted field, ...
function (kyc) {
if (kyc != null || kyc.kyc_Needprescoring != null) {
if (formLabel != "Pre-Scoring" && kyc.kyc_Needprescoring == true) {
var windowOptions = { openInNewWindow: false };
var parameters = {};
parameters["formid"] = "4B0C88A9-720C-4BFA-8F59-7C1D5DD84F02";
Xrm.Utility.openEntityForm("kyc_kycdiligence", kycId, parameters, windowOptions);
alert("Vous devez faire le pre-scoring");
}
}
},
function (error) {
Xrm.Utility.alertDialog(error.message);
});
},

Why does my selected value not get added to my SelectListItem as the “Text” and “Value” properties do?

I am trying to set a default value of a list in a partial view. The partial view is called using this…
#Html.Action("Acton", "Controller", new { department = item.Data.departmentNumber, defaultValue="someValue" })
Then in the controller I have…
[ChildActionOnly]
public ActionResult Categories(int? id, int? department, string defaultValue)
{
var typeList = from e in db.Rubrics where e.DepartmentID == department select e;
var selectedRubrics = typeList.Select(r => r.Category);
List<String> rubricsList = selectedRubrics.ToList();
var categories = new List<SelectListItem>();
for (int i = 0; i < rubricsList.Count(); i++)
{
categories.Add(new SelectListItem
{
Text = rubricsList[i],
Value = rubricsList[i],
Selected = (defaultValue == "defaultValueGetsSentToView")
});
}
var ViewModel = new RubricsViewModel
{
Category = "Select a Category",
Categories = categories
};
return View(ViewModel);
}
Why does my selected value not get added to my SelectListItem as the “Text” and “Value” properties are? Thanks for any help!
Assuming the values in the code are the literal values you are using, "defaultValue" is "someValue" and you are setting selected with the comparison defalutValue == "defaultValueGetsSentToView".
"someValue" == "defaultValueGetsSentToView" evaluates to false.

Get list on basis of dropdownlist data in asp.net mvc3

I have two dropdownlists in my module.
In one dropdownlist, I have hardcoded all the operators like <,>,<=,>=,==
In second dropdownlist, I have hardcoded salary of employees like 1000,2000,3000,4000....50000
Now if I select < from one list and 2000 from second list and click on submit button I should get list of employees who have salary less than 2000.
I want to do this in asp.net mvc3
How can I accomplish this task? Do I need to write a stored procedure for this?
I have created dropdownlist like:
viewModel.OperatorsList = new[]
{
new SelectListItem { Value = "<", Text = "<" },
new SelectListItem { Value = ">", Text = ">" },
new SelectListItem { Value = "<=", Text = "<=" },
new SelectListItem { Value = ">=", Text = ">=" },
new SelectListItem { Value = "==", Text = "==" }
};
viewModel.SalaryList = new[]
{
new SelectListItem { Value = "1000", Text = "1000" },
new SelectListItem { Value = "2000", Text = "2000" },
new SelectListItem { Value = "3000", Text = "3000" },
// and so on
};
and I have used this to show dropdownlist in view:
<%: Html.DropDownListFor(x => x.Operators, Model.OperatorsList)%>
well, you could do something like that
assuming viewModel is... your viewModel, and you've got an entity Employee with a property Salary (int in this sample, it's probably a decimal in real world)
create a static helper class
public static class MyHelper
{
// a dictionary for your operators and corresponding ExpressionType
public static Dictionary<string, ExpressionType> ExpressionTypeDictionary = new Dictionary<string, ExpressionType>
{
{"<", ExpressionType.LessThan},
{">", ExpressionType.GreaterThan},
{">=", ExpressionType.GreaterThanOrEqual}
//etc
};
//a method to filter your queryable
public static IQueryable<Employee> FilterSalary(this IQueryable<Employee> queryable, int salary, string operatorType)
{
//left part of the expression : m
var parameter = Expression.Parameter(typeof(Employee), "m");
//body is the right part of the expression : m
Expression body = parameter;
//m.Salary
body = Expression.Property(body, "Salary");
//m.Salary <= 1000 (for example)
body = Expression.MakeBinary(ExpressionTypeDictionary[operatorType], body, Expression.Constant(salary));
//m => m.Salary <=1000
var lambda = Expression.Lambda<Func<Employee, bool>>(body, new[] { parameter });
//so it will be queryable.Where(m => m.Salary <= 1000)
return queryable.Where(lambda);
}
}
usage
var queryable = context.All<Employee>();//or something like that, returning an IQueryable<Employee>
queryable = queryable.FilterSalary(viewModel.Salary, viewModel.Operators);

Why is the Dropdownlist in my MVC3 application disabled when I have set the SelectedItem?

I have an MVC3 application that I have a view defined to have the possibility of passing values and setting the SelectedItem value.
List<SelectListItem> items = new SelectList(db.BILLING_COUNTRY, "ISO_Code_BillingCountry", "CountryName", Country).AsParallel().ToList();
items.Insert(0, (new SelectListItem { Text = "Select Your Country", Value = "0" }));
ViewBag.Countries = items;
I am setting the disabled = "disabled" attribute on the dropdownlist if a ViewBag.EnableDropDowns is false or not set.
#{ object displayMode = (ViewBag.EnableDropDowns) ? null : new { disabled = "disabled" };
#Html.DropDownList("Countries", null, new { disabled = displayMode, onchange = "LoadItems()" } )
}
I set the ViewBag.EnableDropDowns to true, it correctly sets all the values in the drop down list but they are disabled instead of enabled.
What is wrong?
Be careful with the dictionary declaration. It has to beDictionary<string, object>() otherwise you will face run time issues.
I have to disable the listbox based upon a condition.
#{
var sourceListOptions = new Dictionary<string, object>();
sourceListOptions.Add("style", "Height: 250px; width: 225px;");
if (Model.SourceColumns.Count() == Model.ImportMappings.Count())
{
sourceListOptions.Add("disabled", "disabled");
}
}
#Html.ListBox("SourceColumns", Model.SourceColumns, sourceListOptions)
or
#Html.DropDownList("SourceColumns", Model.SourceColumns, sourceListOptions)
I think you need to set enabled="enabled"
Try:
#{
bool displayMode = (ViewBag.EnableDropDowns) ? "enabled": "disabled";
};
#if(displayMode)
{
Html.DropDownList("Countries", null,
new { enabled= displayMode, onchange="LoadItems()" } );
}
else
{
Html.DropDownList("Countries", null,
new { disabled= displayMode, onchange="LoadItems()" } );
}
The select element will be disabled if the disabled attribute is present at all (regardless of its value). So you'll need something like this (specifying htmlAttributes as a Dictionary rather than an anonymous object, as it seems more convenient in this case):
#{
var displayMode = new Dictionary<string,object>();
displayMode.Add("onchange", "LoadItems()");
if (ViewBag.EnableDropDowns) displayMode.Add("disabled", "disabled");
}
#Html.DropDownList("Countries", null, displayMode)

How do I Get Full Html Field Name for an item in a list on my model at controller level?

*First Post
I have a JQuery error handler for my Ajax posts that I must use, it appends an error to the html based on the field name for that element like this
$(document).ready(function () {
function myHandler(e, error) {
var tag = "";
if (error.Success == true) { $('.field-validation-error').remove(); return; } // if success remove old validation and don't continue
if (error.Success == false) { $('.field-validation-error').remove(); } // if success remove old validation and continue
for (i = 0; i < error.Errors.length; i++) {
var t = error.Errors[i];
//get error key and assign it to id
tag = t.Key;
//clear down any existing json-validation
for (j = 0; j < t.Value.length; j++) {
//this part assumes that our error key is the same as our inputs name
$('<span class="field-validation-error">' + t.Value[j].ErrorMessage + '</span>').insertAfter('input[name="' + tag + '"], textarea[name="' + tag + '"], select[name="' + tag + '"], span[name="' + tag + '"]');
}
}
}
$.subscribe("/******/errors", myHandler);
});
This works perfectly out of the box with our fluent validation setup until I try to add a custom modelstate error at controller level like so:
foreach (var item in model.Locations)
{
var cityRepos = new CityRepository(NhSession);
var cityItem = cityRepos.GetAll().FirstOrDefault(o => o.Country.Id == item.CountryID && o.Name == item.City);
if (cityItem == null)
item.City
ModelState.AddModelError("City", string.Format(#"The city ""{0}"" was not found, please ensure you have spelt it correctly. TODO: add a mail to link here with city not found subject", item.City));
}
the problem is that the modelstate error needs to be attached to the html field name not my magic string "City". The html name property is MVC Generated and looks something like this:
name="Locations[0].City"
I have encountered this problem in a html helper before and used the method:
.GetFullHtmlFieldName(
ExpressionHelper.GetExpressionText(propertySelector)
);
which resolved my problem in that case.
My question is can I use this method on my model property in an MVC post action to obtain the html name property it has come from?
Thanks in advance
ok so it's not ideal but I have implemented this Helper method until I can find a better solution that doesn't involve magic strings:
public static class ModelStateErrorHelper
{
public static string CreateNameValidationAttribute(string collectionName, int index, string propertyName)
{
string template = "{0}[{1}].{2}";
return string.Format(template, collectionName, index.ToString(), propertyName);
}
}

Resources