intercellular / cell

A self-driving web app framework
https://www.celljs.org
MIT License
1.5k stars 94 forks source link

Nested Structures #5

Closed syncai closed 7 years ago

syncai commented 7 years ago

Any tips or elegant way to handle a nested structure like this?

<div class="mdc-form-field">
  <div class="mdc-textfield" data-mdc-auto-init="MDCTextfield">
    <input id="firstname" type="text" class="mdc-textfield__input">
      <label for="firstname" class="mdc-textfield__label">
            First Name
      </label>
 </div>
</div>
gliechtenstein commented 7 years ago

If we just raw-translate that HTML markup into Cell, it should look something like:

ಠᴥಠ = {
  $cell: true, class: "mdc-form-field", $components: [{
    class: "mdc-textfield", "data-mdc-auto-init": "MDCTextfield", $components: [{
      $type: "input", id: "firstname", type: "text", class: "mdc-textfield__input",
      $components: [{
        $type: "label",
        for: "firstname",
        class: "mdc-textfield__label",
        $text: "First Name"
      }]
    }]
  }]
}

This becomes increasingly useful as you start extracting small pieces out into reusable functional components.

At the end of the day all you need is an object in the global scope that looks like above, so you can write generator functions that create reusable components.

For example, let's try extracting out input component as a function that looks like this:

Input = function(options){
  return {
    class: options.class, "data-mdc-auto-init": "MDCTextField", $components: [{
      $type: "input", id: options.id, type: "text", class: options.class+"__input",
      $components: [{
        $type: "label",
        for: options.id,
        class: options.class+"__label",
        $text: options.label
      }]
    }]
  }
}

Then you can use it like this:

ಠᴥಠ = {
  $cell: true, class: "mdc-form-field", $components: [ Input({id: "firstname", class: "mdc-textfield", "label": "First Name"}) ]
}

Maybe you want to add other fields:

ಠᴥಠ = {
  $cell: true, class: "mdc-form-field", $components: [ 
    Input({id: "firstname", class: "mdc-textfield", "label": "First Name"}), 
    Input({id: "lastname", class: "mdc-textfield", "label": "Last Name"}),
    Input({id: "age", class: "mdc-textfield", "label": "Age"})
  ]
}

Maybe you want to abstract out even further to extract the "mdc-form-field" as a component. You could do:

Form = function(items) {
  return { $cell: true, class: "mdc-form-field", $components: items.map(Input) }
}

and call it from the app like this:

ಠᴥಠ = Form([ 
  {id: "firstname", class: "mdc-textfield", "label": "First Name"}, 
  {id: "lastname", class: "mdc-textfield", "label": "Last Name"},
  {id: "age", class: "mdc-textfield", "label": "Age"}
])

You can even separate them out into different files.

<html>
<script src="https://www.celljs.org/cell.js"></script>
<script src="./input.js"></script>
<script src="./form.js"></script>
<script src="./app.js"></script>
</html>

I'm sure there are other creative ways of making things modular other than this. Like I mentioned above, at the end of the day, all we need is a JSON object with a $cell key in the global scope.

Hope this helps, let me know if you were asking something else :)

mrdougwright commented 7 years ago

THIS IS AWESOME!!!!!!! 😆

syncai commented 7 years ago

Thanks, I figured the design pattern was something like this, but the detail is very helpful.