4

I have created a basic project and simply want to pull a list of greetings from Google Firestore instead of using the default static array of greetings.

My greeting.js file looks like this:

import { LightningElement, track, api, wire } from 'lwc';
import { getGreetings } from '../../../models/greeting.js';

const SPEED_CLASS_MAP = {
    slow: 'fade-slow',
    fast: 'fade-fast',
    medium: 'fade-medium'
};
const DEFAULT_SPEED = 'medium';

export default class Greeting extends LightningElement {
    @track animationSpeed = DEFAULT_SPEED;
    @track index = 0;
    @track isAnimating = true;

    @wire(getGreetings, {})
    greetings = [];

    @api
    set speed(value) {
        if (SPEED_CLASS_MAP[value]) {
            this.animationSpeed = value;
        } else {
            this.animationSpeed = DEFAULT_SPEED;
        }
        this.isAnimating = true;
    }

    // Return the internal speed property
    get speed() {
        return this.animationSpeed;
    }

    // Get the current greeting
    get greeting() {
        return this.greetings[this.index];
    }

    // Map slow, medium, fast to CSS Animations
    get animationClass() {
        if (this.isAnimating) {
            return SPEED_CLASS_MAP[this.speed];
        }
        return 'hide';
    }

    //Handle the animation ending, update to next hello
    handleAnimationEnd() {
        this.isAnimating = false;
        this.index = (this.index + 1) % this.greetings.length;

        setTimeout(() => this.updateGreeting(), 500);
    }

    // Update to the next greeting and start animating
    updateGreeting() {
        this.isAnimating = true;
    }
}

And my models/greeting.js file looks like so:

import { register, ValueChangedEvent } from '@lwc/wire-service';
import firebase from '../firebase.js';
const db = firebase.firestore();

export function getGreetings(config) {
    return getObservable(config)
        .map(makeReadOnlyMembrane)
        .toPromise();
}

register(getGreetings, function getGreetingsWireAdapterFactory(eventTarget) {
    let subscription;
    let config;
    let greetings;

    // Invoked when config is updated.
    eventTarget.addListener('config', newConfig => {
        // Capture config for use during subscription.
        config = newConfig;
    });

    // Invoked when component connected.
    eventTarget.addListener('connect', () => {
        // Subscribe to stream.
        subscription = getObservable(config)
            .map(makeReadOnlyMembrane)
            .subscribe({
                next: data => 
                    wiredEventTarget.dispatchEvent(
                        new ValueChangedEvent({ data, error: undefined })
                    ),
                error: error =>
                    wiredEventTarget.dispatchEvent(
                        new ValueChangedEvent({ data: undefined, error })
                    ),
            });


        // Get the data from Firestore:
        db.collection("greetings")
            .get()
            .then(function(querySnapshot) {    
                querySnapshot.forEach(function(doc) {
                    // doc.data() is never undefined for query doc snapshots
                    let data = doc.data();
                    if (data.greeting != undefined) {
                        greetings.push(data.greeting);
                    }
                });

                wiredEventTarget.dispatchEvent(
                    new ValueChangedEvent({ data: greetings, error: undefined })
                );
            })
            .catch(function(error) {
                console.log("Error getting documents: ", error);
            });
    });

    // Invoked when component disconnected.
    eventTarget.addListener('disconnect', () => {
        // Release all resources.
        subscription.unsubscribe();
    });
});

My firebase.js file looks like so:

import firebase from 'firebase/app';
import 'firebase/firestore';

const firebaseConfig = {
    apiKey: "<insert-api-key-here>",
    databaseURL: "https://<insert-project-id-here>.firebaseio.com",
    projectId: "<insert-project-id-here>",
    appId: "<insert-app-id-here>"
  };

firebase.initializeApp(firebaseConfig);

export default firebase;

But, the firestore call never fires.

I haven't been able to find any examples on calling external (asynchronous) APIs using @wire and I'm not fully grasping how it works anyway, so I'm not event sure if I've put the API call in the right place.

If I do the firestore call in connectedCallback() it works fine.

Has anyone had any experience with this? How do I do external Rest API calls using @wire decorator?

0

In the 'Recipes' section of the LWC documentation, there is an example titled "Call a REST service using the Fetch API." (link: https://recipes.lwc.dev/#misc)

Source code is here: https://github.com/trailheadapps/lwc-recipes-oss/tree/master/src/modules/recipe/miscRestApiCall

The example uses the Google Books search public API and doesn't require an API key or other config - so it may be too simplistic of an example. But it uses JavaScript promises and (a major bonus) does its work with very little code.

Happy hunting!

| improve this answer | |
  • 1
    Yes, I have tried that method. Unfortunately, that call is based off an action. I’m wanting to use the @wire decorator to keep the data in the component in sync with the API. – ejangi Jan 10 at 2:58
  • As usual, @sfdcfox has a helpful response. May want to check out this question: [How to setup Custom LWC Wire Service] (salesforce.stackexchange.com/questions/284380/…) – Talkin Data Jan 10 at 16:52
0

When fetch API fails (which can happen for a number of reasons), you might want to try with good old Apex, as your next port of call.

Apex API calls

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.