Dazed
Dazed

Reputation: 1259

How to get the object created by customElement in a Web Component?

I am building a Web Component to be used in a Framework, which embeds a grid libary.

I have managed to get the grid to display by wrapping it in an HTMLElement Class

export default class DataGrid extends HTMLElement {    

    constructor() {
        super(); 

        let tmpl = document.createElement('template');
        tmpl.innerHTML = `
            <div id="lib-datagrid"></div>
        `;

        this._shadowRoot = this.attachShadow({mode: "open"});
        this._shadowRoot.appendChild(tmpl.content.cloneNode(true));

        this._rowData = [];

        ...
}

and

    // Load the grid
    customElements.define('my-grid', DataGrid);     

I need to be able to pass data into the Grid via a DataGrid instance. However it seems that createElements.define() takes a Class rather than an object instance, so I don't have the option to create an Instance (new DataGrid()) and pass that in.

My theory is that I should be able to retrieve the created element via the dom tree, but my Web Component lives within a Shadow Dom and my Web Component isn't itself a Dom element (I think) i.e. no "this.getRootNode()" etc, but do have access to document and window.

enter image description here

Am I missing something in the createElement process or is there a way to find the root node of the current shadow dom?

** Edit - adding Top level WebComponent view

export default (state) => {
const { items, alert, loading } = state;
return (
    <div>       
        <div className="card-top">
            <my-grid></my-grid>
        </div>
    </div>
);

};

** Edit 2

I have found that coding the class extends HTMLElement in-line (rather than in a seperate js file) does allow me to update a reference from the objects connectedCallback() function.

    let myobject = null;
    
    customElements.define('my-grid2', class extends HTMLElement {
        connectedCallback() {
          const shadow = this.attachShadow({mode: 'open'});
          shadow.innerHTML = `<p>
            Hello
          </p>`;

          myobject = this;
        }
      });  
    

Pending other suggestions - I will work with this and post an answer if it works out.

Upvotes: 3

Views: 2813

Answers (4)

connexo
connexo

Reputation: 56773

Note that as soon as you have access to an instance of your custom element, you can simply use constructor theft to create new instances:

// let's say you have a reference to `<my-grid />` in `el`:
const newEl = new el.constructor;

Upvotes: 2

Just to save you typing

You can write this:

connectedCallback() {

        let tmpl = document.createElement('template');
        tmpl.innerHTML = `
            <div id="lib-datagrid"></div>
        `;

        const shadow = this.attachShadow({mode: 'open'});
        shadow.appendChild(tmpl.content.cloneNode(true));

        myDataGrid = new DataGrid(shadow);
        myElement = this;
    }

as:

connectedCallback() {

        this.attachShadow({mode: 'open'}).innerHTML = `<div></div>`;

        myDataGrid = new DataGrid(this.shadowRoot);
        myElement = this;
    }

Upvotes: 0

Dazed
Dazed

Reputation: 1259

I have found a way to do this.

By defining the HTMLElement class in-line and handling the grid wrapper object in the connectedCallback() function, I can get access to references for both the element created and the DataGrid wrapper object.

All the DataGrid wrapper requires is the shadowRoot created as a constructor parameter.

let myElement = null;
let myDataGrid = null;

// Load the grid
customElements.define('my-grid', class extends HTMLElement {
    connectedCallback() {

        let tmpl = document.createElement('template');
        tmpl.innerHTML = `
            <div id="lib-datagrid"></div>
        `;

        const shadow = this.attachShadow({mode: 'open'});
        shadow.appendChild(tmpl.content.cloneNode(true));

        myDataGrid = new DataGrid(shadow);
        myElement = this;
    }
  });    
    

With that I can now, later on, call a function on the DataGrid object to update data.

Upvotes: 0

Harshal Patil
Harshal Patil

Reputation: 21030

After customElements.define('my-grid', DataGrid), you have to actually create an element using:

const elm = document.createElement('my-grid');
const orElm = new DataGrid();

// Use `elm` or `orElm` to pass the data.

If your web component is added to DOM tree by some other means, then you must locate it using querySelector or equivalent like:

const elm = document.querySelector('my-grid');

But if it is nested within some other shadow-root, then querySelector won't be able to do it. For this purpose, you would have to precisely find the parent element, get its shadow root and then run querySelector on that shadow root, something like:

const shadowroot = parentElement.shadowRoot;
shadowroot.querySelector('my-grid');

On a side note, if you need to query or send the data from outside your web component, then it is probably code smell as you are breaking laws of encapsulation. There are other better ways to pass the data to the child component. Or else, you don't need shadow DOM API. Just use custom elements without shadow DOM.

Upvotes: 1

Related Questions