lit/lit-element

Iterating properties and their options

GHNewbiee opened this issue · 4 comments

Description

  • Who wants the functionality
    I do, GPT GHNewbiee.

  • What it is they want
    I want to iterate the name (as a string) and the (attribute, type - at least) options of each property of a class to calculate other values

  • Why they want it
    It makes my life easier!

  • Functional description of task/subtask

    Example

@customElement('my-element')
export class MyElement extends LitElement {
  @property1({attribute: 'my-property-1', type: String}) myProperty1;
  @property2({attribute: 'my-property-2', type: Number}) myProperty2;
  @property3({attribute: 'my-property-3', type: String}) myProperty3;
  ...
  @propertyN({attribute: 'my-property-N', type: Number}) myPropertyN;

  construction() {
    super();
    const list = listProperties();
    /**
    / For each property of type `String` in `list` do something
    / For each property of type `Number` in `list` do something
    / ...
    / Aggregate all properties of type `String` which are not `''`, `null`, or `undefined` and
    /   create a dynamic css string for `classMap` or `styleMap`
    / ...
   */
  }
}

Acceptance criteria

  • What the card must do in order to accept it as complete. Acceptance Criteria must be concrete or measurable.
    I think a method like listProperties() which returns an array of objects in the same order that the properties are declared, like:
[
  { name: 'myProperty1', attribute: 'my-property-1', ..., type: 'String' },
  { name: 'myProperty2', attribute: 'my-property-2', ..., type: 'Number' },
  { name: 'myProperty3', attribute: 'my-property-3', ..., type: 'String' },
  ...
  { name: 'myPropertyN', attribute: 'my-property-N', ..., type: 'Number' }
]

would be fine. Tia

Currently we have static getPropertyOptions(propertyName) but we don't expose the list of properties actually defined on the element. We're very likely going to make this list public in the next major version of lit-element.

In the meantime, you can make a LitElement subclass that overrides static createProperty and stores the list of properties created.

// Note, the Map is pre-populated with the superclass map
static elementProperties = new Map(Object.getPrototypeOf(this).elementProperties ?? [])

static createProperty(name, options) {
  this.elementProperties.push(name, options);
  super.createProperty(name, options);
}

Hope that helps.

... We're very likely going to make this list public in the next major version of lit-element.

That's great news!

In the meantime ...

I have tried as follows:

NewElement.js

import { LitElement } from 'lit-element';

export class NewElement extends LitElement {
  // Note, the Map is pre-populated with the superclass map
  static elementProperties = new Map(Object.getPrototypeOf(this).elementProperties ?? []);

  static createProperty(name, options) {
    this.elementProperties.push(name, options);
    super.createProperty(name, options);
  }
}

But it gives

TypeError: undefined is not an object (evaluating 'Object.getPrototypeOf(this)')

Next, something like the following is ok?

example.js

import { NewElement } from './NewElement';
import { customElement, property, html } from 'lit-element';

@customElement('my-example')
export class MyExample extends NewElement {
  @property({
    attribute: true,
    noAccessor: false,
    reflect: false,
    type: String
  }) aProperty;

  constructor() {
    super();
  }

  render() {
    return html`
      <div>${this.elementProperties}</div>
    `;
  }
}

Tia!

This is how it works for me:

NewElement.js

import { LitElement } from 'lit-element';

export class NewElement extends LitElement {
  self = this;

  // Note, the Map is pre-populated with the superclass map
  static elementProperties = new Map(Object.getPrototypeOf(self).elementProperties ?? []);  // `self` instead of `this`

  static createProperty(name, options) {
    this.elementProperties.set(name, options);  // `set` instead of `push`
    super.createProperty(name, options);
  }

  static get properties() {
    return {
      greeting0: {type: String},
      data0: {attribute: false},
      items0: {type: Array},
    };
  }
}

example.js

import { NewElement } from './NewElement';
import { customElement, html } from 'lit-element';

@customElement('my-example')
export class MyExample extends NewElement {
  static get properties() {
    return {
      greeting1: {type: String},
      data1: {attribute: false},
      items1: {type: Array},
    };
  }

  constructor() {
    super();
  }

  render() {
    return html`
      <div>${this.constructor.elementProperties}</div>
    `;
  }
}

Finally getting the following proof of working:

greeting0[object Object]data0[object Object]items0[object Object]greeting1[object Object]data1[object Object]items1[object Object]

Note: For using @property ({...}) ... ; instead of static get properties() {...} see the response of this issue.

The example above wasn't quite correct. Here's a working example: https://stackblitz.com/edit/lit-element-typescript-demo?file=my-element.ts.

Hope that helps.