Example #1
0
/*
 Given a path name, returns whether or not a component with that
 name was found in the container.
*/
export default function isComponent(env, scope, path) {
  let owner = env.owner;
  if (!owner) { return false; }
  if (typeof path === 'string') {
    if (CONTAINS_DOT_CACHE.get(path)) {
      let stream = env.hooks.get(env, scope, path);
      if (isStream(stream)) {
        let cell = stream.value();
        if (isComponentCell(cell)) {
          return true;
        }
      }
    }
    if (!CONTAINS_DASH_CACHE.get(path)) { return false; }

    if (hasComponentOrTemplate(owner, path)) {
      return true; // global component found
    } else {
      if (isEnabled('ember-htmlbars-local-lookup')) {
        let moduleName = env.meta && env.meta.moduleName;

        if (!moduleName) {
          // Without a source moduleName, we can not perform local lookups.
          return false;
        }

        let options = { source: `template:${moduleName}` };

        return hasComponentOrTemplate(owner, path, options);
      } else {
        return false;
      }
    }
  }
}
Example #2
0
export default function linkRenderNode(renderNode, env, scope, path, params, hash) {
  if (renderNode.streamUnsubscribers) {
    return true;
  }

  let keyword = env.hooks.keywords[path];
  if (keyword && keyword.link) {
    keyword.link(renderNode.getState(), params, hash);
  } else {
    switch (path) {
      case 'unbound': return true;
      case 'unless':
      case 'if': params[0] = shouldDisplay(params[0], toBool); break;
      case 'each': params[0] = eachParam(params[0]); break;
      case 'with': params[0] = shouldDisplay(params[0], identity); break;
    }
  }

  // If there is a dot in the path, we need to subscribe to the arguments in the
  // closure component as well.

  if (CONTAINS_DOT_CACHE.get(path)) {
    let stream = env.hooks.get(env, scope, path);
    let componentCell = stream.value();

    if (isComponentCell(componentCell)) {
      let closureAttrs = mergeInNewHash(componentCell[COMPONENT_HASH], hash);

      for (let key in closureAttrs) {
        subscribe(renderNode, env, scope, closureAttrs[key]);
      }
    }
  }

  if (params && params.length) {
    for (let i = 0; i < params.length; i++) {
      subscribe(renderNode, env, scope, params[i]);
    }
  }

  if (hash) {
    for (let key in hash) {
      subscribe(renderNode, env, scope, hash[key]);
    }
  }

  // The params and hash can be reused. They don't need to be
  // recomputed on subsequent re-renders because they are
  // streams.
  return true;
}
Example #3
0
export default function componentHook(renderNode, env, scope, _tagName, params, _attrs, templates, visitor) {
  var state = renderNode.getState();

  let tagName = _tagName;
  let attrs = _attrs;
  if (isAngle(tagName)) {
    tagName = tagName.slice(1, -1);
    let block = buildHTMLTemplate(tagName, attrs, { templates, scope });
    block.invoke(env, [], undefined, renderNode, scope, visitor);
    return;
  }

  if (CONTAINS_DOT_CACHE.get(tagName)) {
    let stream = env.hooks.get(env, scope, tagName);
    let componentCell = stream.value();
    if (isComponentCell(componentCell)) {
      tagName = componentCell[COMPONENT_PATH];

      /*
       * Processing positional params before merging into a hash must be done
       * here to avoid problems with rest positional parameters rendered using
       * the dot notation.
       *
       * Closure components (for the contextual component feature) do not
       * actually keep the positional params, but process them at each level.
       * Therefore, when rendering a closure component with the component
       * helper we process the parameters and attributes and then merge those
       * on top of the closure component attributes.
       *
       */
      let newAttrs = assign(new EmptyObject(), attrs);
      processPositionalParamsFromCell(componentCell, params, newAttrs);
      params = [];
      attrs = mergeInNewHash(componentCell[COMPONENT_HASH], newAttrs);
    }
  }

  // Determine if this is an initial render or a re-render.
  if (state.manager) {
    let sm = state.manager;
    let templateMeta = null;
    if (sm.block) {
      templateMeta = sm.block.template.meta;
    } else if (sm.scope && sm.scope._view) {
      templateMeta = sm.scope._view.template.meta;
    }
    env.meta.moduleName = (templateMeta && templateMeta.moduleName) || (env.meta && env.meta.moduleName);
    extractPositionalParams(renderNode, sm.component.constructor, params, attrs, false);
    state.manager.rerender(env, attrs, visitor);
    return;
  }

  let parentView = env.view;
  let options = { };
  let moduleName = env.meta && env.meta.moduleName;
  if (moduleName) {
    options.source = `template:${moduleName}`;
  }
  let { component, layout } = lookupComponent(env.owner, tagName, options);

  assert(`HTMLBars error: Could not find component named "${tagName}" (no component or template with that name was found)`, !!(component || layout));

  let manager = ComponentNodeManager.create(renderNode, env, {
    tagName,
    params,
    attrs,
    parentView,
    templates,
    component,
    layout,
    parentScope: scope
  });

  state.manager = manager;
  manager.render(env, visitor);
}
Example #4
0
export default function componentHook(renderNode, env, scope, _tagName, params, attrs, templates, visitor) {
  var state = renderNode.getState();

  let tagName = _tagName;
  if (CONTAINS_DOT_CACHE.get(tagName)) {
    let stream = env.hooks.get(env, scope, tagName);
    let componentCell = stream.value();
    if (isComponentCell(componentCell)) {
      tagName = componentCell[COMPONENT_PATH];

      /*
       * Processing positional params before merging into a hash must be done
       * here to avoid problems with rest positional parameters rendered using
       * the dot notation.
       *
       * Closure components (for the contextual component feature) do not
       * actually keep the positional params, but process them at each level.
       * Therefore, when rendering a closure component with the component
       * helper we process the parameters and attributes and then merge those
       * on top of the closure component attributes.
       *
       */
      let newAttrs = assign(new EmptyObject(), attrs);
      processPositionalParamsFromCell(componentCell, params, newAttrs);
      params = [];
      attrs = mergeInNewHash(componentCell[COMPONENT_HASH], newAttrs);
    }
  }

  // Determine if this is an initial render or a re-render.
  if (state.manager) {
    let templateMeta = state.manager.block.template.meta;
    env.meta.moduleName = (templateMeta && templateMeta.moduleName) || (env.meta && env.meta.moduleName);
    state.manager.rerender(env, attrs, visitor);
    return;
  }


  let isAngleBracket = false;
  let isTopLevel = false;
  let isDasherized = false;

  let angles = IS_ANGLE_CACHE.get(tagName);

  if (angles) {
    tagName = angles[2];
    isAngleBracket = true;
    isTopLevel = !!angles[1];
  }

  if (CONTAINS_DASH_CACHE.get(tagName)) {
    isDasherized = true;
  }

  let parentView = env.view;

  // | Top-level    | Invocation: <foo-bar>    | Invocation: {{foo-bar}}  |
  // ----------------------------------------------------------------------
  // | <div>        | <div> is component el    | no special semantics (a) |
  // | <foo-bar>    | <foo-bar> is identity el | EWTF                     |
  // | <bar-baz>    | recursive invocation     | no special semantics     |
  // | {{anything}} | EWTF                     | no special semantics     |
  //
  // (a) needs to be implemented specially, because the usual semantics of
  //     <div> are defined by the compiled template, and we need to emulate
  //     those semantics.

  let currentComponent = env.view;
  let isInvokedWithAngles = currentComponent && currentComponent._isAngleBracket;
  let isInvokedWithCurlies = currentComponent && !currentComponent._isAngleBracket;

  // <div> at the top level of a <foo-bar> invocation.
  let isComponentHTMLElement = isAngleBracket && !isDasherized && isInvokedWithAngles;

  // <foo-bar> at the top level of a <foo-bar> invocation.
  let isComponentIdentityElement = isAngleBracket && isTopLevel && tagName === env.view.tagName;

  // <div> at the top level of a {{foo-bar}} invocation.
  let isNormalHTMLElement = isAngleBracket && !isDasherized && isInvokedWithCurlies;

  let component, layout;
  if (isDasherized || !isAngleBracket) {
    let options = { };
    if (isEnabled('ember-htmlbars-local-lookup')) {
      let moduleName = env.meta && env.meta.moduleName;

      if (moduleName) {
        options.source = `template:${moduleName}`;
      }
    }

    let result = lookupComponent(env.owner, tagName, options);

    component = result.component;
    layout = result.layout;

    if (isAngleBracket && isDasherized && !component && !layout) {
      isComponentHTMLElement = true;
    } else {
      assert(`HTMLBars error: Could not find component named "${tagName}" (no component or template with that name was found)`, !!(component || layout));
    }
  }

  if (isComponentIdentityElement || isComponentHTMLElement) {
    // Inside the layout for <foo-bar> invoked with angles, this is the top-level element
    // for the component. It can either be `<foo-bar>` (the "identity element") or any
    // normal HTML element (non-dasherized).
    let templateOptions = {
      component: currentComponent,
      tagName,
      isAngleBracket: true,
      isComponentElement: true,
      outerAttrs: scope.getAttrs(),
      parentScope: scope
    };

    let contentOptions = { templates, scope };

    let { block } = buildComponentTemplate(templateOptions, attrs, contentOptions);
    block.invoke(env, [], undefined, renderNode, scope, visitor);
  } else if (isNormalHTMLElement) {
    let block = buildHTMLTemplate(tagName, attrs, { templates, scope });
    block.invoke(env, [], undefined, renderNode, scope, visitor);
  } else {
    // Invoking a component from the outside (either via <foo-bar> angle brackets
    // or {{foo-bar}} legacy curlies).

    var manager = ComponentNodeManager.create(renderNode, env, {
      tagName,
      params,
      attrs,
      parentView,
      templates,
      isAngleBracket,
      isTopLevel,
      component,
      layout,
      parentScope: scope
    });

    state.manager = manager;
    manager.render(env, visitor);
  }
}