Wednesday, December 19, 2018

Lightning Web Components - Unit Testing immediate impression.

One of the things I’m most excited about in Lightning Web Components is the ability to write Unit Tests for the components.

The unit testing framework of choice is Jest, and it looks well suited. Not least it’s the framework of choice for Facebook, and describes itself as well suited to React apps. Why should that matter? Well, React is a 1-way bound Javascript framework - and so is LWC.

So I was looking forward to get into Unit Testing, following the documentation for testing wired components

Unfortunately, the documentation didn’t work out for me, and it looked like there’s a couple of mistakes in there:

The example ‘getRecord.json’ file isn’t valid JSON.

In order for the file to work, the field names need to be surrounded in double quotes.

I.E. Instead of:

    // getRecord.json
    {
       fields: {
           Name: {
               value: "DYNAMO X1"
           }
       }
    }

The file should be:

    // getRecord.json
    {
       "fields": {
           "Name": {
               "value": "DYNAMO X1"
           }
       }
    }

Interrogating the ‘element’ for its state does not seem to work.

Instead, I found that I needed to get data from the document object.

I.E. The following does not work:

    // Resolve a promise to wait for a rerender of the new content.
       return Promise.resolve().then(() => {
           const content = element.querySelector('.content');
           expect(content.textContent).toBe("Name:DYNAMO X1");
       });

But the following does:

    // Resolve a promise to wait for a rerender of the new content.
       return Promise.resolve().then(() => {
           const content = document.body.querySelector('.content');
           expect(content.textContent).toBe("Name:DYNAMO X1");
       });

Mocking doesn't seem to work for Apex, only LDS

From the quick test I did, I could get the mocking framework to work for the Lightning Data Service, once my implementation of the example was tweaked. However, I couldn't get it to work with an imported Apex method

I didn't see a reference to this being missing, though I guess I may have just missed that, and I know that the recommendation is to use LDS whenever you can. I just worry that there's a really important use case out there - it seems natural to me that components that use custom Apex are likely to be more complex than ones that use LDS. And with that in mind, it feels like missing Apex will be a big loss to the testing framework.

Hopefully the last part is already known about, is a bit of missing documentation, or is simply that I misunderstood something.

Whatever the case, I plan on doing more investigations into the Unit Testing capabilities, and will obviously blog my findings - but I have to admit that I found the initial experience a little disappointing after the extremely polished experience up to now.

I sincerely hope that it isn’t an indicator that Unit Testing is bit of an after-thought.

Update - 20/12/18

It felt like a good avenue for exploring testing would be to put together a test for the "message" component that I put together for the blog post on re-usable components

Unfortunately, I stumbled on this block. I was able to pretty quickly write a test that proved that @api properties were rendered properly in the resulting HTML, but I couldn't find a way of setting the value the slot. Adding a textNode as a child of the built node is blocked (seemingly by LWC), with an error that appears to suggest it can be worked around. But with no substantial documentation yet available, it feels like I'm just shooting in the dark

When I couple that with the fact that the git repo for the ebikes app seems to only contain one set of tests for the productFilter component, I'm stating to take the hint that unit testing hasn't been explored by the team fully yet.

I think it's entirely understandable, and I still hope that this area will be fleshed out significantly in the coming weeks, but the first impression remains - not yet

Lightning Web Components - @api, slots and getters

I've blogged about a few of the behaviours of Lightning Web Components, but the proof is really in building useful bits. What happens when you actually try to make a re-usable component?

For our example, we'll rebuild 'ui:message'. A now (seemingly) defunct base component that would render a message in a box that is coloured based on the 'severity' of the message being shown. In the original it could be set to 'closable', although we're going to ignore that and focus on just the rendering of it.

In a Lightning component we would use it like this:

Original usage - Lightning Component

    <ui:message title="Error" severity="error" >{!v.errorMessages}</ui:message>

Ideally, the version we will create, would be used like this:

Desired usage

    <c-message title="Error" severity="error" >{errorMessages}</c-message>

Looks pretty straightforward, and actually - it is. Just as long as we know about a few simple concepts.

Before we go into them, let's see what a working example could look like:

Javascript component

    import { LightningElement, api } from 'lwc';

    export default class Message extends LightningElement {

        @api title;
        @api severity;

        get classes() {
            return this.severity + ' uiMessage';
        }
    }

HTML Template

    <template>
        <div class={classes} role="alert" >
            <div class="uiBlock" >
                <div class="bBody" >
                    <h4>{title}</h4><slot></slot>
                </div>
            </div>
        </div>
    </template>

OK then, let's pick a few of these bits apart, and hopefully we'll explain a few little behaviours along the way.

First up, let's take a look at the '@api' declarations.

@api

The @api property lines are pretty simple to understand - they define that 'title' and 'severity' are publicly available properties of the component. In the context of Lightning Web Components, public and private mean 'available outside of the component, and invisible to the outside of the component'. It's tempting to think that this is to do with the scope of the Javascript, but it's not.

That is, every property of the Javascript component is available to be referenced in the HTML template - whether it is 'public' or 'private'. One way of thinking about it is that the HTML template forms part of the component, and so it can see private properties.

Another (probably more accurate) way of thinking about it is that the template is processed by the Javascript component (that code it's immediately obvious, but it's almost certainly in LightningComponent - which this class extends), and the Javascript can see its own properties, so the private ones are available.

However, other components (like ones that include this in their templates) can only see public properties. @api is how you make them public. Doing so means that they are available as attributes on the tag you use to include the component (hence <c-message title="Error"... is possible)

Not only that, but every @api decorated property is also 'reactive'. That is, whenever its value changes the component is re-rendered. The documentation is pretty clear on that point - and is presented as a fundamental property of a public property:

Public Properties

To expose a public property, decorate it with @api. Public properties define the API for a component. An owner component that uses the component in its markup can access the component’s public properties. Public properties are reactive. If the value of a reactive property changes, the component’s template rerenders any content that references the property.


Why would a public property be reactive?

Put simply, if we change the value of one of those properties in a parent component, we want the component to re-render - and it's pretty much guaranteed that we ALWAYS want the component to re-render.

For example, we may do the following:

    <c-message title="{title}" severity="{severity}" >{messages}</c-message>

When the value of 'title' or 'severity' changes, we would always want the message box to re-render to show our new values. And so the framework takes care of that and makes EVERY public property of the component reactive

So that takes care of the attributes we need to pass in, what about the content?

Slots

Lightning Components had facets. And they weren't intuitive. I mean they weren't complex, but they weren't in keeping with HTML - they always felt unnatural - especially in the simplest of cases.

Lightning Web Components fixes that, with slots. And in the simple case they are trivial. The documentation isn't long, and doesn't need to be.

All we need to do, in this simple case, is add <slot></slot> into our component, and the body of any tag that instantiates the component will be rendered in that slot.

Now something that's missing from the documentation, which is a fairly obvious behaviour once you see it in action, is that slots are effectively reactive.

That is, if you change the content of the tag, that content is immediately reflected in the component's rendered output.

So, in our example:

    <c-message title="Error" severity="error" >{errorMessages}</c-message>

Whenever the value of 'errorMessages' changes, the slot inside the 'message' component is re-rendered to include the new content.

I admit, I had assumed that this would be the case, but I didn't immediately realise that it was an assumption. So I thought it was worth calling out

Getters

The final part of the example that I want to explain is the use of the 'getter':

    get classes() {
        return this.severity + ' uiMessage';
    }

What we're doing here is building a list of CSS classes for a node in the component that includes one of the passed in attributes plus a standard class that must be applied

The use of the getter illustrates an important difference between the behaviour of the templates in Lightning Components (LC) and Lightning Web Components (LWC), as well a reminder of the behaviour of properties.

That is, in LC we could have done the following in our template:

    <div class="{!v.severity + ' uiMessage'}" role="alert" >

In LC, our replacements could include expressions, so we could build up strings in the template. In LWC, we can't do this, we can only reference properties or getters.

Not only that, but we can't build up the strings in the attribute assignment.

I.E. We can't do this:

    <div class="{severity} uiMessage" role="alert" >

In LWC we don't assign properties to attributes in this way, the framework takes care of the wrapping in double quotes, escaping the strings, and other such things, so we can only assign the property, and that's it.

I.E. This is what is allowed:

    <div class={severity} role="alert" >

So, if we want to assign more than just the value of 'severity' to the class attribute, we need to build that string up outside of the template.

Your first reaction might be - OK, we can create a trackable property to store it, right?

    @track classes = this.severity + ' uiMessage';

But this doesn't work. You'll end up with the classes property defined as 'undefined uiMessage', and it won't change. Why is that?

Well, it's tempting to think that 'track' and 'api' mean that Javascript will re-run when things change, but that's not what they do - nor what the documentation says they'll do

Rather, if a property is reactive it means that the component will be re-rendered when the property changes. That says nothing about running Javascript.

So when we look at the above, what happens is the property 'classes' is set when the Javascript object is constructed. At this point the property 'severity' is undefined. When the 'severity' is updated via the attribute, the component is re-rendered and the 'classes' property is re-injected into the template, but the Javascript that sets the classes property is not re-run - that is only executed when the object is instantiated.

So, instead of setting the 'classes' property directly, we set up a getter for it:

Javascript component

    get classes() {
        return this.severity + ' uiMessage';
    }

Now, when the 'severity' property changes, the 'classes' property is re-injected. In order to get the value for 'classes', the getter is executed - this is the only way the property can be retrieved. In doing so, the string concatenation is re-evaluated and the new value is retrieved.

Summary

None of the concepts here are particularly difficult, or really that earth shattering, but building even the simplest of re-usable components starts to shed some light on what the parts do any why.

The framework has been very thoughtfully put together, and some of the techniques will be different to what people are used to, having cut their Javascript teeth with Lightning Components, but the changes are for very good reasons. An example like this really shows how those changes make for simple components.

Tuesday, December 18, 2018

Lightning Web Components - The subtleties of Tracking and Wiring

Following on from yesterday's investigations into the behaviour of '@track', and its effect on untracked properties, I figured I should expand my remit to '@wire'.

My main reason for that was the statement in the documentation:

In the wire adapter’s configuration object, prefix a property with $ to reference a property of the component instance. The $ prefix tells the wire service to treat it as a property of the class and evaluate it as this.propertyName. The property is reactive. If the property’s value changes, new data is provisioned and the component rerenders.

This comment relates to code along the lines of:

    @api recordId;

    @wire(getRecord, { recordId: '$recordId', fields })
    contact;

The bit that really piqued my interest was 'The property is reactive'.

In all the examples, it looked like the property being referenced was always tracked or set as an api field (the latter implying the former)

That's the case in the above example - the property passed as a parameter 'recordId' as referenced by '$recordId' is defined as an api field, and is therefore tracked.

There's also that recurring point 'the component rerenders', which we saw in my previous post. Recalling that, it essentially meant that a change to a tracked property caused all untracked properties in the same component (and I am assured, but yet to prove, child components) to be re-rendered

So, what is the actual behaviour in this case? Are the implications the same?

You can code that illustrates the below examples, as well as the points from yesterday's post in this bitbucket repository if you want to explore the behaviours further. I suggest you take a look.

Tracked property used as a parameter to a wired property

The natural first example is much like the one exampled above. We have an Apex method that takes a parameter. We wire that parameter to a javascript property, and reference a single parameter.

Javascript component

    @track searchString;

    @wire(searchContacts, { searchString: '$searchString' })
    contacts;

When the searchString property is updated:

  • The searchString's change causes the wired function to be re-evaluated.
  • Therefore the value of 'contacts' changes.
  • The component treats contacts as if it was tracked and causes the component to be re-rendered.

OK. That's pretty decent, and what the documentation says will happen. No surprises there!

But then I got to thinking: So, what happens if the property isn't tracked?

My first guess was that maybe the Apex method wouldn't get re-executed, but I wasn't entirely convinced - after all, the documentation only really makes a big deal about rendering. So I changed the code and tried again...

Javascript component

    searchString;

    @wire(searchContacts, { searchString: '$searchString' })
    contacts;

This time, when the searchString property is updated:

  • The searchString's change causes the wired function to be re-evaluated.
  • Therefore the value of 'contacts' changes.
  • The component treats contacts as if it was tracked and causes the component to be re-rendered.

Erm. OK. It's the same. Hmmm....

Looking at what's actually going on, that does make sense. It's the '$searchString' reference that tells the framework that searchString is reactive, as respect to the wiring, so it's that causing the Apex to re-execute. And once the method is re-evaluated, the value of 'contacts' changes, and that causes the component to re-render.

That got me to thinking - so what does the '@track' do against the searchString in the original example. Experience tells me that these things generally will have an effect.

So I added something else to my example...

    renderedCallback() {
        console.log( 'renderedCallback was called' );
    }

This hooks into the component's lifecycle, as described here, and will tell us when the component gets re-rendered.

It turns out that my initial understanding of the first example was slightly wrong, though not in a way that would generally have much of an impact.

That is, if the parameter is tracked, you end up with the following:

  • The searchString's change causes:
    • The component to be re-rendered (since it is tracked).
    • The wired function to be re-evaluated (since it is referenced as a '$' parameter).
  • The execution of the wired function causes the value of 'contacts' to change.
  • The component treats contacts as if it was tracked and causes the component to be re-rendered.

The result is that the component is re-rendered twice!

And sure enough, if you take the tracking off the searchString parameter, the component is only re-rendered once.

So, this does reinforce another point that the documentation makes here:

Don’t overuse @track. Track a property only if you need the component to rerender when the property’s value changes.

Personally, I think I'd call out this particular behaviour and remind people - you don't need to 'track' a property in order for the wired method to re-execute - but maybe it's just my lack of attention that missed that little point.

And I'd also state that you should only track a property that is being used as a reactive wired parameter if you need the property to be re-rendered before the Apex you are calling returns with its result.

Monday, December 17, 2018

Lightning Web Components - the art of Tracking

On Friday I wrote an excited blog post on the introduction of Lightning Web Components.

One of the things I mentioned was that I didn't quite get how '@track' was supposed to work

Since then, I've found the documentation (add /docs/component-library/documentation/lwc/lwc.get_started_introduction to the end of your Salesforce URL), read it, got confused by my memory, wrote a test-rig and looked at it all in depth.

On the surface, it's simple, but there are some complications, which were the cause of my confusion...

In the documentation it says the following:

Both @track and @api mark a property as reactive. If the property’s value changes, the component re-renders.

OK, nice and simple. So what does that mean?

A single untracked property

Testing with a simple component that contains a single property that is not tracked I found the following behaviour:

Javascript component

    export default class TrackExperiments extends LightningElement {
        primitiveUntracked;

        handlePrimitiveUntrackChanged( event ) {
            this.primitiveUntracked = event.target.value;
        }
    }

Template

    <p>The value is: {primitiveUntracked}</p>
    <lightning-input label="Input without an on-change handler"
                        type="Text"
                        value={primitiveUntracked}></lightning-input>
    <lightning-input label="Input with an on-change handler"
                        type="Text"
                        value={primitiveUntracked}
                        onchange={handlePrimitiveUntrackChanged}></lightning-input>
  • Changing the value defined in either of the inputs will not result in the rendered value changing.
  • Changing the value defined in the input without the onchange handler will not change the Javascript property.
  • Changing the value defined in the input that does have an onchange handler will update the Javascript property.

So, fairly straightforward, and maybe in-line with what you would expect:

  • Untracked properties are not re-rendered in the UI when they change
  • Untracked properties that are bound to inputs are not automatically updated when the input changes unless there is an onchange handler defined that updates the Javascript property

A single untracked property

Testing with a simple component that contains a single property that is tracked, I found the following behaviour:

Javascript component

    export default class TrackExperiments extends LightningElement {
        @track primitiveTracked;

        handlePrimitiveTrackChanged( event ) {
            this.primitiveTracked = event.target.value;
        }
    }

Template

    <p>The value is: {primitiveTracked}</p>
    <lightning-input label="Input without an on-change handler"
                        type="Text"
                        value={primitiveTracked}></lightning-input>
    <lightning-input label="Input with an on-change handler"
                        type="Text" value={primitiveTracked}
                        onchange={handlePrimitiveTrackChanged}></lightning-input>
  • Whenever the value of the Javascript property changes, it is reflected on the outputted page.
  • Changing the value defined in the input without the onchange handler will not change the Javascript property, and so the rendered property does not change.
  • Changing the value defined in the input that does have an onchange handler will update the Javascript property, and both the rendered property and the value in the other input is updated.

OK, in my opinion, slightly odd - I was expecting the input binding to be two-way - that's not the case:

Lightning Web Components contain 1-way binding.

Still, it may have surprised me, but it's easy to remember, and no big deal once you know that's the behaviour.

  • Tracked properties are re-rendered in the UI when they change.
  • Tracked properties that are bound to inputs are not automatically updated when the input changes unless there is an onchange handler defined that updates the Javascript property.
  • Inputs that are bound to tracked properties are automatically updated when the underlying property is updated.

A tracked property and an untracked property on the same page

Javascript component

    export default class TrackExperiments extends LightningElement {
        @track primitiveTracked;
        primitiveUntracked;

        handlePrimitiveTrackChanged( event ) {
            this.primitiveTracked = event.target.value;
        }

        handlePrimitiveUntrackChanged( event ) {
            this.primitiveUntracked = event.target.value;
        }
}

Template

    <p>The value is: {primitiveTracked}</p>
    <lightning-input label="Input without an on-change handler"
                        type="Text"
                        value={primitiveTracked}></lightning-input>
    <lightning-input label="Input with an on-change handler"
                        type="Text" value={primitiveTracked}
                        onchange={handlePrimitiveTrackChanged}></lightning-input>

    <p>The value is: {primitiveUntracked}</p>
    <lightning-input label="Input without an on-change handler"
                        type="Text"
                        value={primitiveUntracked}></lightning-input>
    <lightning-input label="Input with an on-change handler"
                        type="Text"
                        value={primitiveUntracked}
                        onchange={handlePrimitiveUntrackChanged}></lightning-input>

Now things start to get a little surprising.

  • Changing the inputs related to the tracked property works as described above, as if it was the only property on page.
  • Then, changing the inputs related to the untracked property as described above.
  • However, changing the tracked property inputs after changing the untracked input, causes both the tracked and untracked properties to be re-rendered.

The last point really took me by surprise - I was expecting that only the tracked property would be updated. This led me to go back to the documentation again and re-read it.

That is (bold added for emphasis):

Both @track and @api mark a property as reactive. If the property’s value changes, the component rerenders.

Yep, I think I'm reading that right, and it looks like it's reflected in the behaviour I see (without delving too deep into it). When you change the value of a tracked property, the whole component re-renders. This means that any untracked properties will also be re-rendered.

I've put together a more complete illustration of that, which you can find in this git repo.

Friday, December 14, 2018

Lightning Web Components - the dawn of (another) new era

Note that this post was put together before the documentation for Lightning Web Components went live at developer.salesforce. That site should be your first port for any explanation of a concept in LWC!

Salesforce have a new technology. Lightning Components look like they’re on the way out, and are being replaced with a new technology ‘Lightning Web Components’.

The reasons behind that, and the main principles behind its design are covered very nicely in this article on developer.salesforce.com.

From that we can then get to a series of examples here.

(Note: some of the code snippets used below, to illustrate points, are taken from the recipes linked above)

Now I’m a big supporter of evolution, and I love to see new tools being given to developers on the Salesforce platform, so, with a couple of hours to play with it - what’s the immediate impression?

This is an article on early impressions, based on reviewing and playing with the examples - I fully expect there to be misunderstandings, bad terminology, and mistakes in here - If you're OK with that, I'm OK with that. I admit, I got excited and wanted to post something as quickly as possible before my cynical side took over. So here it is - mistakes and all.

WOW. Salesforce UI development has grown up.

Salesforce aren’t lying when they’ve said that they’re trying to bring the development toolset up to the modern standards.

We get imports, what look like annotations and decorators, and there’s even mention of Promises. Maybe there’s some legs in this…

It’s easy to dismiss this as ‘Oh no, yet another change’, but the thing is - the rest of industry develops and improves its toolset - why shouldn’t Salesforce?

The only way to keep the product on point IS to develop the frameworks, replace the technology, upgrade, move on. If you don’t do that then the whole Salesforce Ecosystem starts to stagnate.

Or to put it another way - in every other part of the developer community, learning from what was built yesterday and evolving is seen as a necessity. It’s good to see Salesforce trying to keep up.

So what are the big things that I’ve spotted immediately?

import is supported, and that makes things clearer

Import is a massive addition to Javascript that natively allows us to define the relationships between javascript files within javascript, rather than at the HTML level.

Essentially, this replaces the use of most ‘script’ tags in traditional Javascript development.

For Lightning Web Components, we use this to bring in capabilities from the framework, as well as static resources.

E.g. Importing modules from the Lightning Web Components framework:

 import { LightningElement, track } from 'lwc';

Importing from Static Resources:

 import { loadScript } from 'lightning/platformResourceLoader’;
 import chartjs from '@salesforce/resourceUrl/chart';

What this has allowed Salesforce to do is to split up the framework into smaller components. If you don’t need to access Apex from your web component, then you don’t need to import the part of the framework that enables that capability.

This *should* make individual components much more lightweight and targeted - only including the capabilities that are required, when they are required.

Getting data on screen is simpler

Any javascript property is visible to the HTML template.

E.g.

    export default class WebAppComponentByMe extends LightningElement {
     contacts;

We can then render this property in the HTML with {contacts} (none of those attributes to define and none of those pesky v dot things to forget).

Much neater, much more concise.

We track properties

Looking at the examples, my assumption was that if we want to perform actions when a property is changed, we mark the property trackable using the @track decorator.

For example:

    export default class WebAppComponentByMe extends LightningElement {
     @track contacts;

I was thinking that, at this point, anything that references this property (on page, or in Javascript) will be notified whenever that property changes.

However, at this point I can't really tell what the difference is between tracked and non-tracked properties - a mystery for another day

Wiring up to Apex is much simpler

One of the big criticisms of Lightning Components that I always had was the amount of code you need to write in order to call an Apex method. OK, so you have force:recordData for a lot of situations, but there are many times when only an Apex method will do.

In Web Components, this is much simpler.

In order to connect to Apex, we import the ‘wire’ module, and then import functions into our javascript

 import { LightningElement, wire } from 'lwc';
 import getContactList from '@salesforce/apex/ContactController.getContactList';

The first line imports the wire capabilities from the framework, the second then imports the Apex method as a javascript method, therefore making it available to the component.

We can then connect a javascript property up to the method using the wire decorator:

 @wire(getContactList) contacts;

Or wire up a javascript method:

 @wire(getContactList)
 wiredContacts({ error, data }) {
  if (data) {
   this.contacts = data;
  } else if (error) {
   this.error = error;
  }
 }

When the component is initialised, the getContactList method will be executed.

If the method has parameters, that’s also very simple (E.g. wiring to a property):

 @wire(getContactList, { searchKey: '$searchKey' })
 contacts;

Changing the value of a property causes Apex to re-execute

Having wired up a property as a parameter to an Apex bound Javascript function, any changes to that property will cause the function to be re-executed

For example, if we:

 searchKey = '';

 @wire(findContacts, { searchKey: '$searchKey' })
 contacts;

Whenever the searchKey property changes, the Apex method imported as ‘findContacts’ will be executed and the contacts property is updated.

Thankfully, we can control when that property changes, as it looks like changing the property in the UI does not automatically fire a change the property on the Javascript object. In order to do that, we need to change the property directly.

E.g. Let’s say we extend the previous example and there’s an input that is bound to the property, and there’s an onchange event defined:

 

And the handler does the following:

 handleKeyChange(event) {
  this.searchKey = event.target.value;
 }

This will cause the findContacts method to fire whenever the value in the input is changed.

Note that it is the assignment to this.searchKey that causes the event to fire - it looks like the binding from the HTML is 1-way. I admit that I need to investigate this further.

Events do not require configuration to be implemented

Events work in a completely different way - but then that’s not a problem - Application and Component events were different enough to cause headaches previously. The model is actually much simpler.

The example in the above referenced repository to look at is ‘PubSub’.

It’s much too involved to into detail here, but the result is that you need to:

  • Implement a Component that acts as the messenger (implementing registerListener, unregisterListener and fireEvent)
  • Any component that wants to fire an event, or listen for an event will import that component to do so, firing events or registering listeners.

This would seem to imply that (at least a certain amount of) state within components is shared - looking like those defined with 'const'

Whatever the precise nature of the implementation, a pure Javascript solution is surely one that anyone involved in OO development will welcome.

I suspect that, in a later release, this will become a standard component.

Summary

Some people will be thinking "Man, glad I didn’t migrate from Classic / Visualforce to Lightning Experience / Components - maybe I’ll just wait a little longer for it all to settle down”.

You’re wrong - it won’t settle, it’ll continue to evolve and the technologies will be continually replaced by new ones. Eventually, the jump from what you have to where you need to get to will be so huge that you’ll find it incredibly hard. There’s a reason why Salesforce pushes out 3 releases a year, whether you want it or not, these technology jumps are just the same. The more you put it off, the more painful it’ll be.

The change from Lightning Components to Lightning Web Components is vast - a lot more than a single 3 letter word would have you suspect. The only real similarities between the two frameworks that I’ve seen up to now are:

  • Curlies are used to bind things
  • The Base Lightning Components are the same
  • You need to know Javascript

Other than that, they’re a world apart.

Also, I couldn’t find any real documentation - only examples - although those examples are a pretty comprehensive starting point.

Now, obviously it's early days - we're in pre-release right now, but what I've seen gives me great hope for the framework, it's a significant step forward and I can't wait to see what happens next. I wonder if a Unit Testing framework might follow (I can but hope)

You could wait, but hey, really, what are you waiting for? Come on, jump in. The change is exciting...