Use AdditionalFields to compare to field in a different class - razor

Introduction
In MVC Core I have a base ViewModel and two ViewModels included in the base model as properties, like so:
public class BaseViewModel
{
public FirstViewModel First { get; set; }
public SecondViewModel Second { get; set; }
}
In FirstViewModel I added a custom validation attribute on one of the properties, inheriting from RemoteAttribute. My goal is to use this attribute comparing the value to a property in SecondViewModel. I've set this up using the AdditionalFields property of the RemoteAttribute.
I think my problem lies in the way the HTML attributes are added to the control in the razor view:
data-val-remote-additionalfields="*.PropOfModelFirst,*.PropOfModelSecond"
When the clientside validation is calling the controller action, the *. is replaced by the framework by First., which is wrong, because the second value is not part of the First-class.
I tried prepending the classname to the second property, resulting in
data-val-remote-additionalfields="*.PropOfModelFirst,*.Second.PropOfModelSecond"
but as can be expected this is changed to First.Second.PropOfModelSecond.
Question
Can the AdditionalFields property be used to compare against values from another ViewModel?

You cannot use AdditionalFields to compare against values from another ViewModel. The reason is that the rules are added to jquery.validate.js by the jquery.validate.unobtrusive.js plugin (which reads the data-val-* attributes generated by the HtmlHelper methods). Specifically it is the adapters.add("remote", ["url", "type", "additionalfields"], function (options) { method that is pre-pending First to the property names.
One option would be to use a single 'flat' view model containing all properties.
If that is not desirable, then you can just write your own ajax code to call your server method that performs the validation. This actually has some added performance benefits as well. By default, after initial validation triggered by the .blur() event, validation is performed on every .keyup() event, meaning that you are potentially making a lot of ajax and database calls if the user initially entered an invalid value.
Remove the [Remote] attribute, and add the following script (I'll assume the properties are First.ABC and Second.XYZ)
$('#First_ABC').change(function() {
var url = '#Url.Action(...)'; // add your action name
var input = $(this);
var message = $('[data-valmsg-for="First.ABC"]'); // or give the element and id attribute
$.post(url, { abc: input.val(), xyz: $('#Second_XYZ').val() }, function(response) {
var isValid = response === true || response === "true";
if (isValid) {
input.addClass('valid').removeClass('input-validation-error');
message.empty().addClass('field-validation-valid').removeClass('field-validation-error');
} else {
input.addClass('input-validation-error').removeClass('valid');
message.text(response).addClass('field-validation-error').removeClass('field-validation-valid');
}
})
});
where the controller method would be
[HttpPost]
public ActionResult Validate(string abc, string xyz)
{
bool isValid = .... // code to validate
if (isValid)
{
return Json(true, JsonRequestBehaviour.AllowGet);
}
else
{
return Json("your error message", JsonRequestBehaviour.AllowGet)
}
}

Related

Typescript class with default values, how to parse JSON to this

I have a class of type A.
This class has several properties, let's call them prop1, prop2 and prop3.
When I'm calling an API, that returns a JSON string representing the object, some properties might be omitted if they are null. Further down the road, however, this object is used to construct a form dynamically (using Formik, but that's unrelated).
This framework expects all properties to be there, and some will be visible dynamically depending on other properties.
So my question, how can I parse a JSON response to my custom class, keeping default values in case properties are omitted in the API response?
What I've tried was:
static getCustomer(id) {
return fetch(process.env.MD_API_URL + 'customers/' + id, { mode: 'cors' })
.then(response => {
let cust = new Customer();
return response.json().then(x => cust = JSON.parse(x));
}).catch(error => {
return error;
});
}
But this returns undefined. Must be doing something wrong...
since typescript is not actually compiled but translated into javascript so all the javascript rules apply.
Therefore deserializing json wont actually create a new instance of the class in question but gives you an object you can "call" Customer during design time.
you could however create an object and then assign the json values like this:
export class Customer {
public id: number;
public name: string;
// your stuff here
public myDefaultProp: string = "default value";
public constructor(init?: Partial<Customer>) {
Object.assign(this, init);
}
}
your return then would look like this:
return response.json().then(x => new Customer(JSON.parse(x)));
added an example https://stackblitz.com/edit/typescript-16wlmg
This essentially just a matter of determining what to do in order to create an instance of a class, and map the properties of a JSON response towards your custom class, and there could be many different ways to solve this,
But I think (Factory function) is appropriate approach for this kind of task.

Converting simple value to JSON in ASP.NET Core API

Sometimes my ASP.NET Core API needs to return a simple value i.e. bool, int or string even though in most cases, I return complex objects/arrays as JSON.
I think for consistency purposes, it's a good idea to return even simple values as JSON. What's the easiest way to convert a simple value, whether it's bool or int into JSON?
My standard controller action looks like this -- see below -- which gives me the ability to return status codes as well as data. Therefore, I'd like to stick to that approach, rather than return JsonResult.
public async Task<IActionResult> Get()
{
// Some logic
return Ok(data);
}
I'm just trying to figure out the easiest way to convert my data into JSON, if it's not already in JSON format.
Looking at your code, I assume your application is supposed to be a service that needs to return some kind of data serialised in JSON.
Well, good news is ASP.NET Core already includes a data serialiser that would do the job for you.
You may need to set it up according to your needs.
For example, let's assume the following data class:
public class Data {
public string Name { get; }
public string Value { get; }
public bool IsValid { get; }
public Data(string name, string value, bool isValid) {
Name = name;
Value = value;
IsValid = isValid;
}
}
Then the following method in your Controller:
public async Task<IActionResult> Get() {
var data = new Data("sample name", "this is a value", true);
return Ok(data);
}
would return:
{
"name": "sample name",
"value": "this is a value",
"isValid": true
}
Even thought the standard serialisation behaviour may fit fine for very simple implementations, you may need more control on how your different data types should be serialised (and deserialised) by your application, especially when those do not exactly match the way you want to present the data back to the client. In this case you may want to use Custom Converters.
You can configure that when setting up MVC in the ConfigureServices(IServiceCollection services) method:
// Add framework services.
services.AddMvc().AddJsonOptions(jo => {
// sample serialiser setup
jo.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
jo.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
jo.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
// custom Converters
jo.SerializerSettings.Converters.Add(new MyCustomConverter());
});
Here you can read and learn more on how to setup and use Custom Converters.

Add 'standard' JSON fields to serialized objects in REST API

Motivated by this: Google JSON Style Guide, I want to insert a bit of custom serialization logic to my rest API. I'm using the WebAPI 2 and JSON.NET. My goal is to wrap the 'payload' of my response in the 'data' field of the main JSON response, as described in the style guide, include an apiVersion field in every response, and that sort of thing. Of course the controller actions just return straight POCO's, and I want to modify the container that they're sent inside of, not the POCOs themselves, so:
{
"id": "111",
"apiVersion": "1.0",
"data": {
"kind": "monkey",
"name": "manny",
"age": "3"
},
"error": null
}
...that type of thing. So I envision inserting little bits of standard data into every response before it goes over the wire. What's the best way to accomplish this?
TIA.
I believe you can use an ActionFilterAttribute to achieve this kind of behaviour. You would first need to create a class to represent your wrapped response (all the properties are string, adjust as you need):
public class WrappedJsonResponse
{
public string Id {get;set;}
public string ApiVersion {get;set;}
public object Data {get;set;}
public string Error {get;set;}
}
The ActionFilterAttribute allow you to do some processing after the execution of an action via the virtual OnActionExecuted method:
public class WrappedJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext context)
{
// A POCO response will normally be wrapped in an ObjectContent
var content = context.Response.Content as ObjectContent
if(content != null)
{
// Create the WrappedJsonResponse object appropriately and
// put the original result in the Data property
content.Value = new WrappedJsonResponse { Data = content.Value };
content.ObjectType = typeof(WrappedJsonResponse);
}
}
}
With the attribute, you can then choose to apply it where you want (whole controller, action only or as a default filter).
Note: I do not have access to a development environment at the moment and have not tested the filter. If this is not complete, it should at least give you an idea on how it can be done.

ASP.NET MVC2 UpdateModel not updating a public property included in whitelist

I have a class Foo with a field UpdateMe of type Confirmation as described below..
public class Foo
{
public Confirmation UpdateMe{get;set;}
public int BarInt{get;set}
}
public enum Confirmation
{
N = 0,
Y = 1
}
I have a whitelist that has UpdateMe, and runs the following way...
[AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken]
public ActionResult Update(Foo foo)
{
if(ModelState.IsValid)
{
//this is the Foo as it exists in the backend..using Linq2Sql read/record behavior
Foo existingFoo = _Service.GetFoo();
string[] whitelist = { "UpdateMe" };
UpdateModel(existingFoo, whitelist);
//do persistence stuff down here...
}
}
the model is bound just fine, the incoming Foo has whatever UpdateMe value I set, however the UpdateModel procedure is not updating the property.
This has been ridiculously simplified, but rest assured the UpdateModel is working for other properties coming through the action.
Any idea why this particular public property is not updating?
Ok, heres the scoop.
The issue is that the field was mapped to a checkbox. When not writing the checkbox using an HtmlHelper it was not propagating into the ModelState, and therefore not being included in the UpdateModel.
When I switched to using an HtmlHelper, the ModelState was then including the checkbox value regardless of being selected(desired)...however this brought back the ugliness of mapping an enum type to a checkbox.

Implementing linqtosql partial DataContext class - how to inspect before/after values

I'm trying to extend the linqtosql classes generated by the VS designer and need to determine if the value of a specific field has changed. Is there a way for me to access the before and after values for a field in the DataContext Update method for a table/entity?
Here's my code:
public partial class DataClassesDataContext
{
partial void UpdateActivity(Activity instance)
{
this.ExecuteDynamicUpdate(instance);
//need to compare before and after values to determine if instance.AssignedTo value has changed and take action if it has
}
}
I'm also open to adding a property to the Activity entity class to signal whether the value has changed but I can't figure out how to tell if the value has changed there either. I can't just use the OnAssignedToChanged method of the Activity class because it fires whenever the property value is set, not necessarily changed. I'm using the ListView and LINQDataSource control for updating so it gets set no matter what.
I also thought I might be able to use the OnAssignedToChanging method but the Activity instance does not seem to have current values at that point. The following code does not work as this.AssignedTo is always null.
partial void OnAssignedToChanging(int? value)
{
if (value != this.AssignedTo)
{
_reassigned = true;
}
}
You should be able to do this:
public partial class DataClassesDataContext
{
partial void UpdateActivity(Activity instance)
{
Activity originalActivity = Activities.GetOriginalEntityState(instance);
if (instance.Property != originalActivity.Property)
{
// Do stuff
}
this.ExecuteDynamicUpdate(instance);
//need to compare before and after values to determine if instance.AssignedTo value has changed and take action if it has
}
}
Another alternative:
public partial class DataClassesDataContext
{
partial void UpdateActivity(Activity instance)
{
ModifiedMemberInfo[] changes = Activities.GetModifiedMembers(instance);
foreach (var change in changes)
{
Console.WriteLine("Member: {0}, Orig: {1}, New: {2}", change.Member, change.OriginalValue, change.CurrentValue);
}
this.ExecuteDynamicUpdate(instance);
//need to compare before and after values to determine if instance.AssignedTo value has changed and take action if it has
}
}
I just checked into your other option (OnAssignedToChanging(int? value)), and it seems to work fine for me. Are you sure the initial value wasn't actually null? I tested it with a new object as well as one pulled from a database and it appears to work correctly.