Ismael Rodriguez
Ismael Rodriguez

Reputation: 339

It is possible that attributeChangedCallback get a object an object like a newVal?

I'd problem when I set a object y a property in webcomponents. In my code it's listening change in a property with attributeChangedCallback(attrName, oldVal, newVal) but when I receive a new property the value of newVal is equal to [Object object]. Why is it happening?

Thanks

Upvotes: 1

Views: 2197

Answers (4)

Rickard Elimää
Rickard Elimää

Reputation: 7591

From the documentation:

attributeChangedCallback (native)

Attributes must be explicitly registered to be observed.

For Polymer elements, only properties explicitly declared in the properties object are tracked for attribute changes. (That is, changing the attribute value invokes the attribute changed callback, and causes Polymer to set the property value from the attribute.)

https://polymer-library.polymer-project.org/2.0/docs/about_20

If you're getting [object object], I'm guessing that you need to declare the object in properties.


Also, I wouldn't use attributeChangedCallback myself, but instead put a complex observer on the property.

class XCustom extends Polymer.Element {

  static get is() {return 'x-custom'; }

  static get properties() {
    return {
      user: {
        type: Object,
        value: function() {
          return {};
        }
      }
    }
  }

  // Observe the name sub-property on the user object
  static get observers() {
    return [
        'userNameChanged(user.name)'
    ]
  }

  // For a property or sub-property dependency, the corresponding
  // argument is the new value of the property or sub-property
  userNameChanged(name) {
    if (name) {
      console.log('new name: ' + name);
    } else {
      console.log('user name is undefined');
    }

  }
}

https://polymer-library.polymer-project.org/2.0/docs/devguide/observers#simple-observers

Upvotes: 0

junvar
junvar

Reputation: 11574

Best practice suggests we handle attribute and property changes in the attributeChangedCallback method of HTMLElements. The distinction being attributes are set via html, e.g. <my-element size=3>; whereas properties are set in the js, e.g. myElement.size = 3.

The idea is, our HTMLElement subclasses would have a size setter that would set the attribute, and allow us to handle both cases in attributeChangedCallback, e.g.

class MyElement extends HTMLElement {
  ...

  get size() {
    return this.getAttribute('size');
  }

  set size(value) {
    this.setAttribute('size', value);
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'size') {
      // handle size update here
    }
  }
}

This seems like a good idea, in that it allows a single place to handle both possible methods of updating the size. In fact, this is encouraged by google's HTMLElement best practices guide

That being said, this is problematic given that HTML attributes can only handle strings. So then, we have 2 alternatives,

1) Disregard the best practices suggestion, which isn't totally unreasonable given HTMLElements are still a new thing and best practices aren't necessarily drawn from decades of experience working with them. Perhaps, we do the exact opposite, of delegating attribute changes to property changes, as this would still achieve google's suggested aim, 'avoid[ing] reentrancy issues` and having a single handler.

class MyElement extends HTMLElement {
  ...

  get size() {
    this.size_;
  }

  set size(value) {
    this.size_ = value;
     // handle size update here
  }

  attributeChangedCallback(name, oldValue, newValue) {
    this[name] = newValue;
  }
}

or 2) Rely on setters for properties that can be set to JS objects and attributeChangedCallback for properties that can be represented as strings.

class MyElement extends HTMLElement {
  ...

  get size() {
    return this.getAttribute('size');
  }

  set size(value) {
    this.setAttribute('size', value);
  }

  get myObj() {
    return this.myObj_;
  }

  set myObj(value) {
    this.myObj_ = value;
    // handle myObj update here
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'size') {
      // handle size update here
    }
  }
}

Upvotes: 2

Supersharp
Supersharp

Reputation: 31181

A HTML element attribute is always of type string.

If you want to pass a Javascript object, you should convert it to a JSON notation:

<custom-element attribute-name='{ "value": "v1" }'></custom-element>

You can then parse it attributeChangedCallback():

var o = JSON.parse( newVal )

Upvotes: 2

Intervalia
Intervalia

Reputation: 10945

Since you didn't provide any example code I can't figure out what is failing.

But here is a simple component that supports the attributeChangedCallback method.

If this code helps, great. But it would be nice to see your code to understand what is failing.

// Class for `<my-el>`
class MyEl extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }

  static get observedAttributes() {
    return ['name'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      this.shadowRoot.innerHTML = newVal;
    }
  }
}

// Define our web component
customElements.define('my-el', MyEl);
<my-el name="Frodo Baggins"></my-el>

Upvotes: 0

Related Questions