How do I Get Full Html Field Name for an item in a list on my model at controller level? - asp.net-mvc-3

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

Related

ASP.NET Core 3.1 / EF Core - Search table column by string name

Currently using:
ASP.NET Core 3.1 / EF Core
C#
Code-first approach
Postgres database
I'm building a method to support column searching on a table. I need to feed the column name to be searched by string value and build a query / lambda that can search the right column. I suspect I need to build some sort of expression and search on the expression but am having trouble with the syntax.
Here's the base code:
string search = "Search Value";
string givenColumn = "search_column";
IQueryable<MyModel> data = _dbContext.table;
data = data.Where(data => data.givenColumn.Contains(search));
I'd like to feed the column name in givenColumn and be able to build a query that searches the right column. At first I thought I wanted reflection but I'm looking to build a SQL query based off of a string, so I think I want to build an expression?
TIA!
Here is some sample code for a runtime WhereContains that operates on string columns:
public static class IQueryableExt {
// String.Contains(string)
static MethodInfo containsMI = typeof(string).GetMethod("Contains", 0, new[] { typeof(string) });
// generate r => r.{columnname}.Contains(value)
static Expression<Func<T, bool>> WhereContainsExpr<T>(string columnname, string value) {
// (T r)
var rParm = Expression.Parameter(typeof(T), "r");
// r.{columnname}
var rColExpr = Expression.Property(rParm, columnname);
// r.{columnname}.Contains(value)
var bodyExpr = Expression.Call(rColExpr, containsMI, Expression.Constant(value));
return Expression.Lambda<Func<T,bool>>(bodyExpr, rParm);
}
public static IQueryable<T> WhereContains<T>(this IQueryable<T> src, string columname, string value) => src.Where(WhereContainsExpr<T>(columname, value));
}
Just pass HTML Table id as a parameter onkeyup method of input field. HTML Code:
<input type="text" id="myInput" class="form-control search-input" onkeyup="searchData('myTable')" placeholder="Search...">
Javascript Code for exact match of any column:
function searchData(tableId) {
// Declare variables
var input, filter, table, tr, i, j, column_length, count_td;
column_length = document.getElementById(tableId).rows[0].cells.length;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById(tableId);
tr = table.getElementsByTagName("tr");
if (filter != "") {
for (i = 1; i < tr.length; i++) { // except first(heading) row
count_td = 0;
for (j = 1; j < column_length - 1; j++) { // except first column
td = tr[i].getElementsByTagName("td")[j];
/* ADD columns here that you want you to filter to be used on */
if (td) {
if (td.innerHTML.toUpperCase() === filter) {
count_td++;
}
}
}
if (count_td > 0) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
else {
for (i = 1; i < tr.length; i++) {
tr[i].style.display = "";
}
}
}

How to Access the ID from Kendo Model in KendoGrid / Custom validator editing?

I'm using a Kendo Grid / Custom validator editing to validate the a Column in the grid,Actually I'm trying the check the email already exists in the database or not ? to implement it I would like to get ID for the Row.
For example given in reference its products table, so in this case I would to get the ProductID inside the validation function ?
Reference:
http://demos.telerik.com/kendo-ui/grid/editing-custom-validation
You can get the id by retrieving the uid and then getting the data item from the dataSource via dataSource.getByUid(). Each row in the grid has a unique uid generated by the grid.
So for instance, referring to kendo's demo, the validation would now look like this:
productnamevalidation: function (input) {
//get row and uid
var row = input.closest('tr')[0];
var uid = $(row).attr('data-uid');
//get data item and then its ProductID
var dataitem = dataSource.getByUid(uid);
console.log(dataitem);
console.log(dataitem.ProductID);
//continue doing validation
if (input.is("[name='ProductName']") && input.val() != "") {
input.attr("data-productnamevalidation-msg", "Product Name should start with capital letter");
return /^[A-Z]/.test(input.val());
}
return true;
}
Here is their demo with this code included, you can open the console to see that each data row is being printed out with all its model properties.
You can get the record's ID with this:
input[0].kendoBindingTarget.source.ID
For example:
emailUnique: function (input) {
if (input.is("[name=Email]") && input.val() !== "") {
input.attr("data-emailUnique-msg", "Email already exists");
return isEmailUnique(input.val(), input[0].kendoBindingTarget.source.ID);
}
return true;
}
Bonus track, in case it's useful for someone:
function isEmailUnique(val, id) {
var data = YourGridDataSource; // If you don't have it, you may need something like $("#YourGrid").data().kendoGrid.dataSource
for (var i = 0; i < data.length; i++) {
if (data[i].ID != id && data[i].Email == val)
return false;
}
return true;
}

How to reference styles properly in InDesign Scripting?

I would like to know what is the proper way to reference a character style, a paragraph style or any other style in InDesign Scripting.
Please keep in mind that they could be under style group.
This should do it. Have a look at the function get_style.
update: use id instead of names
// written for
// http://stackoverflow.com/questions/19302941/how-to-reference-styles-properly-in-indesign-scripting
// author: #fabiantheblind
// license: wtfpl http://www.wtfpl.net
main();
function main(){
var doc = app.documents.add();// add a document
// create some styles
doc.paragraphStyles.add({name:"one"});
doc.paragraphStyles.add({name:"two"});
doc.paragraphStyles.add({name:"three"});
// add a group
var grp1 = doc.paragraphStyleGroups.add({name:"grp1"});
// add a style in the group
grp1.paragraphStyles.add({name:"four"});
// add a group in the group
var grp2 = grp1.paragraphStyleGroups.add({name:"grp2"});
// add a style in the group in the group
var parstylefive = grp2.paragraphStyles.add({name:"five"});
var fiveid = parstylefive.id;
var pg = doc.pages[0];// first page in new doc
var tf= pg.textFrames.add({geometricBounds:[0,0,100,100]});// add a textframe
tf.contents = TextFrameContents.PLACEHOLDER_TEXT;// add some cintent
var astyle = get_style(doc, fiveid);//get a style by name
if(astyle === null){
// error
alert("There is no style with that name");
}else{
// woohoo! \o/
tf.paragraphs.everyItem().appliedParagraphStyle = astyle;
}
}
/**
* This loops through all paragraph styles and returns one by his name
* could be rewritten for allCharacterStyles, allObjectStyles etc
*/
function get_style(d, id){
var thestyle = null;// result
for(var i = 0; i <d.allParagraphStyles.length;i++ ){
var onestyle = d.allParagraphStyles[i]; // isolate
if(id === onestyle.id){
thestyle = onestyle;// found it
} // end of check
} // end of loop i
// if we did not finds it we return null else Object ParagraphStyle
return thestyle;
}
I took an approach where you have to work with what I call the Fully Qualified Name (FQN) of a style, which is combination of all the groups and the style name separated by a pipe (|)
I've implemented the two following function in our code. They can be used with all type of styles with just a little bit of modification to the getStyleByFullyQualifiedName() function to support the other 4 types.
//getStyleFullyQualifiedName allow you to retrieve the style FQN,
//by providing a style object. Combine the name of the style and
//groups together separated by pipe (|).
function getStyleFullyQualifiedName(object){
var objectName = object.name;
if(object.parent.constructor.name != "Document"){
return getStyleFullyQualifiedName(object.parent) + "|" + objectName;
}
return objectName;
}
function getStyleByFullyQualifiedName(paragraphStyleFQN, document){
var tmp = paragraphStyleFQN.split("|");
var object = document;
for(var i=0; i < (tmp.length - 1); i++){
if(object.isValid){
object = object.paragraphStyleGroups.itemByName(tmp[i]);
}else{
return null;
}
}
if(!object.isValid){
return null;
}
object = object.paragraphStyles.itemByName(tmp[(tmp.length - 1)]);
if(!object.isValid){
return null;
}
return object;
}
//Assuming you have a style "Heading 1" under group "Title" and "Center.
//You can retrieve the style object like this.
var styleObject = getStyleByFullyQualifiedName("Title|Center|Heading 1", app.activeDocument);
//You can get the style FQN when you have a style object like this.
var styleFQN = getStyleFullyQualifiedName(styleObject);
alert("Valid: " + styleObject.isValid + " styleFQN: " + styleFQN);

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

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...

Obtaining a hidden column value from the selected row in a Telerik RadGridView

How do I obtain the selected row value for a hidden column in a Telerik RadGridView? The column is hidden on the aspx page and I would like to retrieve the value on the client side (JavaScript).
Essentially, I want to display a name in the grid view and be able to retrieve a value from the hidden field "ID" to bring up an edit form.
Here is an example of how I'm hiding the RadGridView column.
code sample:
onKeyPressEvent(sender, args) {
var variable = function (e) {
e = e || window.event;
if (e.keyCode == 13) {
var PartyID = args.getDataKeyValue("PARTY_ID");
var oManager = '<%=winMgr.ClientID %>';
var oManager = window.radopen("AttorneyEdit.aspx?PARTY_ID=" + PartyID, null);
oManager.setSize(1000, 530);
//Width, Height oManager.center();
} else { return true;
}
}
theForm.onkeypress = variable
}
Thanks for your help...
maybe you're going the wrong way. There is a property called "ClientDataKeyNames" on the mastertableview. You can add several key separated by a comma in this property. Then you'll be able to get your value without adding an extra column.
You should do something like this:
function onKeyPressEvent(sender, args) {
if (args.get_keyCode() == 13) {
var dataItem=sender.get_selectedItems()[0];
var PartyID = dataItem.getDataKeyValue("PARTY_ID");
var oManager = '<%=winMgr.ClientID %>';
var oManager = window.radopen("AttorneyEdit.aspx?PARTY_ID=" + PartyID, null);
oManager.setSize(1000, 530);
//Width, Height oManager.center();
}
else {
return true;
}
}
good luck

Resources