Hi,
Trying to do something along these lines working, but to no avail. The "I got wrapped" content never gets rendered. (This is a home-made markdown converter we have made.)
The reason why I need to use Ember.HTMLBars.compile, instead of just using a string is that we want to support data-binding within the Markdown content.
Of course, we are no longer extending Ember.view, which is no longer available, but instead doing this in an Ember.Component, like below. Could this be causing the issue? Ember.Component _derives_ from Ember.View so I'd assume it would work, but...
export default Ember.Component.extend({
layout: Ember.HTMLBars.compile("<div class='my-decorative-class'>{{yield}}</div>"),
template: Ember.HTMLBars.compile("I got wrapped")
});
I cannot even get a basic example like that work, which indicates to me that the example is outdated and maybe not even working any more. (or maybe more correctly, not working from a _component_)
The previous code we had looked something like this. It didn't work at all; no content was rendered at all. (The template does indeed have a {{yield}} statement in this case.)
import Ember from 'ember'
import layout from './template'
export default Ember.Component.extend
layout: layout
_yield: (context, options, morph, blockArguments) ->
view = options.data.view
markdown = (@get('text') || '').toString()
content = marked(markdown)
content = Ember.Handlebars.compile(content)
view.appendChild(Ember.View,
isVirtual: true
tagName: ''
layout: content
_blockArguments: blockArguments
_contextView: view
_morph: morph
context: @get('content')
)
I know that _yield is not a very public API. :smile: I'd be happy to change this in whichever way. The ability to support data-binding within the Markdown is the critical part; just getting it rendered and displayed in the template is trivial if you skip that.
Huge thanks in advance.
I'm not sure when this happened, but you used to be able to set layout or layoutName in an override to the component's init method and use the specified layout, rather than the component's template file. If, however, there is a template file defined, it overrides any attempt within the component to set the layout. I think this is backwards (i.e. broken). (Or perhaps there is a different way to override a component's template file. If so, it would be great to document it.)
Use case: I use an addon which has a number of deeply-nested components. I do not reference the component that I need to customize within my own template files. Instead, I use a component which uses another component (which uses yet another component). So I want to reopen the deeply-nested component and adjust its template to my liking. I can easily set the layout or layoutName before calling this.super(...arguments), and the correct layout is placed within the component object, but then that is ignored by the code which actually creates the component and merges it with the magic-named template.
@perlun the link to the View docs for layout/template is deprecated right? http://emberjs.com/api/classes/Ember.View.html#toc_layouts
My best guess is that a change in strategy my be needed to abandon the reliance on the View behavior.
Instead see https://guides.emberjs.com/v2.3.0/components/defining-a-component/#toc_dynamically-rendering-a-component the component helper may be a better way to use dynamic templates. (I think you can use a template only component.)
I don't see how the {{component xxx}} helper, uh, helps (in this use case). The difficulty is in trying to override a deeply-nested component, where the developer using the components is not the author of the add-on. If I can reopen a component and override its template (i.e. layout), then I can customize the component without having to extend every parent component.
WRT the referenced views documentation: it is out-of-date in that setting layout or layoutName only works if there is no template defined. Yes, views as a public class are deprecated, but since components continue to use views, should not layout and layoutName be supported in a way similar to the past, where they can override a template, not just supply a layout in the absence of a template?
Or perhaps there is another way to override a template, without adjusting the parent component's template? If so, let's document it.
i'll see if i can get someone whos been working on recent view changes to speak to this issue.
I should have started with a big "thank you" to the core contributors. So ... Thank you, very, very much. I am very much enjoying my developer experience.
Thank you, very, very much. I am very much enjoying my developer experience.
thank you, much appreciated.
@DanChadwick generally it will be easier to override a layout in the resolver, you can reexport the component to a new name and export a template with that name. If you have to, you could clobber the existing module with the template. You have not been able to dynamically update a component's layout in the 2.x series. Extending a component with the layout overriden has worked for the 2.x series but I don't think it takes precedence over the resolver. cc @rwjblue @wycats
Kris, thanks for looking at this. If using the resolver to accomplish this is the "ember way", let's document it in a guide. Googling gives lots of obsolete information.
Still I wonder: When both the template and the component's layout / layoutName specify a layout, what does the developer intend? I can't think of a scenario where "template" is the answer. The fix would be a one-liner.
In my experience, in 2.4.1 I was able to assign a custom layout using Ember.HTMLBars.compile when I made the layout property a computed value (with no listed properties), however, I could not for the life of me change the layout at runtime, not by adding a listener in the .property() list, nor by using set on layout property , even when I attempted to run rerender after the set. Thus we seem to have lost the dynamic template feature that used to exist as demonstrated here http://emberjs.jsbin.com/qebuxuxasu/1/edit?html,css,js,output .
I believe I may be facing the same issue as @justin-hackin above wherein I can set the layout property of a component to the result of an Ember.HTMLBars.compile successfully, but I can't get it to change at runtime by making it a computed property. Thus I'm not sure how to rely on the layout property if it can't be successfully bound to one or more model values (a property called markdown in my case, per below).
Here is the component I'm using, which is designed to support data-binding in Markdown content as @perlun was intending originally here as well (which is a whole larger topic about how best to do this in a standards-aware way without creating XSS vulnerabilities, which as an aside, I haven't seen addressed by the community yet):
import Ember from 'ember';
export default Ember.Component.extend({
store: Ember.inject.service(),
layout: Ember.computed('markdown', function() {
var converter = new showdown.Converter();
var regex = /\$([a-z]+?)-([a-z0-9]+?)/gi;
var match;
while ((match = regex.exec(this.get('markdown'))) !== null) {
this.set(match[0], this.get('store').findRecord(match[1], match[2]));
}
var html = converter.makeHtml(this.get('markdown'));
return Ember.HTMLBars.compile(html);
})
});
As of Ember 1.13 and later is not possible to change the layout of a component after it initially renders.
@rwjblue thanks for the clarification, although that's a bummer. Is there any plan to reintroduce it or is there a rationale I'm missing?
I'm not sure how to regenerate layouts based on model data (two different blog posts sharing the same route and components, for example) unless I can blow away previous layouts then generate them anew.
@krisselden seems to have laid out a strategy above but I'm having a hard time following it, and I'm not sure it directly addresses my use case to start with.
Thanks!
@perlun This is not a bug, closing for now. I should suggest an alternative solution, perhaps using the component helper so the components in your templates can be dynamically chosen.
Thanks @pixelhandler. I'm not sure how I would be able to do a Markdown-rendering component this way though, where I can embed Handlebars bindings _inside_ the Markdown? (Rendering static content is, of course, trivial - it's the bindings that makes it harder.)
@perlun did you figure out a solution for this? There seems to be no way to have a user input some markup and parse it and generate a new layout or anything now. Having a very hard time accomplishing this.
@rwwagner90 - unfortunately not yet, no. If you manage to come up with a solution for it, I'd be extremely interested in hearing about it though.
@rwwagner90, @perlun and anyone else interested...
I came up with a solution using two simple components. Works in Ember 2.7.3 and updates when your template does.
Gist: https://gist.github.com/nojacko/9b2e8d00c15ad90eb591e7ee382f6634