I'm using Ember and Highlight.JS and I want to dynamically edit an XML document and show a syntax highlighted version in a live preview. Currently I have a preview, but the code is not syntax highlighted.
<script type="text/x-handlebars">
<p>Name {{input type="text" value=name}}</p>
Non-Syntax Highlighted output
<pre>
<person>
<name>{{name}}</name>
</person>
</pre>
</script>
<script type="text/x-handlebars" data-template-name="myxmltemplate">
<person>
<name>{{name}}</name>
</person>
</script>
JSBin with this code
To get syntax highlighting, I need to render myxmltemplate above to a string, not directly to the page. This string can be fed into highlightjs to be turned into syntax highlighted HTML. How can I do this?
The problem is that the template gets inserted / rendered after highlight.js has initialized. Therefore you need initialize the highlighting every time the view gets rendered. This can easily be done using the Ember.Views didInsertElement hook.
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
// From http://highlightjs.org/usage/ see Custom Initialization
this.$('pre').each(function(i, e) {hljs.highlightBlock(e)});
}
});
JSFiddle: http://emberjs.jsbin.com/masomafu/2/edit
Related
I'm trying to build an Ember component consisting of svg tags. The goal is to have a d3 visualization driven by Handlebars template and with the dynamic changes handled by the component. I'm running into an issue though.
My view template is as follows:
<script type="text/x-handlebars">
{{the-parent data=data}}
</script>
Component templates are as follows:
<script type="text/x-handlebars" id="components/the-parent">
<svg {{bind-attr width=width height=height}}>
<g {{bind-attr transform=transform}}>
{{#each data}}
{{the-child text=text}}
{{/each}}
</g>
</svg>
</script>
<script type="text/x-handlebars" id="components/the-child">
<text>Hello</text>
</script>
The parent component is defined as such:
App.TheParentComponent = Ember.Component.extend({
height: function() {
...
}.property(...),
width: function() {
...
}.property(...),
transform: function() {
...
}.property(...)
});
And I get the following error:
Assertion Failed: The metamorph tags, metamorph-93-start and metamorph-93-end, have different parents.
The browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used...<omitted>...')
If in the parent component template you replace the svg tag with a div tag and the g tag with a ul tag, and in the child component template you replace the text tag with a li tag, it works fine.
Any idea what's going on?
EDIT:
The specific assertion that fails is the following:
function _addMetamorphCheck() {
EachView.reopen({
_checkMetamorph: on('didInsertElement', function() {
Ember.assert("The metamorph tags, " +
this.morph.start + " and " + this.morph.end +
", have different parents.\nThe browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '{{#each}}')",
document.getElementById( this.morph.start ).parentNode ===
document.getElementById( this.morph.end ).parentNode
);
})
});
}
When it fails, the value of document.getElementById( this.morph.start ).parentNode is "g" and the value of document.getElementById( this.morph.end ).parentNode is "div#ember521.ember-view", the latter being the div tag of the parent component itself, so the tag parent to the svg tag. I tripled checked all the tags and I'm pretty sure none is left unclosed.
Found a solution! The trick is to not use Handlebars in <script type="text/x-handlebars" id="components/the-child"></script>. Define the tag in the component definition itself under the tagName hook and bind any attributes through the attributeBinding hook. See this jsbin for a working solution. The reason this is working is that the template engine then does not add these weird metamorph tags.
I would like to do the following handlebar script:
The color:{{object.labelColor}} seems not working (Cf. other post).
One solution seems to bind the complete style attribute on the "javascript side" but I would like to avoid managing the "fixed" part of the style (text-align:left) on that side.
Is there a solution to make something similar to my sample code (dynamic part on the js side,fixed part on the html view) ?
<div class="label" style="color:{{object.labelColor}};text-align:left">{{object.name}}</div>
You could use 'classNameBindings', and define a set of css rules to the corresponding classes.
Js
Ember.View.create({
classNameBindings: ['isUrgent'] // array of class names
isUrgent: true //conditional to set class name
});
css
.is-urgent{
background-color: #356aa0;
}
Resource
http://emberjs.com/api/classes/Ember.ContainerView.html#property_classNameBindings
If you have to use styles instead of classes, and want to change only a portion of the style strings at the time, one alternative would be using $.css, applying the style changes you want. For example:
<script type="text/x-handlebars">
<h1>App</h1>
{{view App.SomeView}}
</script>
<script type="text/x-handlebars" data-template-name="some-view">
<div style="color:#00F;text-align:left">
Some View<br />
<button {{action changeColor on="click"}}>Change Color</button>
</div>
</script>
<script type="text/javascript">
App = Em.Application.create();
App.SomeView = Em.View.extend({
templateName: 'some-view',
changeColor: function(e) {
console.log('changing color');
this.$('div').css('color', '#F00');
}
});
</script>
In the script above I'm using jQuery's css function to change the color attribute whatever elements my selector returns from within SomeView instance (in this case as I only have one div, I'm using the element as a selector). There are other (and likely better) ways to do this, but I hope this helps.
The problem with this solution as it is, is that you can't keep the state of the style attribute as it's not bound to any property, that's why I think binding classes would be better in the long run.
/START description of why
I'm doing a 'load more' type of interaction: user gets at bottom of a page, I load more content.
As I'm using a plugin that formats content in a 'pinterest-style' (masonry), I need to take the output of a rest call, formatted using an ember view, and i'm doing something like:
<div id="list">
</div>
<div id="hidden" style="display:none">
{{#each item in App.itemsController}}
test {{item.id}}
<br />
{{/each}}
</div>
What I want to do is, load the content in the hidden div, and then pass its HTML generated content to the plugin to process in my formatted list view.
If I just copy the #hidden content, the Ember scripts get in, and on subsequent 'load more', content is inserted in the #list, in addition of going in the #hidden div.
That's because I copied the entire handlebars.
So I get rid of the container tag, the one I supposed was wrapping the controller content binding, but even when stripping it from the content I pass to #list, the #list still auto-updates when a 'load more' is called.
I know that's a dirty hackish thing, but in order to improve performance in the client I must take a similar route.
/END description of why
//ACTUAL QUESTION ;)
Given this background, the question is, stripping the container metamorph script tags (like the ones here below), and just take the content inside them, shouldn't get rid of the auto-updating content functionality?
<script id="metamorph-X-start" type="text/x-placeholder"></script>
//ALL THE CONTENT
<script id="metamorph-X-end" type="text/x-placeholder"></script>
Inside those, I just have the actual content generated, like:
<script id="metamorph-9-start" type="text/x-placeholder"></script>
test <script id="metamorph-59-start" type="text/x-placeholder"></script>2873<script id="metamorph-59-end" type="text/x-placeholder"></script>
<br>
<script id="metamorph-9-end" type="text/x-placeholder"></script>
<script id="metamorph-10-start" type="text/x-placeholder"></script>
test <script id="metamorph-60-start" type="text/x-placeholder"></script>2872<script id="metamorph-60-end" type="text/x-placeholder"></script>
<br>
<script id="metamorph-10-end" type="text/x-placeholder"></script>
The alternative is programmatically render the template inside a variable and process that, which is surely a better way of doing this, but I just wonder how the #each binding works internally as I thought the metamorph was doing that.
Have you looked into using a CollectionView and calling the plugin through the didInsertElement callback?
e.g.
MyList = Ember.CollectionView.extend({
itemViewClass: 'App.ListItem',
didInsertElement: function(){
view.$().jqueryPlugin({ ... })
}
})
I am new to Ember and most likely I am missing something very simple.
Anyway, this is my questions:
I try to add an empty input box to an HTML page the following way ...
...
var view = Ember.View.create({
templateName: 'say-hello',
});
view.append();
...
<script type="text/x-handlebars" data-template-name="say-hello">
{{view App.TextField}}
</script>
However I don't get the input box.
Feedback is appreciated.
Thanks
Ember.TextField instead of App.TextField
I have a textarea binded to a div such that anything you type in the textarea updates the div.
The only thing that bindings doesn't respect is newlines in textarea so if you hit 'enter' within textarea, the div doesn't get a break.
Fiddle: http://jsfiddle.net/lifeinafolder/Ajkyw/19/
I am using a helper and it doesn't work.
As per Point 4 on this link: http://codebrief.com/2012/03/eight-ember-dot-js-gotchas-with-workarounds/, it is not supposed to work. But even with the solution on that link, I can't get it to work.
Any ideas on how to update the div with <br/> tags on linebreaks within the textarea?
Use a computed property to format the location's line breaks.
HTML:
<script type="text/x-handlebars">
{{#with App.obj}}
{{view Ember.TextArea valueBinding="location"}}
<div>{{{formattedLocation}}}</div>
{{/with}}
</script>
JavaScript:
App.obj = Ember.Object.create({
location:'Palo Alto',
formattedLocation: function() {
return this.get('location').replace(/\n\r?/g, '<br />');
}.property('location').cacheable()
});
http://jsfiddle.net/e8G2j/
In the example I removed your helper so it would be a bit easier to follow exactly what is going on. If you need to use a helper and are unable to figure it out let me know and I will add the helper back in.