Skip to content

Commit caf8165

Browse files
committed
[WiP] link back to Todo List from elmish.md & add update section for #44
1 parent 39779b2 commit caf8165

File tree

5 files changed

+135
-26
lines changed

5 files changed

+135
-26
lines changed

Diff for: elmish.md

+2
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,8 @@ refer to the completed code:
17971797
That's it for now! `Elm`(_ish_) is "ready" to be _used_
17981798
for our TodoMVC App!
17991799

1800+
# [< _Back_ To Todo List App!](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/master/todo-list.md#how)
1801+
18001802
<br />
18011803

18021804
### Why _Not_ use HTML5 `<template>` ??

Diff for: examples/todo-list/elmish.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ function empty(node) {
1818

1919
/**
2020
* `mount` mounts the app in the "root" DOM Element.
21-
* @param {Object} model store of the application's state.
22-
* @param {Function} update how the application state is updated ("controller")
23-
* @param {Function} view function that renders HTML/DOM elements with model.
24-
* @param {String} root_element_id root DOM element in which the app is mounted
21+
* @param {Object} model store of the application's state.
22+
* @param {Function} update how the application state is updated ("controller")
23+
* @param {Function} view function that renders HTML/DOM elements with model.
24+
* @param {String} root_element_id root DOM element in which the app is mounted
2525
*/
2626
function mount(model, update, view, root_element_id) {
2727
var root = document.getElementById(root_element_id); // root DOM element
2828
function signal(action) { // signal function takes action
2929
return function callback() { // and returns callback
30-
var updatedModel = update(model, action); // update model for the action
30+
var updatedModel = update(action, model); // update model for the action
3131
localStorage.setItem('elmish_store', JSON.stringify(updatedModel));
3232
empty(root); // clear root el before rerender
3333
view(signal, updatedModel, root); // subsequent re-rendering

Diff for: examples/todo-list/todo-app.js

+23-18
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,23 @@ if (typeof require !== 'undefined' ) { // require elm(ish) creating local copy
66
route, section, span, strong, text, ul } = require('./elmish.js');
77
} // in the browser elm(ish) functions are automatically be available
88

9-
// Define the Component's Actions:
10-
var Inc = 'inc'; // increment the counter
11-
var Dec = 'dec'; // decrement the counter
12-
var Res = 'reset'; // reset counter: git.io/v9KJk
9+
var initial_model = {
10+
todos: [],
11+
hash: "#/"
12+
}
1313

1414
function update(model, action) { // Update function takes the current state
15-
var parts = action ? action.split('-') : []; // e.g: inc-0 where 0 is the counter "id"
16-
var act = parts[0];
17-
var index = parts[1] || 0;
1815
var new_model = JSON.parse(JSON.stringify(model)) // "clone" the model
19-
switch(act) { // and an action (String) runs a switch
20-
case Inc:
21-
new_model.counters[index] = model.counters[index] + 1;
22-
break;
23-
case Dec:
24-
new_model.counters[index] = model.counters[index] - 1;
25-
break;
26-
case Res: // use ES6 Array.fill to create a new array with values set to 0:
27-
new_model.counters[index] = 0;
28-
break;
16+
switch(action) { // and an action (String) runs a switch
17+
// case 'CREATE':
18+
// new_model.counters[index] = model.counters[index] + 1;
19+
// break;
20+
// case Dec:
21+
// new_model.counters[index] = model.counters[index] - 1;
22+
// break;
23+
// case Res: // use ES6 Array.fill to create a new array with values set to 0:
24+
// new_model.counters[index] = 0;
25+
// break;
2926
default: return model; // if action not defined, return curent state.
3027
}
3128
return new_model;
@@ -43,7 +40,15 @@ function view(signal, model, root) {
4340
}).forEach(function (el) { root.appendChild(el) }); // forEach is ES5 so IE9+
4441
}
4542

46-
43+
/* module.exports is needed to run the functions using Node.js for testing! */
44+
/* istanbul ignore next */
45+
if (typeof module !== 'undefined' && module.exports) {
46+
module.exports = {
47+
model: initial_model,
48+
update: update,
49+
view: view
50+
}
51+
}
4752

4853

4954
})(); // https://en.wikipedia.org/wiki/Immediately-invoked_function_expression

Diff for: test/todo-app.test.js

+7
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,10 @@ const html = fs.readFileSync(path.resolve(__dirname,
66
require('jsdom-global')(html); // https://github.com/rstacruz/jsdom-global
77
const app = require('../examples/todo-list/todo-app.js'); // functions to test
88
const id = 'test-app'; // all tests use 'test-app' as root element
9+
10+
test('todo `model` (Object) has desired keys', function (t) {
11+
const keys = Object.keys(app.model);
12+
t.deepEqual(keys, ['todos', 'hash'], "`todos` and `hash` keys are present.");
13+
t.true(Array.isArray(app.model.todos), "model.todos is an Array")
14+
t.end();
15+
});

Diff for: todo-list.md

+98-3
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,9 @@ you should see no output.
176176

177177

178178

179-
180179
### `model`
181180

182-
The `model` for our Todo List App is remarkably simple.
181+
The `model` for our Todo List App is **_boringly_ simple**.
183182
All we need is an `Object` containing two keys `todos` and `hash`:
184183

185184
```js
@@ -192,7 +191,11 @@ All we need is an `Object` containing two keys `todos` and `hash`:
192191
hash: '#/active' // the "route" to display
193192
}
194193
```
195-
194+
`todos` is an `Array` of `Objects` and each Todo (Array) item
195+
has 3 keys:
196+
+ `id`: the index in the list.
197+
+ `title`: the title/description of the todo item.
198+
+ `done`: a `boolean` indicating if the item is complete or still "todo".
196199

197200

198201
#### What about `metadata` ?
@@ -217,10 +220,102 @@ this will only take a millisecond to compute,
217220
will not "slow down" the app or affect UX.
218221

219222

223+
#### `model` _Test_
224+
225+
Given that the `model` is "just data"
226+
(
227+
_it has **no** "**methods**" because `Elm`(ish) is_
228+
["***Functional***"](https://en.wikipedia.org/wiki/Functional_programming)
229+
_**not**_
230+
["***Object Oriented***"](https://en.wikipedia.org/wiki/Object-oriented_programming)
231+
),
232+
there is no _functionality_ to test.
233+
We are merely going to test for the "shape" of the data.
234+
235+
In the `test/todo-app.test.js` file, append following test code:
220236

237+
```js
238+
test('todo `model` (Object) has desired keys', function (t) {
239+
const keys = Object.keys(app.model);
240+
t.deepEqual(keys, ['todos', 'hash'], "`todos` and `hash` keys are present.");
241+
t.true(Array.isArray(app.model.todos), "model.todos is an Array")
242+
t.end();
243+
});
244+
```
245+
246+
If you _run_ this test in your terminal:
247+
```sh
248+
node test/todo-app.test.js
249+
```
250+
You should see _both_ assertions _fail_:
251+
![model-tests-failing](https://user-images.githubusercontent.com/194400/43508841-e8473e90-9568-11e8-85fd-6e0e30f244cb.png)
252+
253+
254+
255+
#### `model` _Implementation_
256+
257+
Write the _minimum_ code required to _pass_ this test in `todo-app.js`.
258+
e.g:
259+
260+
```js
261+
/**
262+
* initial_model is a simple JavaScript Object with two keys and no methods.
263+
* it is used both as the "initial" model when mounting the Todo List App
264+
* and as the "reset" state when all todos are deleted at once.
265+
*/
266+
var initial_model = {
267+
todos: [],me
268+
hash: "#/"
269+
}
270+
271+
/* module.exports is needed to run the functions using Node.js for testing! */
272+
/* istanbul ignore next */
273+
if (typeof module !== 'undefined' && module.exports) {
274+
module.exports = {
275+
model: initial_model
276+
}
277+
}
278+
```
279+
280+
Once you save the `todo-app.js` file and re-run the tests.
281+
```sh
282+
node test/todo-app.test.js
283+
```
284+
You _should_ expect to see both assertions _passing_:
285+
![model-tests-passing](https://user-images.githubusercontent.com/194400/43508894-0df475cc-9569-11e8-8665-14320138ba79.png)
286+
287+
We're off to a _great_ start! Let's tackle some actual _functionality_ next!
288+
289+
<br />
290+
291+
### `update`
292+
293+
The `update` function is the
294+
["brain"](https://www.youtube.com/results?search_query=Pinky+and+The+Brain)
295+
of the App.
296+
297+
#### `update` JSDOC
298+
299+
The **`JSDOC`** for our `update` function is:
300+
```js
301+
/**
302+
* `update` transforms the `model` based on the `action`.
303+
* @param {String} action - the desired action to perform on the model.
304+
* @param {Object} model - the App's data ("state").
305+
* @return {Object} updated_model - the transformed model.
306+
*/
307+
308+
```
221309

310+
#### `update` Test
222311

312+
As with the `update` in our `counter` example
313+
the function body is a `switch` statement
314+
that "decides" how to handle a request based on the `action` (_also known as the "message"_).
223315

316+
Given that we _know_ that our `update` function "skeleton"
317+
(_because this is the "TEA" pattern_)
318+
good test to _start_ with is the `default case`.
224319

225320

226321

0 commit comments

Comments
 (0)