1
1
# ` Elm ` (_ ish_ )
2
2
3
- ### How to Build a Front-end Micro-Framework _ From Scratch_
3
+ ### ( How to Build a Front-end Micro-Framework _ From Scratch_ )
4
4
5
5
![ elmlogo-ish] ( https://user-images.githubusercontent.com/194400/43213139-b70a4c68-902d-11e8-8162-3c7cb56b6360.png )
6
6
<!-- the colors are deliberately "a bit off" to emphasize that
@@ -78,26 +78,18 @@ please see:
78
78
and
79
79
[ front-end-with-tape.md] ( https://github.com/dwyl/learn-tape/blob/master/front-end-with-tape.md )
80
80
81
- ### Start by Creating the Files
82
-
83
- It's "OK" to ask: "_ Where do I ** start** (my ** TDD** quest)?_ " <br />
84
- The answer is: create ** two** new files:
85
- ` examples/todo-list/elmish.js ` and ` test/elmish.test.js `
86
-
87
- We will create a couple of tests and their corresponding functions _ next_
88
- but first, let's take a moment to think about what we _ can_ generalise
89
- from the code we wrote for our "counter" example at the start of this tutorial.
90
81
91
82
### What _ Can_ We _ Generalise_ ?
92
83
93
- Our ** first step** in creating ` Elm ` (_ ish_ ) is to consider
94
- what _ can_ be _ generalised_ into
84
+ Our ** first step** in creating ` Elm ` (_ ish_ )
85
+ is to _ re-visit_ the functions we wrote for the "counter app"
86
+ and consider what _ can_ be _ generalised_ into
95
87
an application-independent re-useable framework.
96
88
97
89
> Our ** rule-of-thumb** is: anything that creates (_ or destroys_ )
98
90
a DOM element or looks like "plumbing"
99
- (_ e .g: "routing" or "managing state"_ ) is _ generic _
100
- and should thus be abstracted into the ` Elm ` (_ ish_ ) framework.
91
+ (_ that which is common to ** all apps ** , e .g: "routing" or "managing state"_ )
92
+ is _ generic _ and should thus be abstracted into the ` Elm ` (_ ish_ ) framework.
101
93
102
94
103
95
Recall that there are ** 3 parts** to the Elm Architecture:
@@ -134,21 +126,106 @@ these _can_ (_will_) be generalised (_below_).
134
126
135
127
136
128
Let's start with a couple of "_ familiar_ " _ generic_ functions
137
- (_ which we saw and used in the "counter" example_ ):
138
- ` empty ` and ` mount ` . <br />
129
+ (_ which we used in the "counter-reset " example_ ):
130
+ ` init ` , ` empty ` and ` mount ` . <br />
139
131
140
132
<br />
141
133
142
- #### ` empty ` the DOM
134
+ ### Start by Creating the Files
135
+
136
+ It's _ essential_ to ask: "_ Where do I ** start** (my ** TDD** quest)?_ " <br />
137
+ The answer is: create ** two** new files:
138
+ ` examples/todo-list/elmish.js ` and ` test/elmish.test.js `
139
+
140
+
141
+ ### Test Setup
142
+
143
+ In order to run our test, we need some "setup" code
144
+ that "requires" the libraries so we can _ execute_ the functions.
145
+
146
+ In the ` test/elmish.test.js ` file, type the following code:
147
+ ``` js
148
+ const test = require (' tape' ); // https://github.com/dwyl/learn-tape
149
+ const fs = require (' fs' ); // to read html files (see below)
150
+ const path = require (' path' ); // so we can open files cross-platform
151
+ const html = fs .readFileSync (path .resolve (__dirname ,
152
+ ' ../examples/todo-list/index.html' )); // sample HTML file to initialise JSDOM.
153
+ require (' jsdom-global' )(html); // https://github.com/rstacruz/jsdom-global
154
+ const elmish = require (' ../examples/todo-list/elmish.js' ); // functions to test
155
+ elmish .init (document ); // pass JSDOM into elmish for DOM functions
156
+ const id = ' test-app' ; // all tests use 'test-app' as root element
157
+ ```
158
+
159
+ > Most of this code should be _ familiar_ to you
160
+ if you have followed previous tutorials.
161
+ > If anything is _ unclear_ please revisit
162
+ https://github.com/dwyl/learn-tape
163
+ and
164
+
165
+ If you attempt to run this code using the command:
166
+ ``` sh
167
+ node test/elmish.test.js
168
+ ```
169
+
170
+ you will see something like the following:
171
+ ![ no-init-function] ( https://user-images.githubusercontent.com/194400/43359605-b8cc9418-929c-11e8-92d6-97feb8c67596.png )
172
+
173
+ This is because we do not have anything in the ` elmish.js ` , yet.
174
+ Let's address that now!
175
+
176
+
177
+ ### Add ` init ` function
178
+
179
+ Open the ` examples/todo-list/elmish.js ` file and add the following code:
180
+
181
+ ``` js
182
+
183
+
184
+ /**
185
+ * `init` initialises the document (Global) variable for DOM operations.
186
+ * @param {Object} doc window.document in browser and JSDOM.document in tests.
187
+ * @return {Object} document returns whatever is passed in.
188
+ */
189
+ function init (doc ){
190
+ document = doc; // this is used for instantiating JSDOM for testing.
191
+ return document ;
192
+ }
193
+ ```
194
+
195
+ This code is simply to allow us to "initialise" ` Elm ` (_ ish_ )
196
+ with a "fake" DOM (` JSDOM ` ) so that we can _ test_ it.
197
+
198
+
199
+ ### Add ` module.exports ` to "export" the ` init ` function
200
+
201
+ Adding the function to the ` elmish.js ` file is a good _ start_ ,
202
+ but we need to *** ` export ` *** it to be able to _ invoke_ it in our test.
203
+ Let's add the following code at the end of ` examples/todo-list/elmish.js ` :
204
+
205
+ ``` js
206
+ /* module.exports is needed to run the functions using Node.js for testing! */
207
+ /* istanbul ignore next */
208
+ if (typeof module !== ' undefined' && module .exports ) {
209
+ module .exports = {
210
+ init: init
211
+ }
212
+ } else { init (document );
213
+ ` ` `
214
+
215
+
216
+
217
+
218
+ ### ` empty` the DOM
143
219
144
220
Start by _describing_ what the ` empty` function _does_. <br />
145
221
This is both to clarify our _own_ understanding
146
222
as the people _writing_ the code <br />
147
223
and to _clearly communicate_ with the **` humans` _reading_** the code.
148
224
149
- ##### Function Description
225
+ #### ` empty ` Function _Description_
150
226
151
- The ` empty ` function clears the DOM elements out of a specific "root" element.
227
+ The ` empty` function deletes all DOM elements
228
+ from within a specific "root" element.
152
229
We use it to erase the DOM before re-rendering our app.
153
230
154
231
Following "**_Document(ation)_ Driven Development**",
@@ -158,12 +235,13 @@ with _just_ the function description:
158
235
159
236
` ` ` js
160
237
/**
161
- * `empty` clears the DOM elements out of a specific "root" element.
238
+ * `empty` deletes all the DOM elements from within a specific "root" element.
162
239
* it is used to erase the DOM before re-rendering the app.
163
240
*/
164
241
` ` `
165
242
Writing out the function documentation _first_
166
- helps us _ think_ about the functionality.
243
+ allows (_our subconscious_) time to _think_ about the functionality
244
+ and how to _test_ for the "_acceptance criteria_".
167
245
Even if you know _exactly_ what code needs to be written,
168
246
_resist_ the temptation to write the code until it is documented.
169
247
Even if you are writing code alone,
@@ -172,12 +250,15 @@ who does _not_ (_already_) "know the solution"
172
250
and you are _explaining_ it to them.
173
251
174
252
253
+ #### ` empty` Function _Test_
175
254
176
- Given that we _ know_ we are going to use the ` empty `
177
-
178
- function we used previously in our ` counter ` ,
255
+ We previously used the ` empty` function in our ` counter` ,
179
256
` counter- reset` and ` multiple- counters` examples (_in the "basic" TEA tutorial_)
180
- we can write a _ test_ for the ` empty ` function quite easily.
257
+ so we have a "head start" on writing the test.
258
+
259
+ > _The **reason**(s) we write the **test first**
260
+ even when we (already) know the "solution" is:_ <br />
261
+ >
181
262
182
263
183
264
In the ` test/ elmish .test .js ` file, type the following code:
@@ -187,7 +268,7 @@ const fs = require('fs'); // to read html files (see below)
187
268
const path = require (' path' ); // so we can open files cross-platform
188
269
const elmish = require (' ../examples/todo-list/elmish.js' ); // functions to test
189
270
const html = fs .readFileSync (path .resolve (__dirname ,
190
- ' ../examples/todo-list/index.html' )); // sample HTML file for JSDOM to load
271
+ ' ../examples/todo-list/index.html' )); // sample HTML file to initialise JSDOM.
191
272
require (' jsdom-global' )(html); // https://github.com/rstacruz/jsdom-global
192
273
elmish .init (document ); // pass JSDOM into elmish for DOM functions
193
274
const id = ' test-app' ; // all tests use 'test-app' as root element
@@ -223,17 +304,19 @@ issue***!](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues)
223
304
_It's **essential** that you **understand** each **character**
224
305
in the code **before** continuing to **avoid** "**confusion**" later._
225
306
307
+ #### ` empty` Function _Implementation_
308
+
226
309
Now that we have the **test** for our ` empty` function written,
227
310
we can add the ` empty` function to ` examples/ todo- list/ elmish .js ` :
228
311
` ` ` js
229
312
/**
230
- * `empty` the contents of a given DOM element "node" (before re-rendering).
313
+ * `empty` deletes all the DOM elements from within a specific "root" element.
314
+ * it is used to erase the DOM before re-rendering the app.
231
315
* This is the *fastest* way according to: stackoverflow.com/a/3955238/1148249
232
- * @param {Object} node the exact DOM node you want to empty
316
+ * @param {Object} node the exact ("parent") DOM node you want to empty
233
317
* @example
234
318
* // returns true (once the 'app' node is emptied)
235
- * const node = document .getElementById (' app' );
236
- * empty (node);
319
+ * empty (document .getElementById (' app' ));
237
320
*/
238
321
function empty (node ) {
239
322
while (node .lastChild ) {
@@ -242,12 +325,18 @@ function empty(node) {
242
325
}
243
326
` ` `
244
327
245
- If the ** comment syntax**
246
- above the function definition
247
- is _ unfamiliar_ ,
328
+ If the **comment syntax** above the function definition is _unfamiliar_,
248
329
please see:
249
330
[https://github.com/dwyl/**learn-jsdoc**](https://github.com/dwyl/learn-jsdoc)
250
331
332
+ When you run the test in your terminal with the command
333
+ ` node test/ elmish .test .js `
334
+ you should see something _similar_ to this:
335
+
336
+
337
+
338
+
339
+
251
340
252
341
### ` mount` the App
253
342
0 commit comments