使用Aurelia动态生成自定义组件

时间:2016-03-06 19:10:01

标签: javascript mvvm typescript aurelia

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

Game Setup Image

我已经汇总了一个极小的例子,以进一步解释我遇到的问题。更多的是设计问题源于我不熟悉Aurelia而不是其他任何东西。在这个极少数示例中,我有主要的app.js viewmodel和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>

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

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>

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

我的部分原因是为什么这看起来不是正确的方法是因为使用这个当前设置,当构造函数只应被调用一次时,Card对象的构造函数被调用两次。另外,如果Card类要求依赖注入值,我必须手动将它们传递给新的Card对象,这对我来说不合适。

非常感谢你的帮助!

以下是最小工作回购on GitHub

的链接

2 个答案:

答案 0 :(得分:4)

我认为方法可能在于在compose元素上使用model属性。 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 文件将保持不变。

答案 1 :(得分:0)

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

<强> App.js

<?php
session_start();
if($_SERVER["REQUEST_METHOD"] == "POST")
{
require_once('conn/conn.php');
$user=$_POST['user'];
$pass=md5($_POST['pass']);
$addonq = "WHERE username='".$user."' AND password='".$pass."'"; 
$user = $koneksi->prepare("SELECT * FROM user ".$addonq."");
$user->execute();
$row = $user->fetch(PDO::FETCH_ASSOC);

if(empty($row['username'])){

echo "Your Login Name or Password is invalid";

}else {
$_SESSION['login_user'] = $user;

header("location: index.php");
}
}
?>
<form action="" method="post">
<label>UserName :</label>
<input type="text" name="user"/><br />
<label>Password :</label>
<input type="password" name="pass"/><br/>
<input type="submit" value=" Submit "/><br />
</form>

<强> Card.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());
   }
}