Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot render component cards in FastBoot #89

Closed
habdelra opened this issue Jun 3, 2016 · 2 comments
Closed

Cannot render component cards in FastBoot #89

habdelra opened this issue Jun 3, 2016 · 2 comments

Comments

@habdelra
Copy link
Contributor

habdelra commented Jun 3, 2016

This is kinda related to issue #50, but a very specific scenario. For FastBoot we use the mobiledoc DOM renderer with SimpleDOM. This means that we are not using this particular component to render our mobiledoc component cards https://github.com/bustlelabs/ember-mobiledoc-dom-renderer/blob/master/addon/components/render-mobiledoc.js (we do use this component to render component when running from the browser though). As such, when constructing the renderer, we need to provide cardOptions.addComponent to instruct the renderer on how to assemble the DOM for the component card, as cards are rendered thusly: https://github.com/bustlelabs/ember-mobiledoc-editor/blob/master/addon/utils/create-component-card.js#L22.

Sadly, it's not possible to generate the DOM for an Ember component in this manner using public API's.

For reference, our renderer for fastboot looks something like this:

import Ember from 'ember';
import Renderer from 'ember-mobiledoc-dom-renderer';
import Component from 'ember-component';
import computed from 'ember-computed';
import inject from 'ember-service/inject';
import createComponentCard from 'ember-mobiledoc-editor/utils/create-component-card';
import { optimizedUrl } from '../services/cloudinary';

export const cards = ['image-card', 'images-card', 'video-card'];

const { uuid } = Ember;
const { camelize } = Ember.String;

const CARD_ELEMENT_CLASS = '__rendered-mobiledoc-card';
const CARD_TAG_NAME = 'div';
const UUID_PREFIX = '__rendered-mobiledoc-entity-';

export default Component.extend({
  tagName: '',
  cardNames: cards,
  fastboot: inject(),
  isFastBoot: computed.readOnly('fastboot.isFastBoot'),

  // When in FastBoot mode, where rendering the Mobiledoc is a one-shot
  // operation, we can build the DOM directly and return the serialized value
  // as this computed property. This lets us insert the raw HTML into the
  // rendered output.
  renderedMobiledoc: computed(function() {
    let _this = this;
    let SimpleDOM = FastBoot.require('simple-dom');
    let doc = new SimpleDOM.Document();

    let cards = this.get('cardNames').map(cardName => createComponentCard(cardName, doc));
    let mobiledoc = this.get('mobiledoc');

    let renderer = new Renderer({
      cards,
      dom: doc,
      cardOptions: {
        addComponent({env, options, payload}) {
          let { name: cardName, dom } = env;
          let domForCard = camelize('domFor-' + cardName);
          // it is not possible to generate DOM for an Ember component without using
          // a template so we need to hand-craft the mobiledoc card components
          // when in the fastboot runtime
          let element = _this[domForCard](dom, payload);
          let card = {
            cardName,
            destinationElementId: element.getAttribute('id'),
            payload
          };

          return { card, element };
        },

        removeComponent: Ember.K
      }
    });
    let { result, teardown } = renderer.render(mobiledoc);

    let HTMLSerializer = new SimpleDOM.HTMLSerializer(SimpleDOM.voidMap);
    let serialized = HTMLSerializer.serialize(result);

    // Immediately teardown once we've serialized
    teardown();

    return serialized;
  }),

  domForImagesCard(doc, payload) {
    let classNames = [CARD_ELEMENT_CLASS, `${CARD_ELEMENT_CLASS}-images-card`];
    let parent = createElement(doc, CARD_TAG_NAME, classNames);

    let el1 = createElement(doc, 'div', ['images-card']);
    let el2 = createElement(doc, 'div');
    el1.appendChild(el2);

    payload.images.forEach(image => {
      let div = createElement(doc, 'div');
      let img = createElement(doc, 'img');
      img.setAttribute('src', optimizedUrl(image.url));
      div.appendChild(img);
      el2.appendChild(div);
    });
    parent.appendChild(el1);

    if (payload.caption) {
      let p = createElement(doc, 'p', ['caption']);
      let caption = doc.createTextNode(payload.caption);
      p.appendChild(caption);
      parent.appendChild(p);
    }

    return parent;
  },

  domForVideoCard(doc, payload) {
    let classNames = [CARD_ELEMENT_CLASS, `${CARD_ELEMENT_CLASS}-video-car`];
    let parent = createElement(doc, CARD_TAG_NAME, classNames);

    let posterUrl = payload.posterUrl;
    let el1 = createElement(doc, 'div', ['video-card']);
    let el2 = createElement(doc, 'div', ['lazyLoad-container']);

    if (posterUrl) {
      el2.setAttribute('style', `background-image:url("${optimizedUrl(posterUrl)}")`);
    }
    el1.appendChild(el2);
    parent.appendChild(el1);

    return parent;
  }

});

function generateUuid() {
  return `${UUID_PREFIX}${uuid()}`;
}

function createElement(dom, tagName, classNames=[]) {
  let el = dom.createElement(tagName);
  el.setAttribute('id', generateUuid());
  el.setAttribute('class', classNames.join(' '));
  return el;
}

export function buildMobiledoc(initialString="") {
  return {
    "version": "0.3.0",
    "atoms": [],
    "cards": [],
    "markups": [],
    "sections": [
      [
        1,
        "p",
        [
          [
            0,
            [],
            0,
            initialString
          ]
        ]
      ]
    ]
  };
}
@mixonic
Copy link
Contributor

mixonic commented Jun 3, 2016

@habdelra hm, this seems unrelated to the editor and more to https://github.com/bustlelabs/ember-mobiledoc-dom-renderer?

The mobiledoc-dom-renderer takes a dom argument which is then available on the env passed to the card hook. You should be able to thread through a document or simple dom instance there, and then your cards can be generic. I'll ping you on Slack.

@mixonic
Copy link
Contributor

mixonic commented Jun 9, 2016

@habdelra as we discussed on that hangout last week, you should have no problem getting fastboot happy with the ember-mobiledoc-dom-renderer 0.5.2 (with ember-wormhole 0.4.0, fastboot compat). Please give it a try!

Closing this for now but we can reopen if something was missed :-)

@mixonic mixonic closed this as completed Jun 9, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants