4

Lightning Web Components (LWC) are made up of multiple files in a bundle. For this question I am focusing on the component's main .js file where the web component js class is implemented.

Example code:

import { LightningElement, api } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import doSomething from '@salesforce/apex/MyApexClass.doSomething';

export default class MyLWC extends NavigationMixin(LightningElement) {
    @api a;
    @api b;

    download() {
        let that = this;
        doSomething({
            param1: this.a,
            param2: this.b
        })
        .then((result) => {
            that.navigateToWebPage(result);
        })
        .catch((error) =>{
            console.log(`Error occured ${error}`);
        });
    }

    navigateToWebPage(url) {
        // Navigate to a URL
        this[NavigationMixin.Navigate]({
            type: 'standard__webPage',
            attributes: {
                url: url
            }
        },
        false // Replaces the current page in your browser history with the URL
      );
    }
}

There are two things I don't understand here:

  • extends NavigationMixin(LightningElement) syntax - why brackets? Can js classes extend more than one parent class?
  • this[NavigationMixin.Navigate] - judging by the code it seems like 'this' got assigned the Navigate function (and potentially other functions from NavigateMixin?) to its prototype. Why aren't we using NavigationMixin like any other imported component - just calling their functions directly, not through this?

Can someone explain what is actually happening when using this kind of class extensions/inheritance, and if the syntax used here is something standard from the web components world or something specific for LWC?

Thanks for your answers!

1
  • 1
    Take a look at this article about what mixins do and how you can use them, generally, in JavaScript.
    – Phil W
    Commented Oct 7, 2020 at 9:35

2 Answers 2

7

The navigation API uses JavaScript Mixins!

Mixin – is a generic object-oriented programming term: a class that contains methods for other classes. Some other languages allow multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.

Read more about mixins here and more about class based mixins here

I have always questioned on this since on how hard the syntax is and why lwc team have chosen mixin only for navigation. It seems to deviate away from composition and in this case inheritance is chosen over composition!

Response from LWC platform Team — LNS(Lightning Navigation Service) has it on their roadmap to move away from the Mixin because of the inconsistent look/feel.

The root quality of the navigation API that led to something different is that it’s a contextual API that should be handled like an event would, but we choose to surface it more like a traditional JS/function API to avoid the dependency on events and for possible future static analyzability that one could not get with events.

There is a roadmap to bring in alignment with single imports than creating a mixin class

4
  • 2
    I didn't point to that article in my comment because the syntax used doesn't align with that shown in LWCs. The reason to use a mixin is because you may have a custom hierarchy of LWC classes, rooted at LightningElement then need to add navigation to a new sub-class. If you use regular inheritance you can't do this without changing the root of the whole hierarchy to allow for navigation. I do think the alternative is to have a purely "external" navigation service (one not incorporated into your inheritance) to which you pass your element if that service needs to examine its state.
    – Phil W
    Commented Oct 7, 2020 at 9:58
  • 2
    I think what would be ideal is a simple class or a service that once could just import it! Like all other modules like showtoast or lightning message service! It can also use decorators to pass in the context if needed! Using mixins, the syntax is no more elegant! Commented Oct 7, 2020 at 10:02
  • 2
    @Phil W The article you shared is really neat and I have added as well to my answer! Mixins are hard to wrap head around specifically for someone not so familiar with object oriented programming! Commented Oct 7, 2020 at 10:07
  • Would you please share an article that explains mixins but with the same syntax used in LWC if you know of any? I'd really appreciate that!!
    – Bahman.A
    Commented Feb 5, 2021 at 19:24
4

Mixins can basically merge some elements into the prototype of a class. For a succinct example:

class Animal {
    constructor(family) { this.family = family; }
    makeSound() { console.log('Animal of the ' + this.family + ' family making a sound.'); }
}
const StickCatcher = (superclass) => class extends superclass {
    catchStick() { console.log('Entity of the ' + this.family + ' family catching a stick.'); }
}
class Dog extends StickCatcher(Animal) {
    constructor(dogName) {
        super('canine'); // the super() "routes" to Animal (not to the StickCatcher)
        this.dogName = dogName;
    }
    wagTail() { console.log(this.dogName + ' wags his tail.'); }
}
const fido = new Dog('Fido');
fido.makeSound(); // Animal of the canine family making a sound
fido.catchStick(); // Entity of the canine family catching a stick
fido.wagTail(); // Fido wags his tail

The StickCatcher is a MixIn; it is a "class factory" which returns a class that was "enriched" with some functions -- in this case with a catchStick() function. The StickCatcher doesn't care which particular class it will be trying to extend. (however, catchStick() would care that this.family exists inside it).

On the other hand, note that the super-class of Dog is Animal. StickCatcher is not a class, it is a function that returns classes, i.e. a "class factory". The super('canine') is thus fed to the Animal constructor. The result is a Dog which has all the elements of Animal and of StickCatcher (alongside those of Dog, of course) -- i.e. the StickCatcher methods got mixed into the Animal-to-Dog class hierarchy.

What is the difference from just doing inheritance, i.e. the difference from class StickCatcherClass extends Animal { catchStick(){} } ? The difference is that the StickCatcher mixin is not "tied" to Animal, that you can use it in many other circumstances too, e.g. also use it in class StickCatchingDancingRobot extends StickCatcher(Robot) { dance(){} }.

LWC's NavigationMixin is of a similar nature. It will add the functions for navigation and for URL generation into your class prototype - but will add them under some "ugly, non-conflicting function names". The NavigationMixin.Navigate is a static string or getter, meant to return the said "ugly but non-conflicting function name", so that this[NavigationMixin.Navigate] dereferences to the navigation function that was added to your prototype by the said mix-in.

2
  • Thanks for this very helpful answer. Would you please post a link to a mixin article that teaches about mixins using the syntax that's used in LWC mixin, if you of any? Thanks!
    – Bahman.A
    Commented Feb 5, 2021 at 19:28
  • 1
    Hi @Bahman.A . A good description of the "class factory approach to Mixins" (which LWC uses here) is on link. You can also generally achieve them with "class prototype assign", like Object.assign(MyClass.prototype, mixinObjectWithFunctions), which (opposed to "class factory") decouples the class definition from the "MixIn mixing", and lets you apply many of them. For that, see e.g. link. Commented Feb 8, 2021 at 10:23

You must log in to answer this question.

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