# Tutorial 5

We're going to keep making the tutorials smaller, letting them
transition to being more like worked examples. Why? Because for
most students, it'll be more productive for you to work on your
projects.

The challenge for this tutorial is to build some controls, using
Vue or React, that will let you set up a Conway's Game of Life game.

## Clone the code

First, get the code and put it somewhere you can load it in your browser. 

To get the code, open a terminal, `cd` into the public directory of whichever webserver you are using, and:

```sh
git clone https://gitlab.une.edu.au/cosc360in2018/tutorial-conway-life-t5.git
```

This will clone this code using the git version control system. That will, amongst other things, let you switch between different branches (eg, the solution and the start of the exercise)

## Check it's working

Open `index.html` in the browser, and you should find yourself faced with Conway's Game of Life from Tutorial 4.

## Install loaders for Vue or React

The repository contains all the code from the previous tutorial, but not a copy of `node_modules` which would be too large. Let's make NPM go and get all the modules we've already declared in `packages.json`:

```sh
npm install
```

And before we go any further, let's put webpack into development mode. In `webpack.config.js`, at the top of `module.exports`, set:

```js
  mode: 'development',
```

If you're using Vue, install the modules for vue:

```sh
npm install --save-dev vue vue-class-component vue-property-decorator
```

You'll also need to alter `webpack.config.js` to load the version of Vue that includes the template compiler. Change the `resolve` block to:

```js
  resolve: {
    extensions: ['.ts', '.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
```

If you're using React, though, you'll need to install the modules for react:

```sh
npm install --save react react-dom @types/react @types/react-dom
```

and edit `tsconfig.json` to tell TypeScript to target React as the Virtual DOM for `.tsx` files

```json
    {
        "compilerOptions": {
            "outDir": "./built/",
            "sourceMap": true,
            "strict": true,
            "noImplicitReturns": true,
            "module": "es2015",
            "moduleResolution": "node",
            "target": "es5",
            "experimentalDecorators": true,
            "jsx": "react"
        },
        "include": [
            "./src/**/*"
        ]
    }
```

### Now the challenge

It might be useful to get ready for development by telling npm to watch our project for changes, and rebuild it automatically:

    ```sh
    npm run build -- --watch
    ```

The challenge:

Using *either* React or Vue, create a small block that will let us set up the Game of Life game. We're going to keep the game itself rendered through d3, showing how they exist on the page, but this should have:

* Fields for how many columns and rows you want
* A button that when clicked will create the game (and trigger d3 to rerender it)
* A field for how long (in ms) each tick should be -- default it to 500
* A button that will toggle whether the tick-timer is started or not. This button should only be enabled when the game has been created.

For this exercise, we're not going to put the d3-rendered game inside the element controlled by Vue or React -- we'll just have them co-exist in different elements on the same page.

### How to go about this

As is typical, I'd recommend you start just by working out the data structure for what you want to render. Looking at the requirements above, something like this seems appropriate:

```ts
export interface GameConfig {
    w:number
    h:number
    started:boolean
    created:boolean
    period:number
}
```

The next task I would suggest is just creating this object and rendering it to a little UI using your chosen framework. That'll let you fiddle around with the layout.

Then, wire up the functionality. Note that you'll probably need to change `life`. At the moment, it's a constant in the render file. You will probably want it to be a variable that can be set by calling a function.

## Solving this with Vue

A worked solution can be found on the `vue-solution` branch of this repository. To check out the code, you could run `git checkout vue-solution`. But let's talk you through it.

### Getting Vue in there

First, let's get Vue rendering something, even if just Hello World. 

In index.html, create a div that Vue can render to. Let's put this above the svg for the game:

```html
<div id="app"></div>
```

And inside index.ts, let's import Vue and render something to that div

```ts
import Vue from "vue";
```

```ts
let v = new Vue({
    el: "#app",
    template: `
      <div>
        Hello {{ name }}
      </div>
    `,
    data: {
        name: "Vue"
    }
})
```

Reload, and check we get "Hello Vue" appearing. If so, good -- Vue is working, can compile templates, and we can progress.

### Defining a ConfigView

Let's create a new TypeScript file `gameConfig.ts` to define a component in.

First, let's bring our definition of a game config here:

```ts
interface GameConfig {
    w:number 
    h:number 
    started:boolean 
    created:boolean
    period:number 
}
```

Next, we'll import Vue and Component:

```ts
import Vue from "vue"
import Component from 'vue-class-component'
```

And then let's start defining the component. This is going to take a GameConfig as a prop, and is going to alter its fields

```ts
@Component({
    props: {
        config: Object as () => GameConfig
    },
    template: `
      <div>
        This will render a game config
      </div>
    `
})
class ConfigView extends Vue {

    config!: GameConfig

    constructor() {
        super()
        console.log("a new config view was placed")
    }

}
```

I've included a constructor this time, so we can see in the console that we've tried to add the component.

Now, we're going to need to export the definitions of `GameConfig` and `ConfigView`:

```ts
export { 
    GameConfig as GameConfig,
    ConfigView as ConfigView
}
```

In `index.ts`, let's import our definitions:

```ts
import { ConfigView, GameConfig } from "./gameConfig"
```

Now we should be able to create a config (notice we're using TypeScript's structural typing here):

```ts
let config:GameConfig = {
    w: 40,
    h: 20,
    started: false,
    created: false,
    period: 500
}
```

And we should be able to alter our invocation of Vue to use our new component (don't forget to include it in the components section at the bottom):

```ts
let v = new Vue({
    el: "#app",
    template: `
      <div>
        <ConfigView :config="config"></ConfigView>
      </div>
    `,
    data: {
        config: config
    },
    components: {
        ConfigView
    }
})
```

### Making ConfigView alter the config

Next, let's go back to `gameConfig.ts` and make our ConfigView render something more interesting that actually alters the fields.

The full HTML of the component's template in the solution is quite long, but that's because I've used Bootstrap's components to make it look pretty &mdash; the Vue aspects are quite small in there. So rather than talk you through all of my solution HTML, let's just do a short and not-so-pretty version here.

We're going to need input elements for the width and height of the grid.

The `config` prop we've passed in is read-only. But it's a *read-only reference* to a *mutable object*. So we're going to bind some input controls to its fields. 

First, in the component's template, we need inputs for the width and height:

```ts
template: `
      <div>
        Columns:
        <input type="number" v-model:value="config.w" />

        Rows:
        <input type="number" v-model:value="config.h" />
      </div>
    `,
```

We've used `v-model` rather than `v-bind` to give us a two-way binding between the value of the input field and the value of `config.w` and `config.h`. Remember, we can't alter `config` (the reference) but we can alter its fields.

If you reload the page, you should now have some fields above the grid, and they should appear initialised to 40 and 20.

Next, let's wire up a Create button. Inside the component's template, put:

```ts
    <button v-bind:disabled="config.created" v-on:click="create">Create</button>
```

This bind's the button's "disabled" attribute so that it will become disabled when `config.created` becomes true. And it sets the button so that when it is clicked it will call `create()` in the component's class. We'd better go and make the create function. Remember, it needs to be a method on the ConfigView class:

```ts
    create() {
        console.log("Create was pressed")
        this.config.created = true
    }
```

Reload and check that logs to the browser console when you hit the button (and the button should also become disabled).


### Altering render

We need to let our component actually configure our `Life` instance.

First, in `render.ts`, let's turn `life` into a variable, and also add a create function that accepts a `GameConfig` and a function for getting the value of life.

```ts
import { GameConfig } from "./gameConfig"
```

```ts
let life:Life = new Life(0,0)

function create(config:GameConfig) {
    console.log(config.w)
    life = new Life(config.w, config.h)
    render()
}

function getLife() {
    return life
}
```

Next, make sure those are exported

```ts
export {
    getLife as getLife,
    create as create,
    render as render
}
```

### Making ConfigView set up the game

Let's go back to `gameConfig.ts` and import those definitions

```ts
import { create, render, getLife } from './render'
```

We can now alter the definition of our `create()` method on `ConfigView` to actually call `create` in `render.ts`:

```ts
    create() {
        create(this.config) // note this calls create in render.ts that we imported
        render()
        this.config.created = true
    }
```

Reload, and see if it works.

### Setting up the timer

JavaScript timers give you a timer id that is a number. So in our `ConfigView` class let's add a field that can remember it. This is not a prop, just an ordinary field on the class. And the `?` means it might be null.

```ts
    intervalId?: number
```

Let's now create a method on the class that can start and stop an actual timer using this field. Notice that we're using the `getLife` and `render` functions from `render.ts` that we imported.

```ts
    toggleTimer() {
        if (this.config.started) {
            clearInterval(this.intervalId)
            this.config.started = false
        } else {
            this.intervalId = setInterval(() => {
                let l = getLife()
                if (l) l.stepGame()
                render()
            }, this.config.period)
            this.config.started = true
        }
    }
```

And now all we need do is add some HTML to the template that'll reference the remaining fields of our `GameConfig` and will call this method:

```ts
Period:
<input type="number" v-model:value="config.period" />
<button v-bind:disabled="!config.created" v-on:click="toggleTimer">
    {{ config.started ? "Stop" : "Start" }}
</button>
```

Notice this button is bound so it's disabled until we've created the game. It's onClick is bound to the `toggleTimer` method, and the text changes from Start to Stop depending on whether the timer is already started.


## Solving this tutorial with React

There's a worked solution to this tutorial using React.js on the `react-solution` branch. But let's talk you through it.

### Some config changes

First, there's some config changes we'll make to make things easier. By default, React won't give you nice error messages -- it left them out of the library to save space. So make the HTML load the development scripts from our node_modules directory:

```html
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
```

And tell webpack not to put React itself into bundle.js (because we're loading its development scripts separately). In webpack.config.js, inside `module.exports`:

```js
externals: {
  "react": "React",
  "react-dom": "ReactDOM"
}
```

We're also going to want to use TSX, so let's update compilerOptions in `tsconfig.json`:

```js
    "jsx": "react"
```

And let's also rename `index.ts` to `index.tsx`, and then update `webpack.congfig.js` to tell it about the new entry point:

```js
  entry: './src/index.tsx',
```

### Getting React on-screen

Let's get React rendering something, even if just Hello World. 

In index.html, create a div that Vue can render to. Let's put this above the svg for the game:

```html
<div id="app"></div>
```

And inside index.tsx, let's import React and ReactDOM, and something to that div

```ts
import * as React from "react";
import * as ReactDOM from "react-dom";
```

and now for our first bit of TSX:

```ts
ReactDOM.render(
    <div>Hello React</div>,
    document.getElementById("app")
);
```

Set webpack watching the files:

```sh
npm run build -- --watch
```

Reload index.html in the browser, and check we get "Hello Vue" appearing. If so, good -- React is working and we can progress.

### Creating a ConfigView component

Let's create a file called `gameConfig.tsx'. 

First, let's import react:

```tsx
import * as React from "react"
import * as ReactDOM from "react-dom"
```

Then let's define two types -- one for the config's definable properties, and another that has some additional state:

```tsx
export interface ConfigProps {
    w:number
    h:number
}
```

```tsx
export interface ConfigState extends ConfigProps {
    created:boolean
    period:number
    intervalId?: number
}
```

intervalId is optional -- hence the `?`

And then let's define a React component:

```tsx
export class ConfigView extends React.Component<ConfigProps, ConfigState> {
    render() {
        return <div>This will render some game config</div>
    }

    constructor(props:ConfigProps) {
        super(props)
        this.state = {
            ...props,
            created: false,
            period: 500
        }
    }

}
```

Notice that in the angle brackets, we've said this component's properties are defined by the ConfigProps interface -- so, it has properties for `w` and`h`.
And we've said its state is defined by the `ConfigState` interface.

In the constructor, we've also taken the component's properties, and augmented them with the missing compulsory variables. `...props` is the "spread" operator that sets all the name-value pairs from
`props` on the new object we're creating.

Let's go back to `index.tsx` and use our component.

First, we need to import `ConfigView`:

```tsx
import { ConfigView } from "./gameConfig"
```

and then let's alter our React call to use it:

```tsx
ReactDOM.render(
    <ConfigView w={40} h={20}></ConfigView>,
    document.querySelector("#app")
);
```

Notice that we've been able to set the `w` and `h` properties as attributes on the component. This sets them in the `props` object.

Reload and see if the message is in the UI.

### Render some controls

It's time to get our ConfigView to render some real controls. Let's alter `gameConfig.tsx`.

```tsx
    render() {
        return <div>
          Cols: <input type="number" value={this.state.w} onChange={(e) => this.setW(+e.target.value)} />
          Rows: <input type="number" value={this.state.h} onChange={(e) => this.setH(+e.target.value)} />
          <button onClick={(e) => this.create()}>Create</button>  
        </div>
    }
```

In this, we've bound the input fields to render the `w` and `h` fields from the current state.

We've then needed to bind an `onChange` handler for each field. This takes a lambda function.
The parameter `(e)` is the event that has happened. And we're getting `e.target.value` from it to get the number out
of the input field. This comes out as a string, even though the input field is for numbers. So in order to convert it
to a number, we use `+e.target.value`. This then needs to get passed to `this.setW` and `this.setH` -- methods on our class. So we'd better write those!

Likewise, we've boud the button's click handler to `this.create()`.

Let's write `setW` and `setH`, which are methods on the `ConfigView` class:

```tsx
    setW(w:number) {
        this.setState((state:ConfigState) => {
            state.w = w
            return state
        })
    }

    setH(h:number) {
        this.setState((state:ConfigState) => {
            state.h = h
            return state
        })
    }
```

In both cases, we call `setState`, which is a method from the React API (remember, `ConfigView` inherits from `React.Component`). `setState` gives you the current state, and asks you to return the new state. It will then automatically trigger re-rendering the component.

Now let's write `create` method, and just get it to log to the console:

```tsx
    create() {
        console.log("I was clicked")
    }
```

Reload the page, and check the code so far works

### Making it create the Life game

Let's alter `render.ts`.

First, import the ConfigProps interface

```ts
import { ConfigProps } from "./gameConfig"
```

and now let's make `life` a variable instead of a constant. We'll initialise it to a zero-sized game so it won't be null but won't be visible on the screen. And we'll define a `create` method that takes ConfigProps.

```ts
let life = new Life(0, 0)
```

```ts
function create(config:ConfigProps) {
    life = new Life(config.w, config.h)
}
```

We'd best make sure those are exported:

```ts
export {
    life as life,
    render as render,
    create as create
}
```

Now let's go back to `gameConfig.tsx` and make it set up the game by altering what happens when we click the button

First, let's import the functions from render.ts

```tsx
import { life, create, render} from "./render"
```

Then let's alter the `create` method on `ConfigView`:

```tsx
    create() {
        create(this.state) // note this calls the function imported from render.ts
        render()  // note this calls the function imported from render.ts
    }
```

Reload, and check it works


### Setting up the timer

The last part we'll do is setting up the timer. In `gameConfig.tsx`, let's alter the render() method for our component:

```tsx
    render() {
        return <div>
          Cols: <input type="number" value={this.state.w} onChange={(e) => this.setW(+e.target.value)} /> 
          Rows: <input type="number" value={this.state.h} onChange={(e) => this.setH(+e.target.value)} />   
          <button onClick={(e) => this.create()}>Create</button>  

          Period: <input type="number" value={this.state.period} onChange={(e) => this.setPeriod(+e.target.value)} />
          <button onClick={(e) => this.toggleTimer()}>{ this.state.intervalId ? "Stop" : "Start" }</button>  
        </div>
```

We've added an input field, very similar to the ones for `w` and `h` but operating on `period`.

We've also added a button that will call `toggleTimer`, which we need to define. Its text is set by a ternary expression on whether `intervalId` in the component's state is defined.

`toggleTimer` can work as another call to `setState`. Only this time, the call is also going to either start or cancel a JavaScript timer. We need to use `window.setInterval` because in Node setInterval has a different return type than in the browser -- calling `window.setInterval` makes sure TypeScript uses the browser's version.

```ts
    toggleTimer() {
        this.setState((state:ConfigState) => {

            if (state.intervalId) {
                clearInterval(state.intervalId)
                state.intervalId = undefined
                return state
            } else {
                state.intervalId = window.setInterval(() => {
                    life.stepGame()
                    render()
                }, this.state.period)
                return state
            }

        })
    }
```

Reload, and see if it all works!