Skip to content

Instantly share code, notes, and snippets.

@asavin
Last active August 29, 2015 13:56
Show Gist options
  • Select an option

  • Save asavin/9252277 to your computer and use it in GitHub Desktop.

Select an option

Save asavin/9252277 to your computer and use it in GitHub Desktop.

#Componentify it

One of the most amazing things about Web development is the scale of bycicle inventions. Pretty much every developer at some point decides to conquer a common problem by rewriting it from scratch. Reusing existing solutions is often difficult - they are likely to be tied to internal styles, libraries, build processes. This creates a nurturing ground where same features are implemented many, many times not only because developers think they can do it better, but also because there are too much of efforts to reuse existing solution without bringing up all the dependencies.

This blogpost is about Component approach. Components are designed to have as little dependencies as possible, relying on common web technologies, easily reusable and extendable. With component approach it's trivial to unplug parts of the app and replace them with other parts or improved version of the same component. There are certain principles to be considered when releasing new components, since the Component itself doesn't really limit you in any way. It is possible that later on we'll have some monster components with huge amount of dependencies lurking around - but then again using such components is completely up to you.

When an aspect of a component may be useful to others, consider writing that as a component as well. If it requires reasonable effort to write the code in the first place, chances are someone else could use it too.

Building better components

###Make a new component

Component comes with a handy console command. Assuming that you have component npm package installed - try running component help

To create a new component you can run component create mynewcomponent. This command will ask you (quite) a few questions, and then will create a new folder with some basic files.

If you try compiling newly generated component, you might get a following error:

$ component build
error : ENOENT, open '/Users/alex/red-badger/mytestcomponent/template.js'

This happens because there is template.js file specified in component.json, but is not generated by component create. You can either create this file manually, or remove it from component.json. After that component build should generate your first component under /build folder.

Each component can contain any amount of:

  • JS files, with mandatory index.js or main.js file assigned to a "main" directive in component.json
  • CSS files
  • HTML files
  • Local and external dependencies to other components

All assets must be explicitly listed in component.json, otherwise they are ignored. This is another clever feature of Component since the folder might contain temp files, npm packages, generated files. When Component is instructed to pick only certain files, it will not only limit the build to these files, but also fetch them from Github (ignoring everything else you could've pushed there on purpose or by accident). This way building components with dependencies becomes much faster.

Component doesn't have to contain any JavaScript logic, and can simply export HTML template or CSS style:

<script src="https://gist.github.com/asavin/9269634.js"></script>

You can componentify any bit of style, markup or functionality into a reusable package. As long as it makes sense.

###Entry point

A fairly common pattern for a UI component is to ask for a DOM selector where the component would insert itself.

You can also use component/dom component for basic DOM manipulations. Dom component is obviously not a jQuery replacement, and lacks lots of jQuery functionality. You can live well also without dom component and manipulate DOM directly with document methods like document.getElementById, document.querySelectorAll. Adding and removing classes to elements can be slightly more challenging without any libraries, but there is a special component for that too - component/classes

Here is an example of component main.js using dom component and appending itself to the target element:

<script src="https://gist.github.com/asavin/9269688.js"></script>

In this case dom component would be specified in the dependencies section of component.json:

<script src="https://gist.github.com/asavin/9269696.js"></script>

###Using CoffeeScript, LiveScript, LESS and JADE with components

If you are going to release a public component, it make sense to package it with conventional JS/CSS/HTML files to reduce amount of dependencies and increase reusability. For internal components we've mostly used LiveScript, CoffeeScript, LESS styles and JADE templates. And to complete the LiveScript picture, we've repackaged Prelude.ls library into a component and used it as dependency.

Notable npm packages for component builds:

In Gruntfile you can configure builder to use extra components:

<script src="https://gist.github.com/asavin/9269707.js"></script>

And in grunt.initConfig section:

<script src="https://gist.github.com/asavin/9269718.js"></script>

###Global and local components

When using components extensively in your app you might end up with lots of public and private components. All public components and their dependencies will be automatically installed into a /components folder. It's a good idea to put your private components into a /local folder next to the /components folder. You can also have dependencies between your local component:

<script src="https://gist.github.com/asavin/9269728.js"></script>

Syntax for local dependencies slightly differs from the global dependencies - there is no version, and you just list all local components as array.

In the root of both /local and /components folders you will need a main component.json file, which will tell the build process which components needs to be included in the final main.js file:

<script src="https://gist.github.com/asavin/9269739.js"></script>

paths will tell the builder to look for /local folder in addition to /components folder.

Later in the html file you simply include this generated main.js.

<script src="https://gist.github.com/asavin/9269752.js"></script>

The content of this file is evaluated, now you can require and start using any of the components on your pages:

<script src="https://gist.github.com/asavin/9269747.js"></script>

Most of Red Badger components include example html file with everything you need to start using that component on your pages.

##Check for existing implementation

Before implementing your own component, it might be worth of checking for existing implementation. Most of the components are listed here:

https://github.com/component/component/wiki/Components

Component.io site is another slick way of browsing and searching for existing components. In many cases you'd want to start with existing component and either use it as dependency or fork and add your own functionality. Standard component/components are especially easy starting point for extending.

##Conclusion

Component approach requires some extra efforts. You have to abstract and package bits of UI, make them fairly independent and think of the possible reuse cases. You can ignore the new components creation stage completely and just use existing components. We chose to componentify most of our recent World Risk Review project and this proved to be a brilliant decision. Instead of repetitive blocks of styles, markup and logic we managed to put most of the front end elements into components and reuse them when needed. Some of the components were also released as open source, and we hope to see even more useful components released in the future!

@charypar
Copy link

"All assets must be explicitly listed in component.json, otherwise they are ignored." It's probably worth explaining why that is (that an npm module can have a browser build in it and this way Component can ignore the rest of it and get a speed improvement when fetchnig from Github as a bonus).

@asavin
Copy link
Author

asavin commented Feb 28, 2014

@charypar good note, added some explanation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment