Customer Engagement & Dynamics CRM Forum

Expand all | Collapse all

PCF Control Double Firing updateView

  • 1.  PCF Control Double Firing updateView

    TOP CONTRIBUTOR
    Posted 7 days ago
    Edited by Ryan Perry 7 days ago
    Hi PCF pros (@Rex Kenley Tan, @Daryl LaBar, @Aiden Kaskela, @Andrew Butenko)

    What would cause the PCF Framework to fire the updateView method twice on one update?

    To ilustrate, here is an example using about the most basic control one can make: a text box input. Code is below.

    inIt creates a simple input box with an onChange handler to notify the system when the value is changed. It doesn't set any values. I've confirmed the onChange handler isn't firing multiple times.
    updateView simply sets the value of the inputBox from the context parameter it receives.
    notifyOutputChanged just sends the value from the input field. 
     
    When first, the framework fires the init, then updateView functions,  This populates the initial value. It does not cause the onChange handler to fire.  All good. 
     
    When the user updates the value, the onChange handler fires, which calls notifyOutputChanged. In response, the framework calls getOutputs, which returns the value from the input field.  After this, the framework calls the updateView method TWICE.  The first time, it passes the old value, causing the updateView function to set the input value back to what it was. The 2nd Time, it passes the new value, causing it to set the correct value.

    For a simple text field, this brief oscillation of value back to the old value, before settling on the new isn't a big problem, but it is causing me grief on a more complicated control with multiple bindings.   I'm not sure if this is a peculiarity of the harness... My next step is to test in a real environment. If it works there, great, but having to publish to an environment to test sounds tedious.  Any ideas on the cause and fix would be appreciated.


    The Code:

    import { IInputsIOutputs } from "./generated/ManifestTypes";

    export class TextControl implements ComponentFramework.StandardControl<IInputsIOutputs> {

        private notifyOutputChanged: () => void;
        //private currentValue: string;
        private inputFieldHTMLInputElement;

        /**
         * Empty constructor.
         */
        constructor() {

        }

        /**
         * Used to initialize the control instance. Controls can kick off remote server calls and other initialization actions here.
         * Data-set values are not initialized here, use updateView.
         * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to property names defined in the manifest, as well as utility functions.
         * @param notifyOutputChanged A callback method to alert the framework that the control has new outputs ready to be retrieved asynchronously.
         * @param state A piece of data that persists in one session for a single user. Can be set at any point in a controls life cycle by calling 'setControlState' in the Mode interface.
         * @param container If a control is marked control-type='standard', it will receive an empty div element within which it can render its content.
         */
        public init(contextComponentFramework.Context<IInputs>, notifyOutputChanged: () => voidstateComponentFramework.DictionarycontainerHTMLDivElement) {
            //Make passed in functions available to other methods.
            this.notifyOutputChanged = notifyOutputChanged;

            //Build the control UI
            this.inputField = document.createElement("input");
            container.appendChild(this.inputField);

            //Add Change Event Listener.
            this.inputField.addEventListener("change", () => {
                console.log("fieldOnChange Fired.");
                debugger;
                this.notifyOutputChanged();
            });

        }

        /**
         * Called when any value in the property bag has changed. This includes field values, data-sets, global values such as container height and width, offline status, control metadata values such as label, visible, etc.
         * @param context The entire property bag available to control via Context Object; It contains values as set up by the customizer mapped to names defined in the manifest, as well as utility functions
         */
        public updateView(contextComponentFramework.Context<IInputs>): void {
            debugger;
            this.inputField.value = context.parameters.value.raw || "";
        }

        /** 
         * It is called by the framework prior to a control receiving new data. 
         * @returns an object based on nomenclature defined in manifest, expecting object[s] for property marked as "bound" or "output"
         */
        public getOutputs(): IOutputs {
            debugger;
            return {
                //value: this.currentValue
                value: this.inputField.value
            };
        }

        /** 
         * Called when the control is to be removed from the DOM tree. Controls should use this call for cleanup.
         * i.e. cancelling any pending remote calls, removing listeners, etc.
         */
        public destroy(): void {
            // Add code to cleanup control if necessary
        }
    }
    ​​​

    ------------------------------
    Ryan Perry
    PowerApps Consultant
    ------------------------------
    The first step toward cloud success. - Migrate from CRM to D365 with expert guidance from Microsoft. I'm Ready


  • 2.  RE: PCF Control Double Firing updateView

    TOP CONTRIBUTOR
    Posted 7 days ago

    Update: Tested in real environment. No problems. This appears to be an issue with the Test Harness. 



    ------------------------------
    Ryan Perry
    Business Systems Analyst
    Auric Solar
    ------------------------------

    The first step toward cloud success. - Migrate from CRM to D365 with expert guidance from Microsoft. I'm Ready


  • 3.  RE: PCF Control Double Firing updateView

    TOP CONTRIBUTOR
    Posted 7 days ago

    Ryan

    You are correct, it is a bug in the harness. Although being paranoid I wrote a "gatekeeper" routine to further restrict the event.

    https://github.com/rexkenley/pcf-react/blob/master/SamplePCF/index.ts



    ------------------------------
    Rex Kenley Tan, MCSA, MCSD
    Minneapolis MN
    https://www.youracclaim.com/users/rex-kenley-tan

    *Always be CURRENT with JavaScript & C#, NEVER be obsolete.

    DISCLAIMER: All views expressed on this site are my own and DO NOT represent the opinions of ANY entity whatsoever with which I have been, am now, or will be affiliated.
    ------------------------------

    The first step toward cloud success. - Migrate from CRM to D365 with expert guidance from Microsoft. I'm Ready


  • 4.  RE: PCF Control Double Firing updateView

    TOP CONTRIBUTOR
    Posted 6 days ago
    Edited by Ryan Perry 6 days ago
    Thanks for the sample, @Rex Kenley Tan.

    If I understand your code, each time your control updates values, it sets a flag, "updatedByReact" true. Really, the emphasis isn't react, which is irrelevant in this case. It signals that the values were just updated by your control. Each time refreshView is called it checks if this flag is true, and if the parameters are identical. If so, it is deemed to be an unnessecary :) update request with exact parameters just sent to the harness, and ignored. The updatedByReact flag is cleared, allowing subsequent updateView calls to be evaluated.

    Did I follow that correctly?

    The init method's check to see if it is updated by your control, and if it is equivalent to current values.
    if (this.updatedByReact) {
        if (this.equivalent(parameters)) this.updatedByReact = false;
        return;
    }

    The Equivalency Checker:
    equivalent(parameters) {
        const { value } = parameters;
        return value && value.raw === this.currentValue;
    }


    ------------------------------
    Ryan Perry
    Business Systems Analyst
    Auric Solar
    ------------------------------

    The first step toward cloud success. - Migrate from CRM to D365 with expert guidance from Microsoft. I'm Ready


If you've found this thread useful, dive deeper into User Group community content by role