我目前正在尝试为棋盘游戏构建一个 Web 应用程序,并决定使用 Aurelia JavaScript 框架作为前端。我是 Aurelia 的新手,在尝试通过单击按钮创建自定义组件的新实例时遇到了麻烦。让我通过展示我想要实现的目标的示例来进一步解释。我尝试实现的游戏设置如下:
我整理了一个最基本的示例来进一步解释我遇到的问题。这更多是一个设计问题,源于我对 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/