4

I am creating a web component and I want to display selected Items. There is a parent component which fetches data form an api and creates a component filled with that data. This is a very slimmed down version, but you should get the issue.

Parent Component looks like

const myComponent = document.createElement('template');

class MyComponent extends HTMLElement {
    render(el) {
        if (this.selectedItems.length === 0) {
            selectedContent = `Nothing selected!`;
        } else {
            selectedContent = `<p>Selected:</p>`;
            selectedContent += `<ul>`;
            for (let item of this.selectedItems) {
                selectedContent += `<li>${item.name}</li>`;
            }
            selectedContent += `</ul>`;
        }

        el.shadowRoot.innerHTML = `
            <div>
                <child-component data="${el._data}"></child-component>
                ${selectedContent}
            </div>`;
    }

    constructor() {
        super();

        let self = this;

        self.myService= new MyService();
        self.myItemsList= [];
        self.selectedItems = [];
        self.attachShadow({mode: 'open'});

        self.fetchData(self);
        self.setupEventListeners(self);
    }

    async fetchData(self) {
        try {
            const response = await self.myService.getData();
            const items = response.data;
            items.forEach((item) => {
                self.myItemsList.push({item});
            });
            self.render(self);
        } catch (error) {
            console.log(error);
        }
    }

    setupEventListeners(self) {
        EventBus.addEventListener("my-custom-event", (event) => {
            const item = event.target;
            if (item.active === true) {
                    self.selectedItems = _.reject(self.selectedItems, (element) => {
                        return element.name === self.myItemsList[item.position].name;
                    });
                    self.myItemsList[item.position].checked = false;
            } else if (item.active === false) {
                    self.selectedItems.push(item);
                    self.myItemsList[item.position].checked = true;
            } 
            self.render(self);
        });
    }

    attributeChangedCallback(attrName, oldVal, newVal) {
        if (oldVal !== newVal) {
            this[`_${attrName}`] = newVal;
            this.render(this);
        }
    }

    connectedCallback() {
        this.render(this);
        this.appendChild(myComponent.content.cloneNode(true));
    }
}

window.customElements.define("bootstrap-card-parent", BootstrapCardParent);

Child Component:

const myComponentChild = document.createElement('template');


class MyChildComponent extends HTMLElement {
    render(el) {
        el.shadowRoot.innerHTML = `
            <div>${el._data}</div>
            <input type="checkbox"
onchange="MyChildComponent.onAddToSelectedRequest(${el._data})">`;
    }

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

    constructor() {
        super();
        this.attachShadow({mode: 'open'});
    }

    attributeChangedCallback(attrName, oldVal, newVal) {
        if (oldVal !== newVal) {
            this[`_${attrName}`] = newVal;
            this.render(this);
        }
    }

    connectedCallback() {
        this.render(this);
        this.appendChild(myComponentChild.content.cloneNode(true));
    }

    static onAddToSelectedRequest(someData) {
        EventBus.dispatch("my-custom-event", someData);
    }
}

window.customElements.define("my-child-component", MyChildComponent);

Currently I call self.render(self) in the EventListener to rerender the page to see my data. Isn't there any way to only rerender the content of the array of the selected Items?

Andreas
  • 430
  • 5
  • 21
  • Make your Child elements listen to events. See https://stackoverflow.com/questions/55001211/what-possible-ways-are-there-to-communicate-between-native-ui-web-components – Danny '365CSI' Engelman May 08 '19 at 10:08
  • 1
    If I understand correctly, you want to do the following: - Fetch a list of items - Populate an `
      ` with those items - Select items - Populate a `
        ` with selected items
        – StefanN May 08 '19 at 10:08
      • @StefanN Yeah, that's it! – Andreas May 08 '19 at 10:57
      • @Danny'365CSI'Engelman I don't understand how this question can help me – Andreas May 08 '19 at 12:09
      • You are already using Events, make you childelements listen to an event, the SO answer shows events aren't bound to strings, so your parent element can send a function as event... multiple ways of implementation.. but it is all about Events – Danny '365CSI' Engelman May 08 '19 at 12:11
      • 2
        I personally don't think that you need the parent component. It only fetches data and pass it to the child component. You can handle in your app too. Then you can create a more generic component (i.e. list-component) which handles displaying data and add a boolean parameter to mark items as selected. – StefanN May 08 '19 at 12:13
      • @StefanN Thank you for the advice. I know, I just wanted to encapsulate the logic from the rest of my app. – Andreas May 08 '19 at 12:15
      • Then I still suggest the same. Use the parent to push data to the child component, that handles displaying data – StefanN May 08 '19 at 12:22

      0 Answers0