Can I dynamically append a component to the page?

For example, I clicked a button and I want to display an Ember component onto the page. This component should be dynamically created because I will pass some data on to it, and then show it, just like a modal window. Could this be feasible? I don’t want the way that put the component in the page and hide it first, and then show it after I click the button. Thanks for your help

At present, I just wrote a hbs, and then call Handlebars.compile this hbs and then append the result to the body. In this way I can’t use Ember component in this hbs. How could I solve this problem? Thank you

Have a variable in your controller to toggle for appending your component into your body.

App.PostController = Ember.Controller.extend({
    showComponent = false;
});

In your template:

{{ #if showComponent }}  // Your component will be appended into your html only when its true
    {{ temp-comp }}  
{{/if}}

In your Route have an action handler to toggle the property to show/hide your component.

App.PostRoute = Ember.Route.extend({
    actions:{
        showComp: function(){
                this.controller.toggleProperty( 'showComponent' );
        }
    }
});

That’s really a great suggestion! Thanks a lot!

But If I want to reuse my component in the different pages, I have to write the properties in each controllers, right? So I think this may be a little redundent for the component reuse. Is there a better way?

Anyhow, the way you told me above is really a much better method than the one I’m using at present, I’ll try your way.

Thanks again!

One issue is that If I want to pass different data to the component, how could I do that? If the component has been already initialized when loading the page, the way you wrote is just hide and show this component

@hwoarangzk Kindly look into the SO question I asked and the answer, it may help your needs regarding components.

SO LInk

Thank you so much!!!

I tried your way, but it’s weird :frowning:

In my template, I wrote:

{{#if showDetail}}
    {{store-detail store=store}}
{{/if}}

And the following is my controller code:

App.StoresController = Em.ArrayController.extend({

    store: null,

    showDetail: false,

    actions: {
        setStore: function(store) {
            this.set('store', store);
        },
        show: function() {
            console.log('show detail11');
            this.set('showDetail', true);
        }
    }
});

The component code:

App.ItemOverviewComponent = Em.Component.extend({
    actions: {
        showMap: function() {
            alert('show map');
        },
        showDetail: function(obj) {
            console.log('show detail in component');
            this.get('controller').toggleProperty('showDetail');
            console.log(this.get('controller').get('showDetail'));
        }
    }
});

The problem is that when I click the button in the component, showDetail function has been triggered, and in the console, the showDetail property of the controller got changed each time. But the page doesn’t get any change at all! It won’t display the component or hide the component.

And then, when I set the showDetail property in the controller to true directly, and refresh the page, I can see the component displayed on the page. And next time, I set false to showDetail property, refresh the page, there is no component shown on the page.

So I really can’t figure out why I can’t show or hide my component since I can toggle showDetail property in my component action?

New update, if the component has been shown on the page when loaded this page, I can hide this component by change showDetail property to false, but never get it displayed again…

Hmm, you need to toggle the property in the controller. In your example code you simply set it to true. For example:

App.StoresController = Em.ArrayController.extend({
    showDetail: false,
    actions: {
        toggleDetail: function() {
            this.toggleProperty('showDetail');
        }
    }
});

Also, I’m not sure if you encountered this in the docs, but if a component is rendered within an {{#if...}} block, then the component is not constructed until the condition becomes true, and when the condition becomes false, any components within the block are not only hidden but destroyed.

First, I’d like to confirm that, a component could get the controller in the same page and is able to manipulate the properties of this controller. Is this right?

Second, if the condition is false, then even I set this condition to true, the component in the {{#if condition}} won’t get constructed and displayed on the page again, right?

If the above two points got clarified, then I think that I’ll be certain about how to solve my problem, thank you

@hwoarangzk Kindly go through the Ember Documentation on Ember.Components.

It says,

An Ember.Component is a view that is completely isolated. Property access in its templates go to the view object and actions are targeted at the view object. There is no access to the surrounding context or outer controller; all contextual information must be passed in.

So in your case you cannot access your controller within your Component Class.

Also kindly look into the post to have a detailed understanding of Ember.Components.

Thank you, I found where my problem lies in. In my component, this.get(‘controller’) actually means the component itself, I thought it means the controller. And now I know how to use this.sendAction in the component to call the methods in the controller. Thanks for your heartwarming help :slight_smile:

Another question, can I get the controller in a view? I wrote this.get(‘controller’) in the didInsertElement in my view and it seems worked. I just want to confirm this, thank you.

@hwoarangzk When you are in the same view and need to access its corresponding controller and its method you can call,

this.get(“controller”); // To get the controller

this.get(“controller”).send(“yourMethodinController”);; // To call a method in your controller.

If you want to access another controller from your current view, you can call as below,

this.get(“controller.controllers.another”); // To get the another controller in your current view.

this.get(“controller.controllers.another”).send(“yourMethodinAnotherController”);; // To call a method in another controller.

Thank you so much!!!

And one more question, in the same controller, how could I call the method in actions from another method also in the actions? For example:

App.MyController = Em.ArrayController.extend({
    actions: {
        a: function() {
            ....
        },
        b: function() {
            ....
        }
    }
});

How to call a method inside b method? Thanks :slight_smile:

You can call methods within your actions handler using “this.send(“yourMethod”)”;

Really appreciate your heartwarming help very much!!!

How to get a property of a controller in the view? Can I change the value of a property of a controller? I met this problem now