Is it possible to pass a function to a stencilJs
component?
Something like:
@Prop() okFunc: () => void;
I have a modal and want to dynamically call a passed function on the Ok
button clicked in the modal footer, like an onClick
on a normal HTML button.
Yes, you can. It is just a normal @Prop()
declaration and plays very nicely in TSX. However...
As noted in another answer and comments, if you are consuming a Stencil component in plain ol' HTML, you will not be able to use Stencil's attribute-prop binding to pass a function (or any non-scalar value) to a prop via an HTML attribute.
This means that you have to interact with the DOM if you want to attach events or pass function props to Stencil components. Once your component makes it into the DOM, there is really nothing special about it compared to any other Custom Element.
Without JSX or another template DSL (e.g. Angular), you will need to attach events and set your function- or object-reference props with the JavaScript DOM API:
const componentInstance = document.querySelector('my-stencil-component')
// This works and will trigger a rerender (as expected for a prop change)
componentInstance.someFunc = (...) => { ... }
// This works for any event, including custom events fired from Stencil's EventEmitter
componentInstance.addEventListener('myCustomEvent', (event: MyCustomEvent) => { ... })
If you absolutely must do this in your HTML document for some reason:
<my-stencil-component ... >...</my-stencil-component>
<script>
var componentInstance = document.currentScript.previousElementSibling
componentInstance.someFunc = function(...) { ... }
</script>
It's important to realize that Properties ≠ Attributes
. Props are JavaScript Properties, in this case, properties of the DOM object representing an element. Attributes are XML attributes, although HTML attributes have some unique characteristics and behave slightly differently than typical XML.
Stencil will automatically "bind" HTML attributes to properties for you where possible - in particular, for scalar values (boolean
, number
, string
). Object references, and therefore functions, cannot be used as the value of an attribute. Technically, only string
s can be attribute values, but Stencil is smart enough to convert string
to other another scalar type (boolean
or number
) when you specify the type of your @Prop()
.
I developed a solution for my team to bind attributes containing JSON to Stencil properties using a MutationObserver
. It basically watches for a special attribute bind-json
, then maps attributes starting with json-
onto the corresponding camelCase DOM properties. Usage looks like this:
<my-stencil-component
bind-json
json-prop-name='{ "key": "value" }'>
</my-stencil-component>
With the MutationObserver
in place, this is identical to:
const componentInstance = document.querySelector('my-stencil-component')
componentInstance.propName = JSON.parse(componentInstance.getAttribute('json-prop-name'))
However, there really is not a satisfying solution for binding functions in plain HTML. It really just can't be done without some sort of ugly hack like eval
described in another comment. Not only does that pollute your component's API, it's problematic for all kinds of other reasons I won't get into here and its use will automatically fail your app in practically any modern security check.
In our Storybook stories we bind callback function definitions to the window
, and use <script>
tags and document.currentScript
or querySelector
to pass the function props and event bindings to the component instance:
const MyStory = ({ ... }) => {
window.myStoryFunc = () => { ... }
window.myStoryClickHandler = () => { ... }
return `
<my-stencil-component ... >...</my-stencil-component>
<script>
const componentInstance = document.currentScript.previousElementSibling
componentInstance.someFunc = window.myStoryFunc
componentInstance.addEventListener('click', window.myStoryClickHandler)
</script>
`
}
You can just add a @Prop() someFunc: Function
to any component and pass it from the outside like
<any-component someFunc={() => console.log('coming from the outside')} />
Within anyComponent just check if (this.someFunc) { this.someFunc() }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With