lit / lit-element

LEGACY REPO. This repository is for maintenance of the legacy LitElement library. The LitElement base class is now part of the Lit library, which is developed in the lit monorepo.
https://lit-element.polymer-project.org
BSD 3-Clause "New" or "Revised" License
4.49k stars 319 forks source link

Iterating properties and their options #1102

Closed GHNewbiee closed 3 years ago

GHNewbiee commented 4 years ago

Description

Acceptance criteria

sorvell commented 3 years ago

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.

GHNewbiee commented 3 years ago

... 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!

GHNewbiee commented 3 years ago

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.

sorvell commented 3 years ago

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.