Atom duplicating my view element


#1

I’m developing a package, and I want to have a custom view displayed in a normal tab. I’ve got the following code (bare minimum) to create the view:

'use babel';

class DevmailView extends HTMLDivElement {

  initialize(uri) {
    this.uri = uri;
    this.innerHTML = 'no render yet';
    this.classList.add('devmail');
    this.setAttribute('data-uri', uri);
    return this;
  }

  getTitle() {
    return this.title || `Devmail - ${this.uri}`;
  }

  getLongTitle() {
    return this.getTitle();
  }


}

module.exports = document.registerElement('devmail-root', { prototype: DevmailView.prototype });

When I trigger the view via the URI, it works as intended. However, if I switch to another tab in the same pane and then back, it hides the view I just created and creates a new instance; see GIF:

Weirdly, only the second element has the data-uri attribute I’m adding in the constructor, but only the first one has the rendered content. Also, I have some log statements in the URI handler that only get printed when the initial (correct) view gets drawn. This has been frustrating me for literally hours; what am I doing wrong?

Here is the URI handler I set up:

  function handleUri(uri) {
    const m = uri.match(DEVMAIL_REGEX);
    console.info('## handleUri', m);
    if (m) {
      let view = this.views.get(id);
      console.log('## extant view',view);
      const [, id] = m;
      if (view) {
        return view;
      }
      view = document.createElement('devmail-root').initialize(uri);
      console.log('## new view', view, id);
      this.drivers.tabs.documentCreated(id, view);
      this.views.set(id, view);
      return view;
    }
  },

#2

Solved it! I only had this problem because I didn’t understand how the view library I’m using updated the DOM element I passed it, but hopefully posting the solution here will help someone else.

The issue, I think, is that the render library I’m using replaces the DOM element passed in with the desired render output, as opposed to React which fills the passed DOM element with your rendered content. Here’s a fixed version of my class above:

'use babel';

class DevmailView extends HTMLDivElement {

  initialize(uri) {
    this.uri = uri;
    this.renderTarget = document.createElement('div');
    this.renderTarget.innerHTML = 'no render yet';
    this.classList.add('devmail');
    this.setAttribute('data-uri', uri);
    this.appendChild(this.renderTarget);
    return this;
  }

  getTitle() {
    return this.title || `Devmail - ${this.uri}`;
  }

  getLongTitle() {
    return this.getTitle();
  }


}

module.exports = document.registerElement('devmail-root', { prototype: DevmailView.prototype });