Skip to content

Proposal: default/fallback values for properties #905

Closed
@javier-garcia-meteologica

Description

Problem

Many of our bugs are related to people believing that the initialization value is a default value.

@component('filter')
export class Filter extends LitElement {
  @property({ type: Number })
  min: number = -Infinity;

  @property({ type: Number })
  max: number = Infinity;

  @property({ type: Number })
  decimals: number = 0;

  ...

  onSelectorChanged (e: CustomEvent) {
     const value = e.detail.value;
     // Everyone expects min and max to be defined numbers, never to be undefined
     // But when it is undefined, all hell breaks loose
     if (value < this.min || value > this.max) {
       ...
     }
     this.value = value;
  }
}

So they write code, that works. But it is very ugly and repetitive.

  get _max () {
    return this.max !== undefined ? this.max : Infinity;
  }
  get _min () {
    return this.min !== undefined ? this.min : -Infinity;
  }

  onSelectorChanged (e: CustomEvent) {
     const value = e.detail.value;
     if (value < this._min || value > this._max) {
       ...
     }
     this.value = value;
  }

Why are so many variables set as undefined despite having an initialization value? Because of abstractions. Let's say we have a filters property with many dynamic filters to be rendered.

get filtersTemplate () {
  return this.filters.map(filter => {
    return html`<filter
      .min="${filter.min}" 
      .max="${filter.max}" 
      .decimals="${filter.decimals}" >
    </filter>`
  });
}

Any filter property that is undefined will override the initialization value of our filter component. The following syntax would be great, but it's not legal.

// This wil throw
html`<filter ${ filter.min !== undefined ? 'min="${filter.min}"' : ''}"></filter>`

Proposal

We use a patched version of lit-element that allows the following syntax.

@component('filter')
export class Filter extends LitElement {
  @property({ type: Number, default: () => -Infinity })
  min: number;

  @property({ type: Number, default: () => Infinity })
  max: number;

  @property({ type: Number, default: () => ({ foo: 'bar' }) })
  extraData: number;

  ...

  onSelectorChanged (e: CustomEvent) {
     const value = e.detail.value;
    // Works as expected
     if (value < this.min || value > this.max) {
       ...
     }
     this.value = value;
  }
}

Whenever a property would become undefined, the value is overridden with the default value.

Take a look at branch default_values in my repo.

I've also opened this pull request to discuss implementation details #906

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions