Using shadow dom?

I’m working on an email client and trying to keep the css styling that comes in the body data of the email from conflicting with the rest of my app.

There are three potential solutions.

  1. web component with shadow dom
  2. iframe
  3. Parse the body html and customize all class and ids throughout the html so they are unique.

I feel going the shadow dom route is the best option and I’m almost there, but I’m running into an issue with dmx-html. For some reason it is adding the code to the page, but it’s not being displayed. I think it’s because dmx-html doesn’t work in the way I want it. It adds the code below the #shadow-root instead of in it.

If I hardcode it with some test html, then it displays fine.

Here’s an example where I’ve hardcode the container.innerHTML

const template = document.createElement('template');
template.innerHTML = '<div id="email-body-container"></div>';

class EmailBodyComponent extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.appendChild(template.content.cloneNode(true));
  }

  connectedCallback() {
    const container = this.shadowRoot.getElementById('email-body-container');
    const htmlContent = this.getAttribute('html-body');
    container.innerHTML = '<div><p>hello world</p></div>';
  }
}

customElements.define('email-body', EmailBodyComponent);

So, is there a way to use something like dmx-html to pass an expresssion into the shadow dom?

This is an example of what I’ve used prior to attempting to use a shadow dom.
<email-body dmx-show="payload.parts.where('mimeType', 'text/html', '==')[0].mimeType" dmx-html="payload.parts.where('mimeType', 'text/html' , '==' )[0].body.data.decodeBase64()"></email-body>

Welp, I’m further along. I made some changes to the web component’s js and had to use dmx-bind instead of dmx-html, but I don’t like how dmx-bind continues to show all of the code. I’m wondering if there’s another way to pass the epxressions data without having to show it in the code with dmx-bind.

Here a way using App Connect

For App Connect 1:

dmx.Component('shadow', {
  attributes: {
    content: {
      type: 'string',
      default: ''
    }
  },

  render (node) {
    this.shadowRoot = node.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = this.props.content;
  },

  update (props) {
    if (props.content !== this.props.content) {
      this.shadowRoot.innerHTML = this.props.content;
    }
  }
});

For App Connect 2 Beta:

dmx.Component('shadow', {
  attributes: {
    content: {
      type: 'string',
      default: ''
    }
  },

  init () {
    this._shadowRoot = this.$node.attachShadow({mode: 'open'});
  },

  render () {
    this._shadowRoot.innerHTML = this.props.content;
  },

  performUpdate (updatedProps) {
    if (updatedProps.has('content')) {
      this.render();
    }
  }
});

Usage with your code as example:

<dmx-if dmx-bind:condition="payload.parts.where('mimeType', 'text/html', '==')">
  <dmx-shadow dmx-bind:content="payload.parts.where('mimeType', 'text/html' , '==' )[0].body.data.decodeBase64()"></dmx-shadow>
</dmx-if>
2 Likes

Wow! Thank you, Patrick!!! You’re a genius. :slight_smile:

This also helps provide another example for writing components.