web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Customer experience | Sales, Customer Insights,...
Unanswered

PCF control not working in IE but everywhere else

(0) ShareShare
ReportReport
Posted on by 229

Hello Community,

thanks to Andrews tutorials ( https://www.youtube.com/watch?v=YJ9hrKxAhTU ) and his help in a previous thread I thought I had successfully created a working PCF control that could replace  the ootb DateAndTime.DateAndTime control, but then Internet Explorer happened (well, and the D365 plugin in Outlook - since that's using IE exclusively and apparently there are no plans to move to Edge), and now I have to make the control work with IE, too.

Instead of showing the control, I get an "Error loading control": Error occured during initialization of control: kuc.TimePicker;Message: Ungültiges Argument. (translation: Invalid Argument)

and the full errorDetails are:

Error: Ungültiges Argument.
   at TimePicker.prototype.init (eval code:23:5)
   at CustomControlHostRoot.prototype._initializeControl (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/editformpage.js?v=1.3.228-1912.2:593:21909)
   at Anonymous function (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/editformpage.js?v=1.3.228-1912.2:593:9462)
   at _ (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/app.js?v=1.3.228-1912.2:2684:8745)
   at e.prototype.scheduleControlUpdate (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/app.js?v=1.3.228-1912.2:2904:1647)
   at n (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/editformpage.js?v=1.3.228-1912.2:593:9321)
   at CustomControlHostRoot.prototype._initializeData (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/editformpage.js?v=1.3.228-1912.2:593:9502)
   at CustomControlHostRoot.prototype.componentDidMount (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/editformpage.js?v=1.3.228-1912.2:593:36362)
   at nj (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/vendor.js?v=1.3.228-1912.2:451:172)
   at t.unstable_runWithPriority (https://kucdev.crm4.dynamics.com/uclient/scripts/es5/app.js?v=1.3.228-1912.2:2450:3871)
Aktivitäts-ID: 5457e6b6-2cfd-4a88-b78a-76e56f6bf746

at first I thought this was down to using the blur event I was using to catch inputs once the user leaves the field, but commenting that part out entirely didn't help at all. So I assume this is down to init not being called properly, but I have no idea where to go from here. I didn't see anything come up in the browser console, and the message itself isn't all that helpful either.

The control works fine in Edge (Chromium), Firefox, Chrome and so on, it's just IE that's having issues here.

import {IInputs, IOutputs} from "./generated/ManifestTypes";
import * as $ from "jquery";
import { Component } from "react";

export class TimePicker implements ComponentFramework.StandardControl {

    private date: HTMLInputElement;
    private time: HTMLInputElement;
    private notifyOutputChanged: () => void;
	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(    context: ComponentFramework.Context,
                    notifyOutputChanged: () => void,
                    state: ComponentFramework.Dictionary,
                    container: HTMLDivElement)
    {
        this.date = document.createElement("input");
        this.date.type = "date";
        this.date.className = "date";
        var current = new Date();
        this.date.max = this.padLeft(current.getFullYear(), 4)   "-"   this.padLeft(current.getMonth()   1, 2)   "-"   this.padLeft(current.getDate(), 2);

        this.time = document.createElement("input");
        this.time.type = "time";
        this.time.className = "time";

        var dateTime = context.parameters.dateTime.raw;
        if (dateTime != null) {
            var offset = dateTime.getTimezoneOffset();
            var dateOnly = this.padLeft(dateTime.getFullYear(), 4)   "-"   this.padLeft(dateTime.getMonth()   1, 2)   "-"   this.padLeft(dateTime.getDate(), 2);
            var timeOnly = this.padLeft(dateTime.getHours(), 2)   ":"   this.padLeft(dateTime.getMinutes(), 2);
            this.date.value = dateOnly;
            this.time.value = timeOnly;
        }
        this.notifyOutputChanged = notifyOutputChanged;
        // register onChange events and forward them to CDS
        this.date.addEventListener("blur", () => { // 19
            this.notifyOutputChanged();
        });
        this.time.addEventListener("blur", () => {
            this.notifyOutputChanged();
        });
        
        
        // add fields to container (div)
        container.appendChild(this.date);
        container.appendChild(this.time);
	}


	/**
	 * 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(context: ComponentFramework.Context): void
    {
        var dateTime = context.parameters.dateTime.raw;
        if (dateTime != null) {
            var offset = dateTime.getTimezoneOffset();
            //dateTime.setTime(dateTime.getTime()   offset*60000);
            var dateOnly = this.padLeft(dateTime.getFullYear(), 4)   "-"   this.padLeft(dateTime.getMonth()   1, 2)   "-"   this.padLeft(dateTime.getDate(), 2);
            var timeOnly = this.padLeft(dateTime.getHours(), 2)   ":"   this.padLeft(dateTime.getMinutes(), 2);
            this.date.value = dateOnly;
            this.time.value = timeOnly;

        }

	}

	/** 
	 * 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
    {
        if (this.date.valueAsDate != null && this.time.valueAsDate != null) {
            //offset = this.date.valueAsDate.getTimezoneOffset();
            var offset = this.time.valueAsDate.getTimezoneOffset();
            var dateTime = new Date(this.date.valueAsNumber   this.time.valueAsNumber /*  (offset * 60000)*/);
            dateTime.setTime(dateTime.getTime() (dateTime.getTimezoneOffset()*60000))
            return {
                dateTime: dateTime
            };
        } else {
            return { dateTime: undefined }
        }
	}

	/** 
	 * 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
    }

    /**
     * Helper function to pad strings
     * 
     */
    public padLeft(input: Number, length: Number): String
    {
        var result = input.toString();
        var c = '0';
        while (result.length < length) result = c   result;
        return result;
    }
}

I have the same question (0)
  • rexkenley Profile Picture
    110 on at

    DKasp

    I think your issue is similar to mine.

    powerusers.microsoft.com/.../390013

  • DKasp Profile Picture
    229 on at

    I'm not sure that these issues are related, but maybe that's my limited understanding of the topic.

    The imports for react and jquery were not necessary for my PCF control, so what I just tested was commenting out both lines from my code. The Control still works fine in other browsers, so the imports really weren't necessary - but IE still is unable to load the control.

    Is this a basic issue with PCF controls not loading in IE?

  • rexkenley Profile Picture
    110 on at

    It's not react nor jquery that's the problem. It is IE11 lack of support for es6. The current pcf builder is not "polyfilling" all the necessary functions to make your code work in ie11.

    kangax.github.io/.../

    I had to hack the pcf builder to use corejs 3. Once I did that, my control loaded in IE11.

    Change this file node_modules\pcf-scripts\webpackConfig.js by replacing

    {
        // Tell webpack how to handle JS or JSX files
        test: /\.(js|jsx)$/,
        use: [babelLoader]
    }

    with

    {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
            loader: "babel-loader",
            options: {
                presets: [
                    [
                        "@babel/preset-env",
                        {
                            targets: {
                              chrome: "70",
                              firefox: "63",
                              edge: "17",
                              ie: "11"
                            }
                        }
                    ],
                    "@babel/preset-react"
                ],
                plugins: [
                    "@babel/plugin-syntax-dynamic-import",
                    ["@babel/plugin-transform-runtime", { corejs: 3 }] // non global polyfills
                ]
            }
        }
    }

    After that your code should load, don't forget to load the necessary packages.

  • DKasp Profile Picture
    229 on at

    Finally got around to trying out your suggestions, but I think I did something wrong while implementing it - it didn't change a thing.

    I've replaced the section in webpackConfig.js as per your recommendation, then increased the Version in ControlManifest.Input.xml.

    webpackConfig.js:

    "use strict";
    // Copyright (C) Microsoft Corporation. All rights reserved.
    Object.defineProperty(exports, "__esModule", { value: true });
    const constants = require("./constants");
    const awesome_typescript_loader_1 = require("awesome-typescript-loader");
    const path = require('path');
    // Append a stub to webpack bundle to prevent overwriting global variables
    // If different controls are using the same namespace, webpack will keep redeclaring
    // the namespace as global variables. As a result, only of one the controls can be called.
    // The inserted stub checks whether the namespace already exists and uses a temporary variable
    // to hold the control's constructor.
    function getNamespaceStub(namespace, constructor) {
        const splitNamespace = namespace.split('.');
        let stub = `\tvar ${splitNamespace[0]} = ${splitNamespace[0]} || {};\n`;
        for (let i = 1; i < splitNamespace.length; i  ) {
            const littleStub = `${splitNamespace.slice(0, i   1).join('.')}`;
            stub  = `\t${littleStub} = ${littleStub} || {};\n`;
        }
        stub = stub   `\t${namespace}.${constructor} = ${constants.TEMP_NAMESPACE}.${constructor};\n`  
            `\t${constants.TEMP_NAMESPACE} = undefined;\n`;
        return stub;
    }
    exports.getNamespaceStub = getNamespaceStub;
    // Use registration function if exists, else fall back to the stub that uses the namespace as a global variable
    function generateStub(namespace, constructor) {
        return '\nif (window.ComponentFramework && window.ComponentFramework.registerControl) {\n'  
            `\tComponentFramework.registerControl('${namespace}.${constructor}', ${constants.TEMP_NAMESPACE}.${constructor});\n`  
            `} else {\n${getNamespaceStub(namespace, constructor)}}`;
    }
    exports.generateStub = generateStub;
    function getWebpackConfig(control, controlOutputDir, buildMode, watchFlag) {
        const entryPoint = path.resolve(control.getControlPath(), control.getCodeRelativePath());
        return {
            // `production` mode will minify, while `development` will optimize for debugging.
            mode: buildMode,
            watch: watchFlag,
            watchOptions: {
                aggregateTimeout: 500
            },
            // Tells webpack where to start walking the graph of dependencies
            entry: entryPoint,
            output: {
                // This library value control what global variable the output control is placed in.
                library: constants.TEMP_NAMESPACE,
                pathinfo: true,
                filename: constants.BUNDLE_NAME,
                path: controlOutputDir
            },
            resolve: {
                // Tell webpack which extensions to try when it is looking for a file.
                extensions: ['.ts', '.tsx', '.js', '.jsx'],
                plugins: [new awesome_typescript_loader_1.TsConfigPathsPlugin()]
            },
            module: {
                rules: [
                    {
                        // Tells webpack how to load files with TS or TSX extensions.
                        test: /\.(ts|tsx)$/,
                        use: [
                            babelLoader,
                            {
                                loader: require.resolve('ts-loader')
                            }
                        ],
                        exclude: /node_modules/
                    },
                    {
                        test: /\.(js|jsx)$/,
                        exclude: /node_modules/,
                        use: {
                            loader: "babel-loader",
                            options: {
                                presets: [
                                    [
                                        "@babel/preset-env",
                                        {
                                            targets: {
                                              chrome: "70",
                                              firefox: "63",
                                              edge: "17",
                                              ie: "11"
                                            }
                                        }
                                    ],
                                    "@babel/preset-react"
                                ],
                                plugins: [
                                    "@babel/plugin-syntax-dynamic-import",
                                    ["@babel/plugin-transform-runtime", { corejs: 3 }] // non global polyfills
                                ]
                            }
                        }
                    }
                ]
            }
        };
    }
    exports.getWebpackConfig = getWebpackConfig;
    /* tslint:disable:align */
    // Some babel plugins to support modern JS and TypeScript.
    const babelPlugins = [
        [require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
        require.resolve('@babel/plugin-proposal-class-properties'),
        require.resolve('@babel/plugin-proposal-object-rest-spread'),
        require.resolve('@babel/plugin-syntax-dynamic-import'),
        [require.resolve("@babel/plugin-transform-runtime"), { corejs: 3 }] 
    ];
    // Config for babel to tell it about which browsers we are targeting.
    const babelPresetEnv = [
        require.resolve('@babel/preset-env'), {
            targets: {
                esmodules: true
            }
        }
    ];
    const babelLoader = {
        loader: require.resolve('babel-loader'),
        options: {
            sourceType: 'unambiguous',
            presets: [
                babelPresetEnv,
                [require.resolve('@babel/preset-react')]
            ],
            plugins: babelPlugins
        }
    };
    /* tslint:enable:align */
    

    Then I added the missing modules one by one via 'npm install'

    @babel/plugin-transform-runtime
    @babel/plugin-syntax-dynamic-import
    @babel/runtime-corejs3

    and built the Solution for import afterwards. I deleted the old control from the dynamics instance, then uploaded and pubished the solution with the updated control. I even re-added the control on the form (although that wasn't necessary in the past)

    The control still works fine in all browsers except IE, though :-(
    Did I miss a step, or is there another reason why this didn't help?

  • rexkenley Profile Picture
    110 on at

    Dkasp

    Is it possible for you to post your code on github? Is your solution managed or unmanaged? I had issues with deleting controls using unmanaged solution. So I never used unmanaged again.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > Customer experience | Sales, Customer Insights, CRM

#1
Tom_Gioielli Profile Picture

Tom_Gioielli 70 Super User 2025 Season 2

#2
Gerardo Rentería García Profile Picture

Gerardo Rentería Ga... 33 Most Valuable Professional

#3
Daniyal Khaleel Profile Picture

Daniyal Khaleel 32 Most Valuable Professional

Last 30 days Overall leaderboard

Product updates

Dynamics 365 release plans