【问题标题】:Dynamically generate custom component in view with Aurelia使用 Aurelia 动态生成视图中的自定义组件
【发布时间】:2016-06-20 06:23:03
【问题描述】:

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

Game Setup Image

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

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>

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

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>

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

为什么这看起来不是正确的方法,我的部分原因是因为在当前设置下,Card 对象的构造函数被调用了两次,而构造函数只应该被调用一次。此外,如果 Card 类需要依赖注入值,我将不得不手动将这些值传递给新的 Card 对象,这对我来说并不合适。

非常感谢您的帮助!

这里是最小工作仓库on GitHub的链接

【问题讨论】:

  • 你应该尽量让你的问题保持小而集中,这样其他人就可以很容易地理解这个问题,并且可以为他们自己的问题重复使用答案。请花一些时间将您的问题减少到尽可能小的重复。
  • 好的,我将重写它以使其更简单,并制作一个新的最低限度的 Aurelia 项目并发布。我一直在思考我的应用程序上下文中的所有事情,但这是有道理的,谢谢!
  • 看起来像辉煌:)
  • 你是对的@MiroslavPopovic,这将是一个 Splendor 实现:)

标签: javascript mvvm typescript aurelia


【解决方案1】:

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

所以你可以这样使用它:

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

所以卡片视图模型仍然存在,您将生成逻辑移出并从卡片数组中传递卡片对象。这让事情变得更加干净和简单,您不必每次都实例化这些沉重的对象。

在您的 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 文件将保持不变。

【讨论】:

  • 非常感谢您的回答!当我第一次阅读它时,它似乎很有意义,但是当我去实现它时,我遇到了一些麻烦。我真的不明白我应该在哪里打电话给new Card()。你说把它移到一个不会多次实例化的类中,但它目前在 app.js 中,我认为它只实例化了一次。这是不正确的吗?如果您有时间为我提供更多示例代码,我将不胜感激!谢谢!
  • 不用担心 KevinM。昨晚我有点累,所以为令人困惑的答案道歉(当时这是有道理的)。我会更新我的答案,为您提供更多详细信息。
  • 这将非常有帮助,非常感谢!
  • @KevinM 请查看我对上述答案的最新编辑,
  • 这太完美了!感谢您的所有帮助,我真的很感激。
【解决方案2】:

另一种方法是将 Card 类标记为瞬态并使用 Lazy 解析器。请原谅 ES6。

App.js

import { Card } from './card';
import {Lazy,inject} from 'aurelia-framework';

@inject(Lazy.of(Card))
export class App {
    constructor(cardFactory){
        this.cardFactory = cardFactory;
    }
    cards = [];

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

Card.js

import {transient} from 'aurelia-framework';
@transient()
export class Card {
    //do stuff
}

【讨论】:

  • 感谢@JamesCarters 提供的信息!我将研究瞬态和惰性解析器。
猜你喜欢
  • 1970-01-01
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-18
  • 2012-04-03
  • 1970-01-01
相关资源
最近更新 更多