Reputation: 2857
I'm working on to create a custom component using Stencil to replicate UI-select
.
The component will be used like:
let items = [{name:"Abc", age: 10}, {name:"Xyz", age: 10}];
let itemString = JSON.stringify(items);
<dropdown-search placeholder="Select User" item-string='${itemString}'>
</dropdown-search>
Then the component is defined as
import {
Component,
State,
Prop,
Element,
} from '@stencil/core';
@Component({
tag: 'dropdown-search',
})
export class DropdownSearch {
@Element() dropdownEl: HTMLElement;
@State() isOpen: boolean = false;
@State() items: any = [];
@Prop() itemString: string = '';
@Prop() placeholder: string = '';
componentDidLoad() {
try {
this.items = JSON.parse(this.itemString);
} catch(e) {}
}
onClickDropdownHandler = (e: UIEvent) => {
e.preventDefault();
this.toggleDropdown();
}
toggleDropdown = () => {
this.isOpen = !this.isOpen;
if (this.isOpen) {
window.setTimeout(
() => {
this.dropdownEl.querySelector('input').focus();
},
0,
);
}
}
renderOptions = () => {
if (!Array.isArray(this.items) || !this.items.length) {
return null;
}
return this.items.map((item) => (
<li
>
<a href="javascript:" title="{item.name}">
{item.name}
<small class="d-block">age: {item.age}</small>
</a>
</li>
));
}
render() {
let dropdownClassName = (this.isOpen ? 'open' : '');
return (
<form name="myForm">
<div class="form-group">
<div
class={`btn-group dropdown ${dropdownClassName}`}
>
<button
class="btn btn-default dropdown-toggle"
onClick={this.onClickDropdownHandler}
>
{this.placeholder}
</button>
<ul class="dropdown-menu" role="menu">
<li>
<div class="input-group input-group-search">
<input
class="form-control"
type="search"
/>
</div>
</li>
{this.renderOptions()}
</ul>
</div>
</div>
</form>
);
}
}
The items are rendering fine. As as the user can pass a custom array of objects, so I need to customize the options template. So the user can pass it while using the component.
Right now I'm using a static template for the options within the component, like
<a href="javascript:" title="{item.name}">
{item.name}
<small class="d-block">age: {item.age}</small>
</a>
but I need a way to pass this template from where I'm using the template. I can't use slot there as I'm using the same template within all the options that running in a loop.
Upvotes: 4
Views: 1815
Reputation: 1057
I am new with stencil but just tried to utilize an @Prop function for this, and it worked:
Component
@Component({
tag: 'dropdown-search',
styleUrl: 'dropdown-search.css',
shadow: true,
})
export class DropdownSearch {
// allow template function to be passed from outside
@Prop template!: (item: SelectItem) => any;
@State items!: SelectItem[] = [{name: 'dog'}, {name: 'cat'}, {name: 'elephant'}];
render() {
return (
<Host>
<p>Before Template</p>
{/* render the custom template here: */}
{this.items.map(item => this.template(item))}
<p>After Template</p>
</Host>
);
}
}
Providing custom template to this component:
class AppComponent {
render() {
// Here we define our custom template to be rendered in MyComponent
const CustomTemplate = (item: SelectItem) => <p key={item.name}>Hi, I am {item.name}</p>;
return (
<div class="app-home">
<dropdown-search template={CustomTemplate} />
</div>
);
}
}
result looks like this:
Upvotes: 1