Hitesh Misro
Hitesh Misro

Reputation: 3461

Template inside render function is not resolving - LitElement

I was trying to get a list through an ajax call but before it gets resolved, render() method is being called and the template fragment dependent on the Promise was not able to resolve and throws undefined.

Question: How to show a loader till I get the data from the Promise?

import {
    LitElement,
    html
} from 'lit-element';

class EmpComponent extends LitElement {
    constructor() {
        super();
        this.data = this.getEmpData();
    }

    getEmpData() {
        fetch('../../../emp-data.json')
            .then(
                function(response) {
                    if (response.status !== 200) {
                        console.log('Looks like there was a problem. Status Code: ' +
                            response.status);
                        return;
                    }

                    response.json().then(data => data);
                }
            )
            .catch(function(err) {
                console.log('Fetch Error :-S', err);
            });
    }

    render() {
        <div>
            ${this.data.map(emp => emp.active ? this.dataTemplate(emp) : '')} 
        </div>
    }
}

customElements.define('emp-component', EmpComponent);

Getting this error:

Error

Upvotes: 0

Views: 1142

Answers (3)

shabarinath
shabarinath

Reputation: 593

step 1: create a js file that return true or false (e.g. util.js)

export function when(expression, truVal, falseVal) {
    if (expression) {
        return truVal();
    }
    if (falseVal) {
        return falseVal();
    }
    return undefined;
}

step 2: import util in your js file

import {
    LitElement,
    html
} from 'lit-element';
import {
    when
} from 'util.js'

step 3: set a isLoading property in static get properties. So on inital load, we set onload to true in constructor

import {
    LitElement,
    html
} from 'lit-element';
import {
    when
} from 'util.js'

class EmpComponent extends LitElement {
    static get properties() {
        return {
            isLoading: {
                type: Boolean
            },

        }
    }
    constructor() {
        super();
        this.isLoading = true;
    }

step 4: once data is fetched, we are ready to render DOM. Then we can set isLoading to false and then render DOM using when

static get properties() {
    return {
        isLoading: {
            type: Boolean
        },
        canRender: {
            type: Boolean
        }
    }
}
constructor() {
    super();
    this.isLoading = true;
    this.canRender = false;
    this.data = this.getEmpData();
    this.isLoading = false;
    this.canRender = true;
}

render() {
    return html `
    ${when(this.isLoading,() => html`<p>Loading...</p>`)}
    ${when(this.canRender,() => html`<your-component></your-component>`)}
  `
}

This is an alternate solution for until. you can get more details from this blog sabarinath blog

Upvotes: 2

HerberthObregon
HerberthObregon

Reputation: 2111

The solution

I comment the parts where you should make changes. You don't need to do weird things with other imports

import { LitElement, html } from 'lit-element';
    
class EmpComponent extends LitElement
{
    constructor() {
        super();
        // you were returning a promise to an Array type...
        // this.data = this.getEmpData();
        // Correct
        this.data = [];
        this.getEmpData();
    }

    // ADD PROPS
    static get properties() {
        return {
            data: {type:Array}
        };
    }

    getEmpData() {
        fetch('../../../emp-data.json')
            .then(()=>(response) {
                    if (response.status !== 200) {
                        console.log('Looks like there was a problem. Status Code: ' +
                            response.status);
                        return;
                    }
                    // SET DATA ON RESOLVE
                    response.json().then(data => this.data = data);
                }
            )
            .catch(function(err) {
                console.log('Fetch Error :-S', err);
            });
    }

    render() {
        <div>
            $ {
                (this.data || []).map(emp => emp.active ? this.dataTemplate(emp) : '')
            } 
        </div>
    }
}

customElements.define('emp-component', EmpComponent);

Upvotes: 1

Umbo
Umbo

Reputation: 3142

You're not returning anything in getEmpData() so this.data is undefined, hence the error.

Keep in mind that if you add a return statement before the fetch() call this.data will then contain a Promise. The until directive can help in this case:

import {until} from 'lit-html/directives/until.js';

// ...

render() {
  return html`
    <div>
      ${until(
        this.data.then(data => data.map(emp => emp.active ? this.dataTemplate(emp) : ''),
        html`<p>Loading...</p>`,
      )} 
    </div>
  `;
}

Upvotes: 1

Related Questions