Reputation: 446
I want to pass an array of objects and then iterate through the array and render each object.
I have the following my html file:
<stencil-component
cards = '[{"cardTitle"="placeholder", "copy"="copy1"},{"cardTitle"="placeholder2", "copy"="copy3"}]'>
</stencil-component>
And in my tsx file I have the following:
@Prop() cards: Array<object> = [];
render(){
return (
{this.cards.map(card) => {
return (
<div>
<h1>{card.cardTitle}</h1>
<p>{card.copy}</p>
</div>
);
})}
)
}
I cant even get the array to display. I tried to use the JSON.parse function to parse the data and that didn't work either. If anyone could please help, I would really appreciate it!
Upvotes: 3
Views: 7864
Reputation: 1781
Your Problem is that the JSON is actually no JSON (replace = with :) and further the data isn't parsed automatically to an object array when you share it through the attributes of the component. I would use a generic function like getDataFromProperty
and outsource it in a different file that you can use it in all your web components. But lets keep it simple for now:
@Prop() cards: Array<object> = [];
private getDataFromProperty(toParse):any{
try(JSON.parse(toParse)){
return JSON.parse(toParse);
}catch(e){
//some different conversations you wanna try e.g.
console.error('Couldnt JSON parse', e);
if(toParse.split(',').length > 0) return toParse.split(',');
}
}
render(){
if(!this.cards) this.cards = this.getDataFromProperty(this.cards);
return (
{this.cards.map(card) => {
return (
<div>
<h1>{card.cardTitle}</h1>
<p>{card.copy}</p>
</div>
);
})}
)
}
<stencil-component
cards = '[{"cardTitle": "placeholder", "copy": "copy1"},{"cardTitle": "placeholder2", "copy": "copy3"}]'>
</stencil-component>
Try and Catch will prevent some hard errors and give you informations what happened if it couldn't be parsed. Maybe you have to tweak it a bit for your needs but the concept works. In the catch
condition you can try some other techniques for data conversation.
Secondly i wanna share a complete different approach that depends on the environment you are using but you can use a public method. Than you can share data to the web component directly:
@Prop() cards: Array<object> = [];
@Method() setData(data){
this.cards = data;
}
render(){
//your render function as it was
}
Inside your HTML
<stencil-component id="web-component"></stencil-component>
<script>
let data = '[{"cardTitle": "placeholder", "copy": "copy1"},{"cardTitle": "placeholder2", "copy": "copy3"}]';
document.getElementById("web-component").setData(data);
//just call the setData function from outside works with jquery too
</script>
But there is one little thing you have to notice in that approach. Maybe your render function won't be called than you can do it manually in that function by just using this.render();
or you can use the @State
decorator in front of your variable.
Upvotes: 1
Reputation: 8849
HTML attributes can only hold strings, so if you use the component outside of Stencil you will have to pass all properties as strings.
In Stencil (and probably some frameworks) you could pass it like this:
<stencil-component
cards={[{"cardTitle":"placeholder", "copy":"copy1"},{"cardTitle":"placeholder2", "copy":"copy3"}]}>
</stencil-component>
Note: Your JSON is incorrect, you need to replace =
with :
.
If you need to use the component in plain HTML you will have to either manually parse the property string or set it using javascript.
There are examples of this in the docs:
Setting the prop manually
import { Prop } from '@stencil/core'; export class TodoList { @Prop() myObject: object; @Prop() myArray: Array<string>; }
HTML
<todo-list></todo-list> <script> const todoListElement = document.querySelector('todo-list'); todoListElement.myObject = {}; todoListElement.myArray = []; </script>
Watching props changes
import { Prop, State, Watch } from '@stencil/core'; export class TodoList { @Prop() myObject: string; @Prop() myArray: string; @State() myInnerObject: object; @State() myInnerArray: Array<string>; componentWillLoad() { this.parseMyObjectProp(this.myObject); this.parseMyArrayProp(this.myArray); } @Watch('myObject') parseMyObjectProp(newValue: string) { if (newValue) this.myInnerObject = JSON.parse(newValue); } @Watch('myArray') parseMyArrayProp(newValue: string) { if (newValue) this.myInnerArray = JSON.parse(newValue); } }
HTML
<todo-list my-object="{}" my-array="[]"></todo-list>
Upvotes: 6