Stencil: Help with non-scalar prop parsing

Created on 8 Oct 2020  Â·  3Comments  Â·  Source: ionic-team/stencil

I'm submitting feature request or a hint on how I can achieve this.

We're building a component library and some of our components can accept non-scalar props, like below :

     <my-component list="[1,2,3,4]"

Inside my component, we have :

 @Prop() list:number[]

However, since list is an array, and we're going to use our component as a web component, we need to pass a stringified array, which you can see in example above.

And because the list is a stringified array, we then need to parse it inside our Stencil component, like below :

@Prop() list : string[];

@Watch('list') parseList(stringList:string){
  if (typeof stringList ==='string'){
       try{
          this._parsedList = JSON.parse(stringList)
      }catch(){
        // ... 
      }
 }else{
  this._parsedList = stringList;
 }
} 

componentWillLoad(){
  // and then we have to call `parseList ` inside the componentWillLoad 
  this.parseList(this.list)
}

As you can see, above works, however, it is very unintuitive, messy and a lot of code has to be repeated and if we have multiple of these non-scalar props, you can image how ugly the code will get.

So I was thinking it would be nice if I can do something like :

@ParseJson
@Prop() list  

This is what I tried and failed

function ParseJson(target: Object, propertyName: string) {
  // property value
  let _val = this[propertyName];

// it fails here with error : 

>Cannot read property 'isProxied' of undefined ; Zone: <root> ; Task: Promise.then ; Value: TypeError: Cannot read property 'isProxied' of undefined
wontfix

Most helpful comment

Decorators in Stencil are just annotations for metadata — they aren't processed at runtime. As such, custom decorators don't work.

Side note: while the idea of setting non-string properties as attributes sounds convenient, I've consistently found that it's best to simply set those properties with JavaScript. Parsing JSON every time a prop changes, as you've discovered, requires boilerplate for each prop and can easily lead to performance issues. IMO it's also poor developer experience to expect users to serialize well-formed JSON in plain HTML (think of nested objects, having to escape quotes, etc.).

All 3 comments

Decorators in Stencil are just annotations for metadata — they aren't processed at runtime. As such, custom decorators don't work.

Side note: while the idea of setting non-string properties as attributes sounds convenient, I've consistently found that it's best to simply set those properties with JavaScript. Parsing JSON every time a prop changes, as you've discovered, requires boilerplate for each prop and can easily lead to performance issues. IMO it's also poor developer experience to expect users to serialize well-formed JSON in plain HTML (think of nested objects, having to escape quotes, etc.).

@claviska
Thanks for your comment.
I understand the decorators are just metadata, that's quite obvious.
But I also know that they will be added to the javascript code that we write, that's the whole point of using them.
The same way we have Prop and it works, in theory, we can have any other decorator and it should work.
It's extremely inconvenient, for us and I am sure for many others, have to do it much time in many pages that for every component, one should find the reference to that component and add set them with javascript. This is the complete opposite of using web components and the convenience they give us.

Isn't it poor user experince to expect the users to run document.getElement etc to find each component they use and inject those complex object to them, and update them as well ?

I understand the decorators are just metadata, that's quite obvious.
But I also know that they will be added to the javascript code that we write, that's the whole point of using them.

When I said they're used for metadata, I meant they're used to provide metadata to the _compiler_. The parser ultimately removes the decorators so they're not processed at runtime, which is why your example doesn't work.

Isn't it poor user experince to expect the users to run document.getElement etc to find each component they use and inject those complex object to them, and update them as well ?

No native HTML element parses JSON, so IMO it's a worse experience to expect users to hand type well-formed JSON strings into an HTML attribute, especially considering the requirement to escape certain characters like quotes. If you're working in vanilla HTML+JS, obtaining a reference to the element and assigning properties is the recommended way to do this.

Performance is the other issue. Do you really want your component parsing potentially lengthy JSON strings _every time_ a prop changes? Inevitably, someone's app will sync a prop to something that changes very frequently (e.g. a color picker selection, a resize observer, etc.) and the browser will lag as it tries to keep up.

I appreciate the desire to make this easier for users. Many of us have been down that road before. My takeaway has consistently been that using a reference in vanilla HTML+JS avoids unnecessary boilerplate and performance issues this approach will bring.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bekliev picture bekliev  Â·  3Comments

joewoodhouse picture joewoodhouse  Â·  3Comments

romulocintra picture romulocintra  Â·  3Comments

MrMcGibblets picture MrMcGibblets  Â·  3Comments

elmariofredo picture elmariofredo  Â·  3Comments