javascript - 使用 Aurelia 动态生成 View 中的自定义组件

标签 javascript mvvm typescript aurelia

我目前正在尝试为棋盘游戏构建一个 Web 应用程序,并决定使用 Aurelia JavaScript 框架作为前端。我是 Aurelia 的新手,在尝试通过单击按钮创建自定义组件的新实例时遇到了麻烦。让我通过展示我想要实现的目标的示例来进一步解释。我尝试实现的游戏设置如下:

Game Setup Image

我整理了一个最基本的示例来进一步解释我遇到的问题。这更多是一个设计问题,源于我对 Aurelia 不熟悉。在这个最基本的示例中,我有主 app.js View 模型和 app.html View ,Aurelia 将它们视为主视图模型和 View 。它们看起来像这样:

app.js

import { Card } from './card';

export class App {
    cards = [];

    newCard() {
        this.cards.push(new Card());
    }
}

app.html

<template>
    <require from="./card"></require>

    <button click.delegate="newCard()">New Card</button>

    <div>
        <li repeat.for="card of cards">
            <compose view-model="card"></compose>
        </li>
    </div>
</template>

然后我有一个卡片组件,它非常简单地代表一张扑克牌。这是它的 View 模型和 View :

card.js

export class Card {
    cardValues = ['2','3','4','5','6','7','8','9','10',
        'J','Q','K','A'];
    cardSuits = ['Diamonds', 'Clubs', 'Hearts', 'Spades'];

    value;
    suit;

    activate() {
        this.value = this.pickRandomItem(this.cardValues);
        this.suit = this.pickRandomItem(this.cardSuits);
    }

    pickRandomItem(data) {
        let index = Math.floor(Math.random() * (data.length -1));
        return data[index];
    }
}

card.html

<template>
    <div style="border: 2px solid black;
                display: inline-block;
                margin-top: 10px;">
        <h3>Value: ${value}</h3>
        <h4>Suit: ${suit}</h4>
    </div>
</template>

目前,我可以通过在应用程序 View 模型的按钮单击事件处理程序中实例化新的 Card 对象,在按下应用程序 View 中的按钮时动态生成新卡。我遇到的问题是,我不认为我应该从应用程序 View 模型手动实例化 Card 对象。似乎应该有某种方法告诉 Aurelia 它需要创建一个新的 Card 对象,但我不知道那种方法是什么。所以我的问题是:是否有更好的方法来动态创建自定义组件,而不需要像我那样手动实例化它们?

我之所以认为这似乎不是正确的方法,部分原因是因为在当前设置下,Card 对象的构造函数会被调用两次,而构造函数应该只被调用一次。另外,如果 Card 类需要依赖注入(inject)值,我就必须手动将这些值传递到新的 Card 对象中,这对我来说并不合适。

非常感谢您的帮助!

这是最小工作存储库的链接 on GitHub

最佳答案

我认为该方法可能在于使用 compose 元素上的 model 属性。 model 属性允许您传递要在 View 模型内部使用的值对象,这些值可在 activate 方法中作为第一个参数使用。

所以你可以像这样使用它:

<li repeat.for="cardObj of cards"> <compose view-model="card" model.bind="cardObj"></compose> </li>

因此,卡片 View 模型仍然存在,您移出生成逻辑,并且从卡片数组中传递卡片对象。这使得事情变得更加干净和简单,您不必每次都实例化这些重对象。

在 app.js 文件内,您将创建一个新对象一次,并调用构造函数,因为这就是 new做。然后,一旦 compose 元素调用它,它就会再次实例化它。您实例化该对象两次。

而不是 new Card()您要做的就是将一个简单的对象插入您的卡片数组中。将卡片生成逻辑移出到不会多次实例化的类中。


编辑

经过一些额外的反馈后,我举出了一个示例来说明如何将它们组合在一起。如您所见,我们已将卡片创建逻辑移至函数中。我们调用这个函数来生成一个包含两个属性的对象:suit 和 value。然后我们将其作为数据传递给 compose 元素,然后在内部使用它。

app.js

export class App {
    cards = [];

    newCard() {
        this.cards.push(generateCard());
    }
}

function generateCard() {
    let cardValues = ['2','3','4','5','6','7','8','9','10',
        'J','Q','K','A'];

    let cardSuits = ['Diamonds', 'Clubs', 'Hearts', 'Spades'];

    function pickRandomItem(arr) {
        let index = Math.floor(Math.random() * (arr.length -1));
        return arr[index];
    }

    return {
        suit: pickRandomItem(cardSuits),
        value: pickRandomItem(cardValues)
    };
}

app.html

<template>
    <button click.delegate="newCard()">New Card</button>

    <div>
        <li repeat.for="cardObj of cards">
            <!-- cardObj will be our object {suit: 'suit', value: 'value'} -->
            <compose view-model="card" model.bind="cardObj"></compose>
        </li>
    </div>
</template>

card.js

export class Card {
    suit;
    value;

    activate(model) {
        if (model) {
            this.suit = model.suit;
            this.value = model.value;
        }
    }
}

您的 card.html 文件将保持不变。

关于javascript - 使用 Aurelia 动态生成 View 中的自定义组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35831250/

相关文章:

javascript - Web Sockets DOM 对象检测

javascript - 为什么 browser.wait 会导致 Protractor 无限期地等待异步任务

javascript - 如何在 javascript 中每 5 分钟在数组中调用一个函数

javascript - 如何在发送响应 Node js (Sails) 之前设置 cookie

MvvmCross:如何防止转换 ViewModel?

typescript - 如何扩展@types/express 请求接口(interface)?

silverlight - Xaml 找不到绑定(bind)

c# - MVVM - 在 ViewModel 中使用实体

javascript - d3Service/d3-ng2-service TypeScript TS2346 提供的参数与签名不匹配

javascript - 削减 JSON 对象以声明方式发送到 Typescript 中的客户端?