Writing a frontend framework

 

​purpose & fundamentals

Federico Pereiro

Start with why

Two heroes under stress

Two heroes under stress

1) Starts with energy

 

2) Faces challenges

as chosen

Two heroes under stress

1) Starts with dread

 

2) Faces challenges

as imposed

Tools make all the difference

Simple, not easy

Purpose:

write frontends feeling like Rocky, not like Homer

Moving onto how

How can I write a modern web app without a framework?

What is a modern web app anyway?

Web page

Web application

Web page

- Sending or receiving info requires a page refresh.

 

- The page is rendered once.

Web app

- Background communication with server.

- The page must be updated.

Web page

- Relies on connectivity.

- Fetches the whole thing.

Web app

- Offline ability.

- Fetches just the data.

A horrible solution

// server code

http.get ('/update', function (req, res) {

   response.end ('<li>New item</li>');

 })

// client code

ajax ('get', '/update', function (error, data) {

   $ ('ul.list').append (data);

});

 

Better, but still bad

// server code

http.get ('/update', function (req, res) {

   response.end (['New item']);

 })

// client code

ajax ('get', '/update', function (error, data) {

   JSON.parse (data).map (function (item) {

      $ ('.table').append ('<li>' + item + '</li>');

   });

});

 

Don't try this at home

// client code

function makeTag (tag, className, value, content) {

   var o = '<' + tag;

   o += ' class="' + className + '"';

   o += ' value="' + value + '">';

   o += content;

   o += '</' + tag + '>';

   return o;

}

Object literals!

<p>Hello!</p>

[‘p’, ‘Hello!’]

 

Object literals!

<p class=”nice”></p>

 

[‘p’, {class: ‘nice’}]

Object literals!

<div>
   <p class=”nice”>Hello</p>
</div>


['div', [
   [‘p’, {class: ‘nice’}, 'Hello']
]]

Object literals!

Conditional #1: cond ? [‘p’, ‘Hello!’] : []

 

Conditional #2:
[‘p’, {class: cond ? ‘h2’ : ‘’}, ‘Hello!’]

Iteration:
[‘table’, ITEMS.map (function (item) {
   return [‘tr’, [‘td’, item]];
})]

Object literals!

// client code
ajax ('get', '/update', function (error, data) {
   var items = JSON.parse (data).map (function (item) {
      return ['li', item];
   });
   $ ('ul.list').append (makeHTML (items));
});

I'm free!

Not so fast...

A simple application

Dependencies!

(Re)load:

- Affects everything.

- Must preserve user-provided amounts.

- Must preserve cart.

Filter:

- Affects counter of matching elements.

- Affects display of matching elements.

- Must preserve hidden elements.

 

 

 

Dependencies!

Add to cart:

- Relies on whether product is in cart already.

- Relies on amount entered in box.

- Affects cart item count, total & actual items.

Remove from cart:

- Affects cart item count, total & actual items.

Dependencies!

Submit cart:

- Get all cart data.

- Submit it.

- Clean up cart.

Imperative

reload (data) {
   updateItems ();
   updateCart ();
}

changeFilter () {
    var filter = $ ('#filter').value;
    updateItems ();
}

 

Imperative

addToCart (index) {
   var q = $ ('#amount-' + index);
   var p = ... // get matching product
   updateCart ();
}

removeFromCart (index) {
    var p = ... // get matching cart entry
    updateCart ();
}

 

Imperative

updateItems () {
   // Update counters
   // Go through each of the DOM elements
   // Hide/unhide it if required
   // Update it if it has changed
   // Remove it if it was deleted
   // Add it if it is new
}

Imperative

updateCart () {
   // Update counter & total
   // Go through each of the DOM elements
   // Hide/unhide it if required
   // Update it if it has changed
   // Remove it if it was deleted
​   // Add it if it is new
}

Every new feature must accomodate and be accomodated by the other features

O ( n )

2

Imperative with store

var Store = {
   items: [],
   cart: [],
   filter: ''
};

['input', {oninput: 'Store.filter = this.value; redraw ()'}];

Imperative with store

function redraw () {
   // redraws entire view









}

Reactive views & store

view ('items', function (items) {
   return view ('filter', function (filter) {
      return items.map (function (item) {
         if (item.match (filter)) return ['td', item];
      });
   });
});

Events & store

do ('set', 'filter', 'kitties');

​​do ('set', 'items', [
   {title: 'kitties', price: 20},
   {title: 'sunshine', price: 30}
]);

do ('add', 'items', {title: 'New!', price: 1000});

do ('rem', 'items', 0);

 

 

The big three

verb

 

path

 

arguments

Mind the path


​​do ('set', ['items', 0, title], 'kitties & sunshine');

 

Where's the verb?

view ('items', function (items) {
   return view ('filter', function (filter) {
      return items.map (function (item) {
         if (item.match (filter)) return ['td', item];
      });
   });
});

 

Where's the verb?

add
set
rem

 


change

 

Implicitly...

view ('change', 'items', function (items) {
   return view ('change', 'filter', function (filter) {
      return items.map (function (item) {
         if (item.match (filter)) return ['td', item];
      });
   });
});

 

Summary of fundamentals

  • Object literals for templating
  • Events with verb + path
  • Global store updated by events
  • View is a function of the store, triggered by an event

O ( n )

github.com/fpereiro/gotoB

 

 

 

 

 

Questions!

Questions?

Thank you!


fpereiro@gmail.com

Writing a frontend framework

By Fede Pereiro

Writing a frontend framework

Why and how I ended up writing a frontend framework

  • 1,044