# Creating Drop Down Component with Ember Contextual Component

***Note:*** *This blog was originally written by me on* [kiprosh.com](https://blog.kiprosh.com/creating-drop-down-component-with-ember-contextual-component-2/)

In most of the recent frameworks, you must have come across concept called Component. Component has became integral part of JS development these days. Those are independent and reusable bits of code, which make your code looks much more cleaner and maintainable. Here in this article, we will look into “Contextual Component” to write more flexible and expressive interfaces for your components in Ember.js framework.

## Problem:

While working on one of the Ember.js application, we came across a requirement of adding a drop-down in the project. There were already two drop-down components, but they were very tightly coupled to certain styling, looks, and usage. As per the new requirement, these drop-down components were not fit to be used. We wanted to create a generic drop-down component that should fulfill following requirements:

* It should not have business logic in it.
    
* Users should have the liberty to apply to their UI, styling, and action.
    

## Possible Solution:

We find [block level component](https://guides.emberjs.com/v2.18.0/components/block-params/) should be a perfect fit for this scenario

### What is the Block component?

A block component is a component that has a start-tag, end-tag, but in between, you can add the custom UI to be displayed.

```sql
/**
  This is component usage
**/
{{#drop-down callbackToBeCalled=(action 'AskComponentToRunThisAtSomePoint')}}
   <div>
     The UI user want to write
   </div>
{{/drop-down}}
```

In the component `hbs` file you need to `yield`, in order to render the content.

```sql
/**
  This is drop-down.hbs file
**/
{{yield}}    //This will will render passed `div`
<div>
  This is component default element, which will be available everytime.
</div>
```

Usually, any drop-down component has the following features:

* Dropdown with the message "Choose Option" or a preselected element from the list.
    
* On clicking the drop-down button it should show (render) a list of choices.
    
* On selecting the element from the list, close the drop-down and display the selected choices.
    

We can divide our Drop-down component into 2 parts:

* **Generic drop-down elements** (This contains what every drop-down component should have):
    
    * It should display a `button` or `div` with some text like "Choose Option".
        
    * Show and hide the drop-down list.
        
* **User dependent elements** (This contains what the user can add to the component):
    
    * The list user wants to display along with styling and look of the list.
        
    * How selected element of drop-down should look in the head.
        
    * What action to be executed on selecting the list element.
        

We will design our component based on the above `generic` and `user-dependent` elements.

### Issue with component yield?

This component expects two elements as input from the user, and they are the `heading` of the dropdown and the list element of the dropdown. So, if there are two inputs the component is expecting from the user, then it also has two data to yield. But how will the component know which `yield` will be `heading` and which will be `body`?

To make the picture clear, you can see that the user is providing one `div` with the text "Choose Option" and another `ul` tag with some list rendering logic.

```sql
 {{#drop-down callbackToBeCalled=(action 'AskComponentToRunThisAtSomePoint')}}
   <head>
      <div> "Choose Option" </div>
   </head>
   <body>
    <ul>
     {{#each lists as |list|}}
       {{list}}
     {{/each}}
    </ul>
   </body>
{{/drop-down}}
```

When the user sends the `head` and `body` tag, our component should be able to identify it. How can we achieve this?

### Contextual component comes to rescue 🦸🏻

What is **Contextual Component?** In simple words, a component that can yield other components in its block. The main parent component (in our case, drop-down) will provide a named block component where users can render the UI of their choice. We want to display `head` and `body` information within some blocks. So, the parent component will be passing two block components without a body and a name.

Let's create a block-component named `blank-component` where our `head` and `body` will be rendered.

```sql
//blank-component.hbs
{{yield}}

//blank-component.js
  export default Component.extend({
    tagName: ""
  });
```

Now, we will yield this component with a name in the `drop-down` component. (***Note:*** `yield` has the property to send a hash of information.)

```sql
 //drop-down.hbs
 <div {{action "toggleDisplayItems"}}>
    {{yield (hash dropDownHeader="blank-template")}}
 </div>
 <div class="{{if displayDropDownItems '' 'hidden'}}">
  {{yield (hash
    dropDownBody="blank-template"
    clickItem = (action 'selectOption')
  )}}
 </div>
```

You can see we have two yield statements, one with the name `dropDownHeader` and the other `dropDownBody` and both of them are passing the `blank-component` block component. In short, it will provide a `head` block(namespace) to display `"Choose from a list"` text and the `body` block to "display the list".

### Final Usage:

```sql
{{#drop-down onSelect=(action "doSomethingOnSelecting") as |dropDown|}}
  {{#dropDown.dropDownHeader}}
     <div class="any-styling-of-your-choice">
      "Choose from a list"
     </div>
  {{/dropDown.dropDownHeader}}
  {{#dropDown.dropDownBody}}
     <ul>
       {{#each lists as |list|}}
         <li {{action "doSomeAction" list}}>
          {{list}}
        </li>
       {{/each}}
     </ul>
  {{/dropDown.dropDownBody}}
{{/drop-down}}
```

As you can see, the Contextual component provided the user with a namespace that gives them the freedom to render `head` and display `body` of their choice. The user is now not bounded to drop-down styling and action of the drop-down. User can provide custom UI-styling and actions.

Hope you like this blog. If you have any question, let me know in the comment. Thank you.

### References:

* [How to yield an ember component](https://dockyard.com/blog/2018/11/26/how-to-yield-an-ember-component-in-multiple-placesblog/2018/11/26/how-to-yield-an-ember-component-in-multiple-places)
