#

API Docs for: v2.11.1
Show: Inherited Protected Private Deprecated

# File: packages/ember-glimmer/lib/syntax/render.js

    /**
    @module ember
    @submodule ember-glimmer
    */
    import {
      ArgsSyntax,
      StatementSyntax,
      ComponentDefinition
    } from 'glimmer-runtime';
    import { ConstReference, isConst } from 'glimmer-reference';
    import { assert, runInDebug } from 'ember-metal';
    import { RootReference } from '../utils/references';
    import { generateController, generateControllerFactory } from 'ember-routing';
    import { OutletLayoutCompiler } from './outlet';
    import AbstractManager from './abstract-manager';

    function makeComponentDefinition(vm) {
      let env     = vm.env;
      let args    = vm.getArgs();
      let nameRef = args.positional.at(0);

      assert(`The first argument of {{render}} must be quoted, e.g. {{render "sidebar"}}.`, isConst(nameRef));
      assert(`The second argument of {{render}} must be a path, e.g. {{render "post" post}}.`, args.positional.length === 1 || !isConst(args.positional.at(1)));

      let templateName = nameRef.value();

      assert(`You used \`{{render '${templateName}'}}\`, but '${templateName}' can not be found as a template.`, env.owner.hasRegistration(`template:${templateName}`));

      let template = env.owner.lookup(`template:${templateName}`);

      let controllerName;

      if (args.named.has('controller')) {
        let controllerNameRef = args.named.get('controller');

        assert(`The controller argument for {{render}} must be quoted, e.g. {{render "sidebar" controller="foo"}}.`, isConst(controllerNameRef));

        controllerName = controllerNameRef.value();

        assert(`The controller name you supplied '${controllerName}' did not resolve to a controller.`, env.owner.hasRegistration(`controller:${controllerName}`));
      } else {
        controllerName = templateName;
      }

      if (args.positional.length === 1) {
        return new ConstReference(new RenderDefinition(controllerName, template, env, SINGLETON_RENDER_MANAGER));
      } else {
        return new ConstReference(new RenderDefinition(controllerName, template, env, NON_SINGLETON_RENDER_MANAGER));
      }
    }

    /**
      Calling ``{{render}}`` from within a template will insert another
      template that matches the provided name. The inserted template will
      access its properties on its own controller (rather than the controller
      of the parent template).

      If a view class with the same name exists, the view class also will be used.
      Note: A given controller may only be used *once* in your app in this manner.
      A singleton instance of the controller will be created for you.

      Example:

      ```javascript
      App.NavigationController = Ember.Controller.extend({
        who: "world"
      });
      ```

      ```handlebars
      <!-- navigation.hbs -->
      Hello, {{who}}.
      ```

      ```handlebars
      <!-- application.hbs -->
      <h1>My great app</h1>
      {{render "navigation"}}
      ```

      ```html
      <h1>My great app</h1>
      <div class='ember-view'>
        Hello, world.
      </div>
      ```

      Optionally you may provide a second argument: a property path
      that will be bound to the `model` property of the controller.
      If a `model` property path is specified, then a new instance of the
      controller will be created and `{{render}}` can be used multiple times
      with the same name.

      For example if you had this `author` template.

      ```handlebars
      <div class="author">
        Written by {{firstName}} {{lastName}}.
        Total Posts: {{postCount}}
      </div>
      ```

      You could render it inside the `post` template using the `render` helper.

      ```handlebars
      <div class="post">
        <h1>{{title}}</h1>
        <div>{{body}}</div>
        {{render "author" author}}
      </div>
      ```

      @method render
      @for Ember.Templates.helpers
      @param {String} name
      @param {Object?} context
      @param {Hash} options
      @return {String} HTML string
      @public
    */
    export class RenderSyntax extends StatementSyntax {
      static create(environment, args, symbolTable) {
        return new this(environment, args, symbolTable);
      }

      constructor(environment, args, symbolTable) {
        super();
        this.definitionArgs = args;
        this.definition = makeComponentDefinition;
        this.args = ArgsSyntax.fromPositionalArgs(args.positional.slice(1, 2));
        this.symbolTable = symbolTable;
        this.shadow = null;
      }

      compile(builder) {
        builder.component.dynamic(this.definitionArgs, this.definition, this.args, this.symbolTable, this.shadow);
      }
    }

    class AbstractRenderManager extends AbstractManager {
      prepareArgs(definition, args) {
        return args;
      }

      /* abstract create(environment, definition, args, dynamicScope); */

      layoutFor(definition, bucket, env) {
        return env.getCompiledBlock(OutletLayoutCompiler, definition.template);
      }

      getSelf({ controller }) {
        return new RootReference(controller);
      }

      getTag() {
        return null;
      }

      getDestructor() {
        return null;
      }

      didCreateElement() {}
      didRenderLayout() {}
      didCreate() {}
      update() {}
      didUpdateLayout() {}
      didUpdate() {}
    }

    runInDebug(() => {
      AbstractRenderManager.prototype.didRenderLayout = function() {
        this.debugStack.pop();
      };
    });

    class SingletonRenderManager extends AbstractRenderManager {
      create(environment, definition, args, dynamicScope) {
        let { name, env } = definition;
        let controller = env.owner.lookup(`controller:${name}`) || generateController(env.owner, name);

        runInDebug(() => this._pushToDebugStack(`controller:${name} (with the render helper)`, environment));

        if (dynamicScope.rootOutletState) {
          dynamicScope.outletState = dynamicScope.rootOutletState.getOrphan(name);
        }

        return { controller };
      }
    }

    const SINGLETON_RENDER_MANAGER = new SingletonRenderManager();

    class NonSingletonRenderManager extends AbstractRenderManager {
      create(environment, definition, args, dynamicScope) {
        let { name, env } = definition;
        let modelRef = args.positional.at(0);

        let factory = env.owner._lookupFactory(`controller:${name}`) || generateControllerFactory(env.owner, name);
        let controller = factory.create({ model: modelRef.value() });

        runInDebug(() => this._pushToDebugStack(`controller:${name} (with the render helper)`, environment));

        if (dynamicScope.rootOutletState) {
          dynamicScope.outletState = dynamicScope.rootOutletState.getOrphan(name);
        }

        return { controller };
      }

      update({ controller }, args, dynamicScope) {
        controller.set('model', args.positional.at(0).value());
      }

      getDestructor({ controller }) {
        return controller;
      }
    }

    const NON_SINGLETON_RENDER_MANAGER = new NonSingletonRenderManager();

    class RenderDefinition extends ComponentDefinition {
      constructor(name, template, env, manager) {
        super('render', manager, null);

        this.name = name;
        this.template = template;
        this.env = env;
      }
    }