serenity-is / Serenity

Business Apps Made Simple with Asp.Net Core MVC / TypeScript
https://serenity.is
MIT License
2.6k stars 797 forks source link

Formatter doesn't seem to work with XYZ Form.cs #3081

Closed Leojet86 closed 6 years ago

Leojet86 commented 6 years ago

Hello, I have created a custom formatter to help me translate some status. I have a table name Cases with a column named Status. Status can be something like "Open_New", "Open_Scheduled". I'm using a formatter to translated them into something more user friendly, ie. : "Open", "Scheduled" etc.

Now the issue, the formatter doesn't seem to work when using it on attribute of CasesForm.cs, but it does for CasesColumn.cs. Meaning my Grid does have the HTML formatted by the formatter by the dialog doesn't. I could provide a screenshot if needed.

I managed to get it work by hardcoding the translator code directly into CasesDialog.ts but I don't like to duplicate my code.

Is this an issue or am I just a noob? Thanks


Here's a sample of my code for Status properties in CasesColumn.cs and CasesForm.cs

[StatusFormatter]
 public String Status { get; set; }

Here's a sample of the formatter code:

@Serenity.Decorators.registerFormatter()
    export class StatusFormatter implements Slick.Formatter
    {
        /**
         * Format the context
         * @param ctx
         */
        format(ctx: Slick.FormatterContext) {
            if ((ctx.value == null || String(ctx.value).length == 0)) {
                return ctx.value;
            }

            //
            // Get the ctx_value and translate it for the ui
            //
            let testValue = ctx.value;
            let translator: CaseTranslator = new CaseTranslator();

            //
            // Translate and set the CSS of cases.Status
            //

            //green
            if (testValue == 'Open_New'
               || testValue == 'Open_Assigned'
               || testValue == 'Open_Scheduled'
               || testValue == 'Open_Rescheduled'
               || testValue == 'Open_In_Progress')
            {
                return "<span class='label label-success'>" + translator.TranslateStatus(testValue) + '</span>';             
            }
. . .

And the translator

export class CaseTranslator {

        /**
         * Switch status code for a more user friendly message
         * @param status
         */
        TranslateStatus(status: string): string {
            switch (status) {
                case 'Open_New': {
                    status = Q.text('Site.StatusCode.New')
                    return status;
                }
                case. . .
brunobola commented 6 years ago

This https://github.com/volkanceylan/Serenity/issues/2777 helps ?

Leojet86 commented 6 years ago

@brunobola Sorry for the delay, I was working on something else.

Nope, this issue is using a formatter with XYZColumn.cs.

As stated, with XYZColumn it works. It's with XYZForm.cs that it does not.

I think it is because Form.cs is used with Dialog.ts. When the dialog is created, it has a < form></ form> with some < input> in it. I can't change the value of an input with a formatter.

The Column.cs use Grid.ts and when the UI is created it contains a serie of < div class="grid-container"> . . . <div class"slick-cell"> valueImTryingToFormat </ div> </ div>

volkanceylan commented 6 years ago

Formatters are only used for columns, not editors / forms.

stixoffire commented 4 years ago

@Leojet86 The form is an edit dialog .. so editors are used .. I have a phone editor that I use to format in the forms.

So maybe the below example will assist you in what you need.

namespace MyProjects.MySuperDB {

    @Serenity.Decorators.registerEditor()
    export class PhoneEditor extends Serenity.StringEditor {

        constructor(input: JQuery) {
            super(input);

            this.addValidationRule(this.uniqueName, e => {
                var value = Q.trimToNull(this.get_value());
                if (value == null) {
                    return null;
                }
                return PhoneEditor.validate(value, this.multiple);
            });

            input.bind('change', e => {
                if (!Serenity.WX.hasOriginalEvent(e)) {
                    return;
                }
                this.formatValue();
            });

            input.bind('blur', e => {
                if (this.element.hasClass('valid')) {
                    this.formatValue();
                }
            });
        }

        protected formatValue(): void {
            this.element.val(this.getFormattedValue());
        }

        protected getFormattedValue(): string {
            var value = this.element.val();
            if (this.multiple) {
                return PhoneEditor.formatMulti(value, PhoneEditor.formatPhone);
            }
            return PhoneEditor.formatPhone(value);
        }

        @Serenity.Decorators.option()
        public multiple: boolean;

        get_value() {
            return this.getFormattedValue();
        }

        set_value(value: string) {
            this.element.val(value);
        }

        static validate(phone: string, isMultiple: boolean) {
            var valid = (isMultiple ? PhoneEditor.isValidMulti(phone, PhoneEditor.isValidPhone) : PhoneEditor.isValidPhone(phone));
            if (valid) {
                return null;
            }
            return Q.text((isMultiple ? 'Validation.MySuperDBPhoneMultiple' : 'Validation.MySuperDBPhone'));
        }

        static isValidPhone(phone: string) {
            if (Q.isEmptyOrNull(phone)) {
                return false;
            }
            phone = Q.replaceAll(Q.replaceAll(phone, ' ', ''), '-', '');
            if (phone.length < 10) {
                return false;
            }

            if (Q.startsWith(phone, '0')) {
                phone = phone.substring(1);
            }

            if (Q.startsWith(phone, '(') && phone.charAt(4) === ')') {
                phone = phone.substr(1, 3) + phone.substring(5);
            }

            if (phone.length !== 10) {
                return false;
            }

            if (Q.startsWith(phone, '0')) {
                return false;
            }

            for (var i = 0; i < phone.length; i++) {
                var c = phone.charAt(i);
                if (c < '0' || c > '9') {
                    return false;
                }
            }

            return true;
        }

        static formatPhone(phone) {
            if (!PhoneEditor.isValidPhone(phone)) {
                return phone;
            }
            phone = Q.replaceAll(Q.replaceAll(Q.replaceAll(Q.replaceAll(phone, ' ', ''), '-', ''), '(', ''), ')', '');
            if (Q.startsWith(phone, '0')) {
                phone = phone.substring(1);
            }
            phone = '(' + phone.substr(0, 3) + ') ' + phone.substr(3, 3) + '-' + phone.substr(6, 2) + phone.substr(8, 2);
            return phone;
        }

        static formatMulti(phone: string, format: (s: string) => string) {
            var phones = Q.replaceAll(phone, String.fromCharCode(59), String.fromCharCode(44)).split(String.fromCharCode(44));
            var result = '';
            for (var x of phones) {
                var s = Q.trimToNull(x);
                if (s == null) {
                    continue;
                }
                if (result.length > 0) {
                    result += ', ';
                }
                result += format(s);
            }
            return result;
        }

        static isValidMulti(phone: string, check: (s: string) => boolean) {
            if (Q.isEmptyOrNull(phone)) {
                return false;
            }
            var phones = Q.replaceAll(phone, String.fromCharCode(59), String.fromCharCode(44)).split(String.fromCharCode(44));
            var anyValid = false;
            for (var $t1 = 0; $t1 < phones.length; $t1++) {
                var x = phones[$t1];
                var s = Q.trimToNull(x);
                if (s == null) {
                    continue;
                }
                if (!check(s)) {
                    return false;
                }
                anyValid = true;
            }
            if (!anyValid) {
                return false;
            }
            return true;
        }
    }
}