qualiture / ui5-validator

MIT License
42 stars 23 forks source link

The ValueState flickers on first validation run #18

Open maurr opened 5 years ago

maurr commented 5 years ago

Preconditions:

The demo uses the newest library version and automatic validation turned on in init(): sap.ui.getCore().getMessageManager().registerObject(this.getView(), true);

1) Type in "a" in field "Some Text:" 2) Press "Validate form"

The ValueState border of the control flickers and disappears. Press "Validate form" again and all is ok.

It seems the library is not compatible with automatic validation. https://openui5.hana.ondemand.com/#/topic/a90d93df5a024e8bb18826b699c9aaa7

I tried to set target and processor property of the Message instance to these values, this worked for me:

target : oControl.getId() + "/" + propertyName,
processor : new sap.ui.core.message.ControlMessageProcessor()

http://plnkr.co/edit/EAWrSoknfPU0H3ELtq6X?p=preview

HendrikHein commented 4 years ago

Hey Maurr,

when you add the target and the processor to the message unfortunately this is making some other problems. The field validation will not work correct after you validate the form.

1) add this code to the add message call in the validation class ( target : oControl.getId() + "/value", processor : new sap.ui.core.message.ControlMessageProcessor()

should look like this: sap.ui .getCore() .getMessageManager() .addMessages( new Message({ message: oControl.getValueStateText ? oControl.getValueStateText() : sMessage, // Get Message from ValueStateText if available type: eMessageType, target : oControl.getId() + "/value" , processor : new sap.ui.core.message.ControlMessageProcessor() }) );

http://plnkr.co/edit/EAWrSoknfPU0H3ELtq6X?p=preview

2). after adding the code delete the 0 in "Some Number" field for example 3). press "Validation form" 4). enter now 20 into the Some Number field (it will show not the right validation message in the ValueStateText "please enter a number" and not "please enter a number higher or equal 50"). If you enter a correct number into the field the ValueState will not disappear (this happens also if you are just press validate form and then put a correct number into the input field). 5). only if you press again "Validate form" the valueStates will disappear.

Does anyone know a solution?

maurr commented 4 years ago

Hi HendrikHein,

thank you for your response. In my example the automatic field validation is turned on for the view, it is done with this line of code sap.ui.getCore().getMessageManager().registerObject(page, true); You should add this in your initialization of the page.

The library version should be 1.71.0

after this changes the behaviour is better but still the ValueState is not always removed after clicking aside to trigger an automatic field validation.

My opinion is, that this version of qualiture-validator should not set value states explicitly, neither value state texts.

This version also sets some hard coded value state messages, which is not appropriate regarding i18n.

Please keep in mind there are UI5 library classes which do process a lot regarding value states:

sap.ui.core\src\sap\ui\core\message\MessageMixin.js sap.ui.core\src\sap\ui\core\message\ControlMessageProcessor.js sap.ui.core\src\sap\ui\model\DataState.js

The older version of qualiture-validator worked well, only the set of aggregations should be enhanced for grid and object page..., therefore i will use the older version with an enhanced set of allowed aggregations.

best regards

HendrikHein commented 4 years ago

Thank you for the fast response.

I am using the version 1.60.15. I already use sap.ui.getCore().getMessageManager().registerObject(page, true); for the automatic validation of types.

I found the problem in my case. If you are validating your input fields with the form validation function it will add one message for the input field to the processor.

But if you are also using the automatic validation and change now the value again in the input field. It is just appending another message to the same processor. The valueStateText is only showing the first message but the second is also visible in the processor.

image

the first message came from the validation methode and the second from the automatic field validation after changing the value again.

my solution is now to remove the old message in the validationError and validationSuccess Methode of the input field.

vpasedko commented 4 years ago

Hi,

I have a fix for the wrong validation message. SAPUI5 framework is not clearing the previous message and keeps it in ValueStateText. Solution is to clear ValueStateText in clearValueState method.

    Validator.prototype.clearValueState = function (oControl) {
        if (!oControl) return;

        if (oControl.setValueState) {
            oControl.setValueState(ValueState.None);
            oControl.setValueStateText(); // Clear ValueStateText to avoid last value caching
        }
        this._recursiveCall(oControl, this.clearValueState);
    }; 

This works fine for me.

Let me know, what do you think?

Regards, Vladimirs

HendrikHein commented 4 years ago

Hi Vladimirs,

i am not using the Validator with changing the ValueStates by it self, i am using the addMessage function if for example an error occours.

            this._oMessageManager.addMessages(
                            new Message({
                                message: oMessage,
                                type: MessageType.Error,
                                target: oControl.getId() + "/" + this._aValidateProperties[i],
                                processor: new sap.ui.core.message.ControlMessageProcessor()
                            }));

my solution was to build a resetValidationForControl function in the validator:

        /** 
         * reset a validation message from a given control target 
         * @public
         * @param {string} sTarget - Target for deleting the message from the control ("controlId/property")
         */
        resetValidationForControl: function (sTarget) {
            this._oMessageManager.getMessageModel().getData().forEach(function (oMessage) {
                if (oMessage.target === sTarget) {
                    this._oMessageManager.removeMessages(oMessage);
                }
            }.bind(this));
        },

this function i am calling in both the "validationSuccess" and "validationError" Event of every input field.

        /** 
         * reset the validation message from the control
         * @public       
         * @param {sap.ui.base.Event} oEvent - validateSuccess event
         * @param {string} sDirectTarget - Target string for indentify the correct message processor
         */
        onInputFieldValidationSuccess: function (oEvent) {
            var sTarget = oEvent.getParameter("id") + "/" + oEvent.getParameter("property");
            this.getValidator().resetValidationForControl(sTarget);
        },

        /** 
         * if an input field is having an error after entering the value, it will reset the messages which were created via validator
         * so it will only have one message left in the processor 
         * @param {sap.ui.base.Event} oEvent - validationError event 
         * @public 
         */
        onInputFieldValidationError: function (oEvent) {
            var sTarget = oEvent.getParameter("id") + "/" + oEvent.getParameter("property");
            this.getValidator().resetValidationForControl(sTarget);
        },

Now when the automatic validation of the type is validating the field, all messages from the messagehandler for the given target will be deleted.

Regards Hendrik

vpasedko commented 4 years ago

Hi Hendrik,

Yeah, sorry, this solution was for another case, when using "clearValueState" method before calling "validate", system always retained an old message and did not show the new validated message. This correction may be useful in some cases manually clearing value states.

I checked your case, and tried to resolve that by adding timeout for "_validate" method call, since framework needs a short delay to finish messages removal and value states reset after calling "removeAllMessages". But I had issues with validation result return, since timeout is asynchronous.

As a workaround solution added 2 new methods: to remove all manually added messages and update messages generated automatically by Framework control.

        /** 
         * Clear manually added validation messages
         * @memberof nl.qualiture.plunk.demo.utils.Validator
         */
        Validator.prototype._removeMessages = function () {
            this._oMessageModel.getData().forEach(function (oMessage) {
                if (!oMessage.validation) {
                    this._oMessageManager.removeMessages(oMessage);
                }
            }.bind(this));
        };
        /** 
         * Update automatically generated control validation message text by message target 
         * @memberof nl.qualiture.plunk.demo.utils.Validator
         * 
         * @param {string} sTarget - Message target ("controlId/property")
         * @param {string} sMessage - Message text
         * @return {boolean} bUpdated - Whether message updated or not
         */
        Validator.prototype._updateControlMessage = function (sTarget, sMessage) {
            var bUpdated = false;
            this._oMessageModel.getData().forEach(function (oMessage) {
                if (oMessage.target === sTarget && oMessage.validation) {
                    oMessage.setMessage(sMessage);
                    bUpdated = true;
                }
            });
            return bUpdated;
        };

"_removeMessages" we call in method "validate" instead of standard "MessageManager->removeAllMessages" method.

Plus, trying to update Framework message first if any, otherwise just add message manually.

Also added property "target" for new Message object to establish a reference between property and message in method "_addMessage":

            // Get message target
            var sTarget = "";
            if (oControl.getBindingPath("value")) {
                sTarget = oControl.getId() + "/value";
            }
            if (oControl.getBindingPath("selectedKey")) {
                sTarget = oControl.getId() + "/selectedKey";
            }

            var sNewMessage = oControl.getValueStateText ? oControl.getValueStateText() : sMessage;

            if (!this._updateControlMessage(sTarget, sNewMessage)) {
                this._oMessageManager.addMessages(
                    new Message({
                        message: sNewMessage, // Get Message from ValueStateText if available
                        type: eMessageType,
                        target: sTarget,
                        additionalText: sLabel // Get label from the form element
                    })
                );
            }

This works for me.

Please check this example: http://plnkr.co/edit/5Wxy5wzv11sGj3UQ?preview

Regards, Vladimirs