【问题标题】:Return asynchronous call through Web Components (MVC)通过 Web Components (MVC) 返回异步调用
【发布时间】:2020-11-09 23:29:06
【问题描述】:

我正在使用纯 javascript 和 Web 组件构建应用程序。我也想使用 MVC 模式,但现在我遇到了来自模型的异步调用问题。

我正在开发一个meal-list 组件。数据来自 API 格式的 JSON,格式如下:

[
   {
     id: 1,
     name: "Burger",
    },
]

我希望控制器从模型中获取数据并将其发送到视图。

meals.js(模型)

export default {

    get all() {
        const url = 'http://localhost:8080/meals';

        let speisekarte = [];
        fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            },
        }).then(res => {
            return res.json()
        }).then(data => {
            // This prints the result I want to use, but I can't return
            console.log(data);
            // This does not work
            speisekarte = data;
            // This also does not work
            return data;
        });
        // is undefined.
        return speisekarte;
    },
}

这就是我尝试从 API 获取数据的方式。

meal-list.component.js(控制器)

import Template from './meal-list.template.js'
import Meal from '../../../../data/meal.js'

export default class MealListComponent extends HTMLElement {

    connectedCallback() {
        this.attachShadow({mode: 'open'});
         // Should send the Data from the model to the View
        this.shadowRoot.innerHTML = Template.render(Meal.all);
    }
}

if (!customElements.get('mp-meal-list')) {
    customElements.define('mp-meal-list', MealListComponent);
}

meal-list.template.js(查看)

export default {
    render(meals) {
        return `${this.html(meals)}`;
    },

    html(meals) {
        let content = `<h1>Speisekarte</h1>
                       <div class="container">`;

        content += /* display the data from api with meals.forEach */
        return content + '</div>';
    },
 }

正如我在 cmets 中提到的,我在将异步数据从模型返回到视图时遇到了问题。当我尝试return data; 或尝试将数据保存到数组中时,它是未定义的。我也可以返回整个fetch() 方法,但这会返回一个promise,我认为控制器不应该处理promise。

我已经阅读了How do I return the response from an asynchronous call? 中的长线程,但我无法将其与我的案例联系起来。

【问题讨论】:

  • 如果你使用Events,你会在Fetch得到数据后调度一个Event

标签: javascript asynchronous web-component fetch-api


【解决方案1】:

感谢 lotype 和 Danny '365CSI' Engelman,我为我的项目找到了完美的解决方案。我用自定义事件和 EventBus 解决了这个问题:

meal.js(模型)

get meals() {
    const url = 'http://localhost:8080/meals';

    return fetch(url, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json'
        },
    }).then(res => {
        return res.json()
    }).then(data => {
        let ce = new CustomEvent(this.ESSEN_CHANGE_EVENT, {
            detail: {
                action: this.ESSEN_LOAD_ACTION,
                meals: data,
            }
        });
        EventBus.dispatchEvent(ce);
    });
},

EventBus.js(来自书籍:Web Components in Action)

export default {
    /**
     * add event listener
     * @param type
     * @param cb
     * @returns {{type: *, callback: *}}
     */
    addEventListener(type, cb) {
        if (!this._listeners) {
            this._listeners = [];
        }

        let listener = {type: type, callback: cb};
        this._listeners.push(listener);
        return listener;
    },

    /**
     * trigger event
     * @param ce
     */
    dispatchEvent(ce) {
        this._listeners.forEach(function (l) {
            if (ce.type === l.type) {
                l.callback.apply(this, [ce]);
            }
        });
    }
}

现在,当数据准备好时,会向事件总线发送信号。 meal-list-component 正在等待事件然后获取数据:

export default class MealListComponent extends HTMLElement {

    connectedCallback() {
        this.attachShadow({mode: 'open'});
        this.shadowRoot.innerHTML = Template.render();
        this.dom = Template.mapDOM(this.shadowRoot);

        // Load Speisekarte on init
        this.dom.meals.innerHTML = Template.renderMeals(MealData.all);

        // Custom Eventlistener - always triggers when essen gets added, deleted, updated etc.
        EventBus.addEventListener(EssenData.ESSEN_CHANGE_EVENT, e => {
            this.onMealChange(e);
        });
    }

    onMealChange(e) {
        switch (e.detail.action) {
            case EssenData.ESSEN_LOAD_ACTION:
                this.dom.meals.innerHTML = Template.renderMEals(e.detail.meals);
                break;
        }
    }
}

【讨论】:

  • 干得好!小改动:因为 attachShadow 设置并返回 shadowRoot。你可以写:this.attachShadow({mode: 'open'}).innerHTML = Template.render();
【解决方案2】:

由于您将speisekarte 声明为一个数组,我希望它总是作为一个空数组返回。当 fetch 执行并履行承诺时,在上述实现中总是为时已晚。

您必须等待获取结果,您可以考虑多种选择:

  • 为获取结果提供回调
  • 或通过事件调度和监听器通知您的应用程序您的数据已加载,因此它可以开始渲染

您的链接已经对主题回调和异步/等待有一个很好的答案,我不能比那里解释的更好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 2014-10-29
    • 2016-01-16
    相关资源
    最近更新 更多