Compontens should allow for inline templates


#1

For Components to be fully stand-alone, they should allow for inline templates. gravatar-image is a good example - it’s template contains only little code and it could be a good candidate for template property.


#2

Can you provide a syntax example of what this might look like? You can customize the html tag that wraps the component.

  tagName: 'div'
  tagName: 'span'
  etc

I presume you want a template content property where you can put in arbitrary markup content. Since template already has a meaning within views, it is a function that takes a context property.

http://emberjs.com/api/classes/Ember.Component.html#property_template

Perhaps there should be a property/hook called templateContent where you could place inline markup.

Perhaps something like this? I wonder if some custom handbars code could be used with in didInsertElement event that prepends and appends template content. Maybe a setupTemplate method that goes into action whenever the component is rendered.

App.MyWidgetComponent = Ember.Component.extend({
    templateContent: "<div>My Widget Content</div>",
    foo: null,

    actions: {
        doSomething: function(input) {
            console.log("App.MyWidgetComponent doSomething %O", input);           
            // Implement behavior

            // Send action to external scope
            this.sendAction('action', input);
        }       
    },

    setupTemplate: function(){
        // implement setup bootstrapping and markup when added to DOM
        console.log("App.MyWidgetComponent setupTemplate");  
    }.on("didInsertElement")

});


/* 
* Handlebars Usage
  {{my-widget action="externalFunction" foo="externalValue"}}
*/

#3

Yeah, I meant template as in a view. I asked because I couldn’t make it work but let me try this out again.


#4

I think what you are asking for is that a component in a template could take a block as its inline template? Here’s how you do it with a view:

{{#view App.MyView}}
  This is the inline template.
{{/view}}

It would be cool to do the same thing with components. If we had defined a ‘blog-post’ component, we could do this for example:

{{#blog-post}}
  Blog post inline template.
{{/blog-post}}

#5

This pattern is already possible with {{ yield }}

Here is a JSBin

http://jsbin.com/UsufObE/1/

Template looks like this

<script type="text/x-handlebars" data-template-name="components/my-widget">
   <h3>My Widget</h3>
     {{ yield }}
    <hr />
   <p> {{ footer }} </p>   
</script>  

And then you pass in arbitrary content inside the body of the component

  {{#my-widget}}
    <p>Inner content</p>
  {{/my-widget}} 

The component looks like this

App.MyWidgetComponent = Ember.Component.extend({
    footer: "footer goes here value"
});

There is probably a way to programmatically use yield but I don’t know it off the top of my head.

If you want a component inside a component that is also possible.

http://jsbin.com/AhObAgO/1/

  {{#my-widget}}
      {{other-widget}}
  {{/my-widget}}  

#6

I’m not sure if I understood you but here is a real-life example.

I have a basic FooterButtonComponent that is used in sidebar in different views. Then I have BidButtonComponent and TenderAwardCompoenent that extend FooterButtonComponent.

I tried to set template: '{{label}}' for FooterButtonComponent so that all child components don’t have to set their own. But the problem is that if I want to use BidButtonComponent or TenderAwardComponent and if they have not template defined in templates/components like templates/components/tender-award-button.emblem I get this error message:

Uncaught Error: <App.TenderAwardSidebarView:ember819> Handlebars error: Could not find property 'tender-award-button' on object <App.TenderAwardController:ember450>.`

So it seems that you can use a component without customized ‘ComponentView’ but you cannot use it without a template defined in templates/components

So in my example for each child component I have to add the same template. Not very DRY. :smile:


#7

I could be off base here, but I am not sure I understand the value of subclassing components from each other. It seems useful to think of these as stand alone entities the template is going to be tightly bound to the component logic. That might not seem DRY. but in a way it can be. You can have a variety of components that have very specific use cases. You get better composability by mixing and matching different component into your template. You want to be DRY, just start reusing components within components and narrow the concerns of your underlying base component types.

Also there is reusability here because the component has a narrow interface. We really don’t care about the internal implementation. It is a black back. All we care about is when I load this specific component and pass it some specific data I get a very specific behavior and presentation of my data. Need a different type of presentation, just create a different type of component and tweak the inside of the black box.

Now I definitely understand the use case for subclassing and having nice hierarchical relationships to remove some of the need for boiler plate code.

But in this case just create a base view (say a generic button) and extend in specific ways. And then you can include those specific types of views INSIDE different components.

I do this in the app I am building. In some cases I extend Ember.Textfield to give it specific features, for example I can create a input field that only allows integer numbers. Instant and bulletproof validation.

I can use these types of text fields anywhere in my system. But also I may have a variety of different kinds of forms I want to create. So in that case I choose to wrap the Integer only input view into a specific form component. Some components can have multiple input fields some text some numeric, some boolean, etc.

WIth this system I have a high degree of flexibility and composability. And because the scope of each component is very narrow it is easy to understand each component in isolation. If I need a specific variant I can compose a different one and add it to the system. Reuse it however I want just change the data that is passed into it.

Apologize if that seems too abstract. If I can find some time I will try to put together a JSBin to make this more clear.


#8

Do you mean Ember View class, not Component?


#9

Yes a view class. Basically in one example I just extend Ember.Textfield

Here is a concrete example:

App.IntegerTextField = Ember.TextField.extend({
    // remove any character not a digit from the
    // input parameter
    _stripAlpha: function(input) {
        if (input) {
            return input.toString().replace(/[^\d]/g, "");
        } else {
            return input;
        }
    },

    // observe the input value and automatically remove
    // anything that is not a digit.
    _numericValue: function() {
        this.set('value', this._stripAlpha(this.get('value')));
    }.observes('value')
});

And then inside a form component template I might do something like this.

    {{ view App.IntegerTextField valueBinding="formInputValue" }}

And my component logic might look something like this

App.MyFormComponent = Ember.Component.extend({
    formInputValue: null,

    setupDefaults: function(){
        // implement setup bootstrapping when added to DOM
    }.on("didInsertElement")

});

/* 
* Handlebars Usage
{{ my-form formInputValue="externalValue" }}
*/

So I can create and many of these as I want and pass in as many different values and build as manny forms as I would like. But they all use the same exact implementation of the App.IntegerTextField view class.

{{ my-form formInputValue="costInDollars" }}

{{ my-form formInputValue="numberPets" }}

{{ my-form formInputValue="numberPeople" }}

etc.


#10

You can inline templates with https://github.com/pangratz/ember-cli-htmlbars-inline-precompile now