2

I'm using d3.min.js library in my org. These libraries are being used in lightning component context. this is all working fine till now. After enabling locker service feature it stopped working. Getting below error msg:

Uncaught Error during init [Action failed: c$BaseChartComp$controller$initComponent [TypeError: concreteComponent.getDef is not a function]]

Uncaught TypeError: Cannot set property 'innerHTML'

/resource/D3_3517/D3_3512/d3.min.js:5 Uncaught TypeError: Cannot read property 'document' of undefined

Cannot set property 'innerHTML' of null

Uncaught TypeError: Cannot read property 'matches' of null

Uncaught TypeError: Cannot set property 'innerHTML' of null

Code Snippet which causes the issue:

getConcreteCompHelper: function(component) {
    var me = this,
        concreteComponent = component.getConcreteComponent(),
        ccHelper = concreteComponent.getDef().getHelper();

    return ccHelper;
},
getConcreteComp: function(component) {
    var me = this,
        concreteComponent = component.getConcreteComponent();

    return concreteComponent;
},
handleWindowResize: function(component) {
    var me = this,
        ccHelper = me.getConcreteCompHelper(component),
        childComp = me.getConcreteComp(component),
        chartRedrawListner = function(cc) {
            return function() {
                ccHelper.drawChart(cc);
            }
        },
        listener = chartRedrawListner(childComp);

    window.addEventListener('resize', listener);

    me.windowUnregisterFn = function() {
        window.removeEventListener('resize', listener);
    }
}

Application is erroring out while executing line,

ccHelper = concreteComponent.getDef().getHelper();

Adding screenshot of error message: enter image description here

Break point added to show the stack-trace message enter image description here

Any help would be appreciable.

2

This seems unrelated to the JS library you are using. What is most likely happening is the following: a parent component is trying to call a child helper using the verbose approach:

var concreteHelper = cmp.getConcreteComponent().getDef().getHelper();

We want to remove access to this API because it exposes the complex internal structure of the component. It's hard to test, and hard to maintain.

You need to remove the .getDef().getHelper() and replace it with something else. The trick is to declare a method on your concrete component:

<aura:method name="myMethod" action="{!c.myMethodHandler}"/>

And call it from the super component:

component.getConcreteComponent().myMethod();

In Lightning, every level of inheritance creates a component. This is a better pattern, since the helper is not part of the public API of a component like a method. You can define the method on an interface and ensure every instance abides to the contract, something that the .getDef().getHelper(); was not allowing.

  • Yes, you are correct. Looks like, it encapsulated the component into securecomponent (refer below console output for this statment 'component.getConcreteComponent()'). SecureComponent: markup://c:StackbarLineChart {29:2;a} {clientBusinessNeedsChart}{ key: {"$c":"c"} } Before enabling locker service i was getting as markup://c:StackbarLineChart {29:2;a} {clientBusinessNeedsChart} – Suresh Kumar Jun 28 '16 at 10:55
  • Ok, here is the situation: Lightning is different than JS as it creates one instance per level of inheritance. When we design an application, it's the same cost whether your compose (as in include) or you extend another component. We will add getConcreteComponent() to SecureComponent and help with this use case, but things will not work as before because getDef() will not be exposed. What you will have to do is create a method on your concrete component and link it to your helper method. Would that work for you? – JF Paradis Jun 28 '16 at 15:20
  • Ok, ConcreteComponent is exposed. I will modify my answer with the proper steps. – JF Paradis Jun 28 '16 at 16:53
  • Error "Uncaught Error during init [Action failed: c$BaseChartComp$controller$initComponent [TypeError: concreteComponent.getDef is not a function]]" is fixed but getting next error "Uncaught TypeError: Cannot read property 'document' of undefined throws at /resource/D3_3517/D3_3512/d3.min.js:5:4186" – Suresh Kumar Jun 30 '16 at 8:43
  • This is the line in d3.min.js referring to "fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so)" – Suresh Kumar Jun 30 '16 at 10:47
2

We have been using D3 in our lightning components and ran into the same error message. We found that, when Locker Service is turned on, "this" in D3's encapsulated function becomes undefined in the Component. We have resolved the issue by defining a variable "self" and assigning "window" to it if "this" is undefined. We then had to update references to "this" in that encapsulated function's scope (not anonymous functions defined within it) to reference "self". Locker Service actually helped us find those instances and after resolving all of them, D3 worked with Locker Service turned on.

  • 1
    In D3 v3 there was an issue in that library itself that made it's module initialization not function correctly with strict mode (made the assumption in 1 location that this === window). That has been fixed in the latest version of D3 which is 4.2.6 and works find with LS activated. – Doug Chasman Oct 7 '16 at 11:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.