Rendering ember component outside parent template

匆匆过客 提交于 2019-12-22 13:30:16

问题


Imagine the following situation. I have a sidebar positioned absolutely and then some more stuff positioned absolutely within that sidebar. In that sidebar I have a button, which shows a menu, the template looks like this:

<button>Click me</button>
{{#if shouldDisplayMenu}}
    {{view App.MyMenu}}
{{/if}}

What I like about this solution and want to keep is that the menu in the template is defined right next to the button, making for nice maintainability.

The problem is that I want the menu to be positioned relative to the viewport, i.e. rendered to document.body and positioned using position absolute. This way it will render into the sidebar and cause a scrollbar to be visible because I have already reset positioning context via position relative in one of the parent elements.

Is there any way I can keep {{view App.MyMenu}} where it is now, but have it render to body (perhaps using outlets or some other mechanism)? What is the recommended pattern for a situation like this?


回答1:


There are plenty of ways to render views in ember with related context. I present four approaches (i think the first is more likely what you are looking for) :

1. If the view is required to be placed somewhere out of a handlebars template, it is possible to use jQuery to place the rendered content wherever it is required to be.

(since the replacement of the view element is happening after didInsertElement it is probably required to also add styles to not show the element eg display:none , and show it after it has been repositioned)

Example http://emberjs.jsbin.com/gotabore/1/edit

2. Use {{render}} helper to render the view in place if that place is somewhere in another template. Although i'm not certain about the context requirements, this helper is pretty flexible (http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper).

Example http://emberjs.jsbin.com/xibayava/1/edit

3. Use {{outlet}} helper to render the view based on what is specified within the visited route. (http://emberjs.com/guides/routing/rendering-a-template/)

Example http://emberjs.jsbin.com/tiyuqenu/1/edit

4. Use a ContainerView to push a view programmatically anywhere it is required to be.

Example http://emberjs.jsbin.com/yoyujeqi/1/edit


UPDATE for approach 4: for versions equal or greater than 1.8.x , to avoid the error mentioned in http://emberjs.com/guides/deprecations/#toc_global-lookup-of-views , it is possible to specify a property associated to the ContainerView as shown in the following example

http://emberjs.jsbin.com/difayakeki/1/edit?html,js,output


related code

approach 1

hbs

<script type="text/x-handlebars">
    <h2> Welcome to Ember.js</h2>

    {{outlet}}
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    This is index<br/>
    {{view App.TestView}}
  </script>
  <script type="text/x-handlebars" data-template-name="test">
    This is the test view<br/>
    <b>{{myProp}}</b>
  </script>

  <div id="a-div" style="background-color:lightgray">
    This is a div somewhere,<br/>
  </div>

js

App = Ember.Application.create();

App.Router.map(function() {
  // put your routes here
});

App.IndexController = Ember.Controller.extend({
  myProp:"index controller prop"
});

App.IndexView = Ember.View.extend({
  placeTestViewSomewhere:function(){

    var theTestView = this.$(".my-test-view").detach();
    $("#a-div").append(theTestView);

  }.on("didInsertElement")
});

App.TestView = Ember.View.extend({
  templateName:"test",
  classNames:["my-test-view"]
});

approach 2

hbs

<script type="text/x-handlebars">
    <h2> Welcome to Ember.js</h2>

    {{outlet}}
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    This is index<br/>

    <div style="background-color:lightgray">
    This is a div in index template with render helper,<br/>
    {{render "test"}}
  </div>
  </script>
  <script type="text/x-handlebars" data-template-name="test">
    This is the test view<br/>
    <b>{{myProp}}</b>
  </script>

js

App.IndexView = Ember.View.extend({
  placeTestViewSomewhere:function(){

//     var theTestView = this.$(".my-test-view").detach();
//     $("#a-div").append(theTestView);

  }.on("didInsertElement")
});

App.TestController = Ember.Controller.extend({
  myProp:"test controller prop"
});

App.TestView = Ember.View.extend({
  templateName:"test"
});

approach 3

hbs

<script type="text/x-handlebars">
    <h2> Welcome to Ember.js</h2>

    {{outlet}}
    <div style="background-color:lightgray">
    This is a div in application template with outlet helper,<br/>
    {{outlet "test-outlet"}}
  </div>
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    This is index<br/>

  </script>
  <script type="text/x-handlebars" data-template-name="test">
    This is the test view<br/>
    <b>{{myProp}}</b>
  </script>

js

App.IndexRoute = Ember.Route.extend({
  renderTemplate: function() {
    this.render("index");
    this.render('test', {   // the template to render
      into: 'application',                // the template to render into
      outlet: 'test-outlet'             // the name of the outlet in that template
    });
  }
});

App.IndexView = Ember.View.extend({
  placeTestViewSomewhere:function(){

//     var theTestView = this.$(".my-test-view").detach();
//     $("#a-div").append(theTestView);

  }.on("didInsertElement")
});

App.TestController = Ember.Controller.extend({
  myProp:"test controller prop"
});

App.TestView = Ember.View.extend({
  templateName:"test"
});

approach 4

hbs

<script type="text/x-handlebars">
    <h2> Welcome to Ember.js</h2>

    {{outlet}}
    <div style="background-color:lightgray">
    This is a div in application template with view helper and a view container,<br/>
    {{view Ember.ContainerView viewName="my-menu-container"}}
  </div>

  </div>
  </script>

  <script type="text/x-handlebars" data-template-name="index">
    This is index<br/>
  </script>
  <script type="text/x-handlebars" data-template-name="test">
    This is the test view<br/>
    <b>{{myProp}}</b>
  </script>

js

App.IndexController = Ember.Controller.extend({
  myProp:"index controller prop"
});

App.IndexView = Ember.View.extend({
  placeTestViewSomewhere:function(){

    var theTestView = App.TestView.create({});
    theTestView.set("context",this.get("context"));
    this.get("parentView.my-menu-container").pushObject(theTestView);

  }.on("didInsertElement")
});


App.TestView = Ember.View.extend({
  templateName:"test"
});

UPDATE for approach 4:

js

App.ApplicationView = Em.View.extend({
  containerView:Em.ContainerView
});

hbs

{{view view.containerView viewName="my-menu-container"}}


来源:https://stackoverflow.com/questions/24157798/rendering-ember-component-outside-parent-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!