Reputation: 1259
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
.
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
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
Reputation: 21193
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
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
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