Why EXACTLY not calling Aura.Action attribute from Lightning Controller using $A.enqueueAction()?

Since the event registration/subscription feels like a long roundtrip for simple things, I found a “hack” for implementing a simple onchange handler in my own Lightning Components in form of an Aura.Action unsing $A.enqueueAction() in the child compmonent as discussed here:

Calling Aura.Action attribute from Lightning Controller/Helper

The basic pattern is

c:child

markup :
    <aura:component>
        <aura:attribute name="onchange" type="Aura.Action" />
    </aura:component>

controller :
    onSomethingHasChanged : function(cmp, evt, hlp) { 
        $A.enqueueAction( cmp.get('v.onchange') );
    }, 

c:parent

markup :
    <aura:component>
        <c:child onchange="c.onChildChange" />
    </aura:component>

controller :
    onChildChange : function(cmp, evt, hlp) { 
        console.log('it works!')
    }, 

We are discouraged to do so

enter image description here

btw. haunted houses always made me curious, even at night… 😉

Questions

Now it works, it’s short and most importantly it’s super readable code.

Can you point me to the limits:

  • what EXACTLY does not work?
  • What exactly is so dangerous to use this pattern?
  • What I can not do? What will fail?
  • If it is so bad, why it is there at all?
  • How did Salesforce wire-up their components which are providing onchange attributes such as lightning:input?

Alternatives?

  • are there any alternatives which can provide such a nice notation for the child-compo like <c:child onchange="c.onChildChange"/> without the event-pattern verbosity? Quick and clean?

Answer

what EXACTLY does not work?

You may find that Locker Service will filter out some or all of the data you’re trying to work with, even within your own namespace. For this reason, the two related methods for using Aura.Action, $A.run and Action.run, have been deprecated in favor of event handling.

Previously, these methods would allow you to pass in parameters, but Locker Service ends up converting those parameters to Proxy objects, and may ultimately cause you to lose access to the data in the parent component, even within the same namespace. With the run functions having been deprecated, the only other alternative is $A.enqueueAction, which does work, but…

You have no event to work with. No parameters. While it works in your super-simple example, in the Real World, we like having parameters in our events so we can tell the parent about what’s going on (e.g. aura:valueChange tells the handler what the old and new values are). Not having this capability is frustrating for most non-trivial events. Also, note that evt in your example is undefined. Developers assuming that evt will have a value are in for a rude awakening.

What exactly is so dangerous to use this pattern?

As above, Locker Service can make data randomly inaccessible. There’s no way to predict ahead of time what scenarios will trigger it, and it might even be a moving target as they continue to refine Locker Service and Aura in general. That’s also part of the Haunted House effect they mentioned. Your code can appear to be haunted, randomly causing crashes for no good reason.

What I can not do? What will fail?

I think this is pretty well covered, but you can’t use the run methods (the only way to get parameters to your method), $A.enqueueAction won’t have an event or parameters for you to work with, and you can’t reset the Aura.Action attribute later with a new handler for any reason, which can come back later to haunt you if you’re using a lot of dynamic component generation. In fact, I don’t think you even get access to the correct component with $A

If it is so bad, why it is there at all?

Backwards compatibility, for the moment. This method was pre-Locker-Service. Before LS could be properly implemented, they knew they were going to have to get rid of Action.run and $A.run because of security access problems. If you want a secure component and proper access to your data, you won’t use this method.

How did Salesforce wire-up their components which are providing onchange attributes such as lightning:input?

It’s currently a mix of SecureEvent/aura:registerEvent and a system-only method Action.runDeprecated. I expect that they’ll eventually convert the rest over, remove runDeprecated, and never look back. Aura.Action itself might eventually be deprecated, since it serves no purpose in a world where we have to do everything via SecureEvent instead.


are there any alternatives which can provide such a nice notation for the child-compo like <c:child onchange="c.onChildChange"/> without the event-pattern verbosity? Quick and clean?

Honestly, except for the fact that you need an event (which you could just use one or a handful of generic events with generic data types), it is already just as clean.

Event Way

<aura:registerEvent ... />

component.getEvent("eventName").setParams({...}).fire();

Aura.Action Way

<aura:attribute type="Aura.Action" ... />

// Cannot set any parameters, sorry...
$A.enqueueAction(component.get("v.methodName"));

Note that in both of the above examples, the parent doesn’t care about the underlying complexity, as, except for the missing event parameters, it looks identical in markup.

Attribution
Source : Link , Question Author : Uwe Heim , Answer Author : sfdcfox

Leave a Comment