Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iterating properties and their options #1102

Closed
GHNewbiee opened this issue Nov 7, 2020 · 4 comments
Closed

Iterating properties and their options #1102

GHNewbiee opened this issue Nov 7, 2020 · 4 comments

Comments

@GHNewbiee
Copy link

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

@sorvell
Copy link
Member

sorvell commented Nov 12, 2020

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.

@sorvell sorvell closed this as completed Nov 12, 2020
@GHNewbiee
Copy link
Author

GHNewbiee commented Nov 13, 2020

... 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
Copy link
Author

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
Copy link
Member

sorvell commented Dec 3, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants