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.
Use the DOM API
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>
Why do I have to do this?
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()
.
Other Solutions
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>
`
}