jwaliszko / ExpressiveAnnotations

Annotation-based conditional validation library.
MIT License
351 stars 123 forks source link

Access current control inside ea.addMethod #170

Closed evdinursan closed 6 years ago

evdinursan commented 6 years ago

Hello,

In order to write a custom RequiredIf client-side method, I need a way to access the current control (input) on which the validator has been triggered.

The reason behind my question is pretty simple - instead of saving a value into a hidden field, I would like to set it as data-x custom attribute on the control level.

Can you please tell me how can I do this?

Thank you

jwaliszko commented 6 years ago

Hi, there is no such an information injected to the methods.

The simplest solution would be to pass the field name to the method, e.g.

[AssertThat("Method('SomeField')")]
...

ea.addMethod('Method', function(name) {
    var field = $(':input[name="' + name+ '"]')
    ...
});

Is it acceptable workaround?

evdinursan commented 6 years ago

Hi,

This is was my original intention also - to pass the fieldname from the backend - the issue is a bit more complex then this and I will tell you why: I have a user-defined form which is generated at runtime based on some fields defined in the database.

So, my model for each column of this form looks like this:

public bool IsRequired { get; set; }

public string FormColumnRegexValidation { get; set; }

[RequiredIf("IsStringValueRequiredAndEmpty(StringValue, IsRequired, EncryptedFormColumnTypeId) == true")]
[AssertThat("IsStringValueRegexMatch(StringValue, FormColumnRegexValidation)")]
public string StringValue { get; set; }

[RequiredIf("IsIntValueRequiredAndEmpty(IntValue, IsRequired, EncryptedFormColumnTypeId) == true")]
public int? IntValue { get; set; }

In order to have the front-end validation working, I need to save the IsRequired and FormColumnRegexValidation (among others) as hidden fields so the javascript validation methods recognize them and be able to use them; my ideal situation from which I started this thread, was to save these 2 (and others) properties as data-x attributes on the input field and in the validation methods to use them to check for the validity.

Being a run-time generated form, I'm not able to send from the backend the field name in order to use it on the front-end and this is why I was looking for a way to "catch" a reference to the input field.

I hope I was much clear this time.

Many thanks for your wonderful work!!!

Evdin

jwaliszko commented 6 years ago

You could fork the repo and proceed with the following changes of the expressive.annotations.validate.js script (new lines are denoted by // (1-5) <<---- ! comment):

...

computeRequiredIf = function(method, value, element, params) { currentElement = element; // (3) <<---- !

* modify the add method - inject `currentElement` as a last argument:

toolchain = { methods: {}, addMethod: function(name, func) { // add multiple function signatures to methods object (methods overloading, based only on numbers of arguments) var old = this.methods[name]; this.methods[name] = function() { if (func.length === arguments.length) { [].push.call(arguments, currentElement); // (4) <<---- ! return func.apply(this, arguments); } if (typeof old === 'function') { return old.apply(this, arguments); } [].push.call(arguments, currentElement); // (5) <<---- ! return func.apply(this, arguments); // no exact signature match, most likely variable number of arguments is accepted }; },

When done you should be able to access the current element given as a last argument inside each method:

ea.addMethod('Method', function(arg1, arg2) { var element = arguments[arguments.length - 1]; ... });


I've not tested it, it's just an ad-hoc idea. Give it a try please.
evdinursan commented 6 years ago

MANY THANKS!!!!

I will try it and let you know!

Again, thank you!

Evdin

jwaliszko commented 6 years ago

Hi, Have you found some spare time to check whether it behaves as expected? If yes, do you think it is beneficial to have such a feature (or something similar) built-in into EA e.g. as a configurable option?

Thanks.

evdinursan commented 6 years ago

Hi,

Sorry - didn't had the time to do it yet but most probably I will take care of it this week because I really need it.

On the other hand - I don't see why it should be configurable - it should be there - if you need it, you can just use it :-)

Thank you again and sorry for the delay.

jwaliszko commented 6 years ago

Due to no response I've provided different implementation (see https://github.com/jwaliszko/ExpressiveAnnotations/commit/4f04d08f633e29d2eacc483c3b1d2fb6ce62601f), i.e. the current element can be now accessed through the __meta__ property of this scope, e.g

<script>
    ea.addMethod('Method', function() {
        var elem = this.__meta__.element;
        ...
    });

I think it is more straightforward than the solution presented earlier in this thread. __meta__ is a restricted identifier which is used to associate additional information with the model context. It is created to minimize eventual naming conflicts. Nevertheless it must be unique, so usage of the same name for e.g. fields names is forbidden, and enforced by relevant exception.

JoshAtPCI commented 6 years ago

Any news on when this will be released? I found a need for this feature and found that it is not in the currently released nuget package.

Thanks