I am currently trying to build a web application for a board game and have decided to use the Aurelia JavaScript framework for the front-end. I am new to Aurelia and have run into trouble when trying to create a new instance of a custom component at the click of a button. Let me explain further by showing an example of what I am trying to accomplish. The setup for the game I am trying to implement looks as follows:
I have put together a bare-minimum example to further explain what I am having trouble with. It is more of a design problem that stems from me not being familiar with Aurelia than anything else. In this bare-minimum example I have the main app.js viewmodel and app.html view that Aurelia treats as the main viewmodel and view. They look like this:
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>
I then have a card component which very simply represents a playing card. Here is its viewmodel and 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>
Currently, I am able to dynamically generate new cards at the press of the button in the app view by instantiating a new Card object in the app viewmodel's button click event handler. The issue I have with this is that I do not believe I should have to instantiate the Card objects manually from the app viewmodel. It seems that there should be some way to tell Aurelia that it needs to create a new Card object, but I could not figure out what that way would be. So my question is this: Is there a better way to dynamically create custom components which does not require manually instantiating them as I have done?
Part of my reasoning for why this does not seem like the correct approach is because with this current setup, the constructor for a Card object is called twice when the constructor should only be called once. Also, if the Card class were to require dependency injected values, I would have to manually pass those into the new Card objects, and that does not feel right to me.
Thank you so much for your help!
Here's a link to the minimal working repo on GitHub