# How to create UI components

The page describes how to create a new UI component.

A fully functional example of source code you can find [here](https://github.com/omalab/oma/commit/03847e519214dafa672954e0c4e1fed4370b8d98)

## Steps

### Create a component class

Create a class inherited from`ApplicationComponent`under`app/components/`. By convention a class for a component must end in "Component". Add`#initialize`method to the class.

```
    def initialize(template, model)
      super template

      @model = model
    end
```

**Tips**

* It is better if namespaces of a component and a related controller are similar. It is not required but it simplifies stuff.
* it is encouraged to create a related presenter class as a counterpart of a UI component. In this case a presenter is responsible for fetching data, including optimization concerns. At the same moment a component is responsible for: 1) rendering data, including formatting 2) handling details of WEB UI (for example how to translate names of request params to attributes of a presenter)

**Example**: [component class](https://github.com/omalab/oma/blob/03847e519214dafa672954e0c4e1fed4370b8d98/app/components/keyword_tag/pre_association_form_component.rb)

### Create a view partial for a component

Create a partial file (/^\_\*.html.erb$/) under`app/views/components`. A file-path to just created view have to mimic namespacing of the component. For example if we have`Foo::Bar::BazComponent`, we have to create`app/views/components/foo/bar/_baz.html.erb`.

**Example**:[partial](https://github.com/omalab/oma/blob/03847e519214dafa672954e0c4e1fed4370b8d98/app/views/components/keyword_tag/_pre_association_form.html.erb)

### Instantiate a component object

Instantiate a component object using`ApplicationController#new_component`method:

```
  @baz = new_component(::Foo::Bar::BazComponent, @some_presenter)
```

**Example**: [instantiation](https://github.com/omalab/oma/blob/03847e519214dafa672954e0c4e1fed4370b8d98/app/controllers/keyword_tag/pre_associations_controller.rb#L9)

### Call #render

Render your component using inherited`#render`method:

```
<%= @baz.render %>
```

**Example**: [call render](https://github.com/omalab/oma/blob/03847e519214dafa672954e0c4e1fed4370b8d98/app/views/keyword_tag/pre_associations/new.html.erb)

### Add JavaScript support (optional)

If your component depends on JS functionality, you should perform a few additional steps.

Mix`JsAware`module to your component.

```
module Foo
  module Bar
    class BazComponent < ApplicationComponent
      include JsAware
      ...
```

Create JS file under`app/javascripts/main/components/`using the same approach to "namespacing" like for the view partial:`app/javascripts/main/components/foo/bar/baz.js`

A javascript object for a component should have`init`method. Th method is call right after component is rendered.

```
window.foo = window.foo || {}
window.foo.bar = window.foo.bar || {}

window.foo.bar.Baz = (function () {
  var c = {};

  c.init = function (_opts) {
    $('.baz-time').html(new Date().toString())
  }

  return c;
})();
```

Add newly created JS file to grunt.js. It is preferable to use wildcards:

```
'app/javascripts/main/components/foo/bar/*.js'
```

Recompile javascript (run`grunt`console command).

**Example**: [js file](https://github.com/omalab/oma/blob/03847e519214dafa672954e0c4e1fed4370b8d98/app/javascripts/main/components/keyword_tag/pre_association_form.js\),\[grung]\(https://github.com/omalab/oma/blob/03847e519214dafa672954e0c4e1fed4370b8d98/grunt.js#L119)

_Basically that's it. We just created a new UI component._
