Search code examples
javascripttelerikclosuresinvocationradtextbox

Canceled event is not reverting RadTextBox to initial value


Recently, I have refactored my javascript to better utilize closures and invocation.

The Problem

Setting the cancel property (to true) of the telerik eventArgument (e) accompanying the RadButton Client OnValueChanging event is no longer setting the value back to the oldValue or initialValue.

Paysheet.UI

window.Paysheet.UI.WorkRate = { 
    "OnValueChanging"   : function (sender, e, args) {
        /// <summary>Entry-Point Function; initializes and calls 'Paysheet.XHR.POST', evaluates the response; Initializes and invokes the proper RadWindow/RadAlert/RadPrompt</summary>
        /// <param name="sender" type="Telerik.Web.UI.RadTextBox">The Telerik Control that raises the event</param>
        /// <param name="e" type="Telerik.Web.UI.InputValueChangingEventArgs">The Telerik Event</param>
        /// <param name="args" type="JSON">JSON that is to be processed by the handler, and posted</param>

        args = Paysheet.Common.GetDataKeyValues(sender);
        args.RequestorID = window.Paysheet.Requestor.ID;
        //args.AuthToken = window.Paysheet.Requestor.AuthToken;

        args.Scope = "PaysheetItem";
        args.PaysheetItemScope = "PayRate";
        args.RateType = "WorkRate";
        args.Rate = e.get_newValue();
        args._uri = "Paysheet/" + args.PayPeriodEmployeeID + "/PayRate/Modify";

        console.log("(1st) XHR POST => args: %o", args);
        return Paysheet.Common.Func(Paysheet.Common.OnValueChangingResultHandler, sender, e, args);
    }
};

The above code is called from the RadTextBox OnValueChanging EventHandler like so:

"valueChanging": function(sender, e) {
    args = {};
    console.log(" [EventOccurring] - sender: %o - e: %o - args: %o", sender, e, args); 
    e._cancel = !(Paysheet.UI.WorkRate.OnValueChanging(sender, e, args));
}

Paysheet.XHR

 window.Paysheet.XHR = {
    "POST": function (uri, jsonData) {
        /// <summary>Encapsulates the XmlHttpRequest object; posts the given data (JSON string) to a specified handler page and returns the JSON object</summary>
        /// <param name="url" type="String">URI</param>
        /// <param name="jsonData" type="String">JSON Data to be posted</param>

        uri = uri || jsonData._uri;
        var xhr = new XMLHttpRequest();
        xhr.open("POST", uri, false); // synchronous call
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(JSON.stringify(jsonData));
        return (xhr.status == 200) ? JSON.parse(xhr.response) : { "isValid": false, "StatusCode": 500 };
    }
};

Paysheet.Common

window.Paysheet.Common = {
    "Func": function (func, sender, e, args) {
        console.log("Call to func..");
        var result = func(sender, e, args, (Paysheet.XHR.POST(args._uri, args) || { "isValid": false, "isOverrideable" : false, "StatusCode": 500 }));
        console.log("result of call: %o", result);
        return result;
    },
    "GenericResultHandler": function (result, handlers) {
        result = result || { "isValid": false, "isOverrideable": false, "StatusCode": 500 };

        if (result.StatusCode != 200) {
            console.log("[ERROR] - [RadAlert] Invoked...");
            //the result.message show be more domain specific.
            radalert(result.message || "Internal Error Occurred!", 400, 350, result.title || "Alert - Error", null, null);

        } else if (result.isValid) {

            console.log("[APPROVED]");
            handlers.approve();

            return true;
        } else if (result.isOverrideable) {

            console.log("[OVERRIDE] - [RadPrompt] Invoked...");
            handlers.override();
        } else {

            console.log("[REJECTED] - [RadAlert] Invoked...");
            handlers.reject();

            //the result.message show be more domain specific.
            radalert(result.message || "The state of this data prevents any modifications; Overriding is not an option.",
                400, 350,
                result.title || "Alert - Denied",
                null, null);
        }

        // ultimately, the caller will be a cancelable telerik event.
        //    by convension, the result is inverted/flipped: thus, this resolves to 'e.set_cancel = !([return])'
        //    note: override will have to set the value explicitly without triggering this event in the (presumed) asynchronous method call to 'handlers.override';
        return false;
    },
    "GenericOverrideResultHandler": function (result, handlers) {
        result = result || { "isValid": false, "StatusCode": 500, "isOverrideable" : false };            

        if (result.StatusCode != 200) {
            console.log("[OVERRIDE ERROR] - [RadAlert] Invoked...");
            radalert(result.message || "Internal Error Occurred!", 400, 350, result.title || "Alert - Error", null, null);
        } else if (result.isValid) {
            console.log("[OVERRIDE APPROVED]");
            handlers.approve();
        } else {
            console.log("[OVERRIDE REJECTED] - [RadAlert] Invoked...");

            //the result.message show be more domain specific.
            radalert(result.message || "Override was denied, no modification made to stored data!", 400, 350, result.title || "Alert - Override Denied", null, null);
            handlers.reject();
        }
    },
    "OnValueChangingResultHandler": function (sender, e, args, result) {
        return Paysheet.Common.GenericResultHandler(result, { "approve": approve, "override": override, "reject": reject });

        function approve() {
            var d = Paysheet.Common.GetDataKeyValues(sender);
            var DaysCell = sender.get_parent().get_cell("Days");
            if (DaysCell["set_value"]) {
                DaysCell.set_value(result.content.Days);
            } else {
                DaysCell.innerHTML = result.content.Days;
            }

            var GrossCell = sender.get_parent().get_cell("Gross");
            if (GrossCell["set_value"]) {
                GrossCell.set_value(result.content.Gross);
            } else {
                GrossCell.innerHTML = result.content.Gross;
            }
        }

        function override() {
            radprompt(result.message, callback, 400, 350, null, result.title || "Prompt - Override", false);

            function callback(promptArgs) {
                promptArgs = promptArgs || { "override": false, "note": "" };

                if (promptArgs.override && promptArgs.note.length > 0) {
                    args.Override = promptArgs.override;
                    args.OverrideNote = promptArgs.note;
                    console.log("(2nd) XHR POST [start] => args: %o", args);
                    var result = Paysheet.XHR.POST(args._uri, args);
                    console.log("(2nd) XHR POST [finished] - results: %o", result);
                    if ((result) ? result.isValid : false) {
                        console.log("[SUCCESS]");
                        sender._setNewValue(e.get_newValue());
                        var d = Paysheet.Common.GetDataKeyValues(sender);
                        //Update Gross;
                        var DaysCell = sender.get_parent().get_cell("Days");
                        if (DaysCell["set_value"]) {
                            DaysCell.set_value(result.content.Days);
                        } else {
                            DaysCell.innerHTML = result.content.Days;
                        }

                        var GrossCell = sender.get_parent().get_cell("Gross");
                        if (GrossCell["set_value"]) {
                            GrossCell.set_value(result.content.Gross);
                        } else {
                            GrossCell.innerHTML = result.content.Gross;
                        }
                    } else {
                        console.log("[FAILED]");
                        sender._SetValue(sender._initialValueAsText);
                        sender.updateDisplayValue();
                        if (result.StatusCode == 200) {
                            console.log("... Denied [RadAlert] Invoked...");
                            radalert(result.message || "Override request denied!", 400, 350, result.title || "Alert - Override Denied", null, null);
                        } else //if (result.StatusCode == 500)
                        {
                            console.log("... Error [RadAlert] Invoked...");
                            radalert(result.message || "Internal Error Occurred!", 400, 350, result.title || "Alert - Error", null, null);
                        }
                    }
                } else {
                    // ensuring the control retains the same value; for the prompt is async;
                    sender._SetValue(e.get_oldValue());
                    sender.updateDisplayValue();
                }
            }
        }

        function reject() {
            // neither of the following lines should be necessary, for the GenericResultHandler returns true|false to `e._cancel`
            // sender._SetValue(e.get_oldValue()); //This line works
            // e._cancel = true; //This line does not work
        }
    }
};

I've stepped through this code, and into the telerik's code. It does seem to call _SetValue using the _initialValueAsText. I just doesn't seem to actually apply.

If I explicitly set the cancel property of the event in my 'reject' function. It exhibits the same issue.

If I explicitly set the value of the sender to the old value, it works.


EDIT: I have commented out xhr.send line (synchronous call) - forcing the POST function to return { "isValid" : false, "isOverrideable" : false, "StatusCode": 500 } - the issue persists.


Solution

  • Calling sender._setHiddenValue(e.get_newValue()) at some point prior to assigning true or false to the event._canceled corrects the issue..


    _setHiddenValue(value) conditionally sets the _text and _value fields of the sender to the new value: it checks the passed value against the existing _value, if they are the same, then the function exits without assigning the value. It exits with a true or false value indicating whether the assignment occurred.

    If/when the _canceled field of the eventArg is set to true, the internal event handler calls sender._SetValue(sender._initialValueAsText); Within that procedure, sender._setHiddenValue(sender._initialValueAsText) is called.