2

To be very clear, this is not looking for a solution to a trailhead but the code does come from the lesson walk through where the writer does not give an explanation of why his refactor works.

The lesson discusses the need for events to loosely couple components using events. You create a few events and handlers. The handlers are create, update, and save.

Non-Refactored Methods

 createExpense: function(component, expense) {
   let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
        $A.enqueueAction(action);
     },

updateExpense: function(component, expense) {
    let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                // do nothing!
            }
        });
        $A.enqueueAction(action);

},

The author then adds a "saveExpense" function that allows these two previous methods to be refactored significantly. I do not understand why the updateExpense function does not require to pass a "callback" function to work.

Refactored Code:

({
    createExpense: function(component, expense) {
        this.saveExpense(component, expense, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
        
        $A.enqueueAction(action);
    },
    updateExpense: function(component, expense) {
        this.saveExpense(component, expense);

       
    }, 
    saveExpense: function(component, expense, callback) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        if (callback) {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);
    },
})

TL;DR

I'm trying to understand the refactor of the updateExpense. I don't understand why we don't need the callback when it was present in the first example given by the author. Additionally, I'm trying to understand why we need to use "this". This is not a solution to the challenge, but the descriptions of the preceding sections that I'm having a hard time understanding.

Thanks in advance.

2 Answers 2

8

In the original code, both createExpense() and updateExpense() construct and enqueue a server-side action, calling the same Apex controller method, saveExpense(). That involves some duplication of code across the two methods, which the refactor aims to address.

The key difference between the two methods in the original is that createExpense() updates the component's state, and updateExpense() does not - its callback function does nothing at all. (That's not particularly realistic!)

In the refactor, the boilerplate code that sets up and enqueues the server-side action is moved to a single utility method, saveExpense(), which can be shared between the "create" and "update" code paths. It accepts an optional callback parameter, which is omitted by updateExpense() (JavaScript supplies the falsy value undefined for that missing parameter) because that function doesn't need to update the component state or take other action when the server-side call completes.

Both functions call saveExpense() using this, which is a reference to the containing object. In Aura, it's common to place methods like saveExpense() in the helper object, calling helper.saveExpense() rather than this.

I don't think this example is particularly helpful as real-world code would have some kind of error handling.


Lastly a suggestion: Aura is an obsolete framework. If you need to maintain Aura code or cover one of the few and shrinking areas of functionality that aren't available in LWC, by all means learn and use it - but for general UI development, use LWC instead. It's a much better and honestly more understandable framework.

1
  • Thanks David. I agree that LWC is much better. I'm currently working in an org that has approximately 200 aura components that I need to maintain.
    – thinker
    Commented Jun 5, 2021 at 8:46
3

David's answer is perfectly fine, but we can go a step further: we can make it so we can handle any server call in a condensed manner. This is a function that I use in all of my Aura components that I've written since I "invented" this method (I'm sure it's not original, but here we are).

({
    callServer: function(component, action, params, callback) {
        let serverSideAction = component.get(action);
        serverSideAction.setParams(params || {});
        serverSideAction.setCallback(this, callback || () => {});
        $A.enqueueAction(serverSideAction);
    }
    createExpense: function(component, expense) {
        this.callServer(component, "c.saveExpense", {expense}, (result)=> {
            if(response.getState() === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses",expenses);
            }
        });
    },
    updateExpense: function(component, expense) {
        this.callServer(component, "c.saveExpense", {expense});
    }
})

As you can hopefully see, the callServer method does all of the boilerplate work that we have to do in Aura (one of the many reasons why LWC is superior), meaning all you have to do is pass in parameters.

While the original trailhead demonstrates a refactor, this is a more practical version, as it allows you to compress many calls into a single, or few, lines of code consistently, lending readability and maintainability to your code.

3
  • thanks @sfdcfox. Question on the 'this.callServer(component....)' is the use of 'this' based on the idea we want the function called (e.g. callServer()) to have its effects impact the currently displayed component? What would happen if we didn't use 'this'?
    – thinker
    Commented Jun 7, 2021 at 14:01
  • 1
    @thinker To access another method in the helper, we need to use this to reference the function. Specifically, inside the helper, this refers to the helper itself, which is a shared object across all instances of an Aura component. If you don't use this, you'd get "callServer is undefined".
    – sfdcfox
    Commented Jun 7, 2021 at 14:08
  • Got it! That makes a lot of sense. Thanks!
    – thinker
    Commented Jun 8, 2021 at 23:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .