I'm supposed to use ember.js for a project. I'm converting a json into a HTML view. The json looks something like this:
{
"title": "Some H1 title",
"children": [
{"title": "Some H2 title",
"children": .....
}
]
}
So the result should look like
Some H1 Title
Some H2 Title
Doesn't really matter if it's dynamic with handlebars or static.
What is the recommended emberjs way of doing this? Load the whole json into a data model and go from there? Or just give the object to handlebars? (there is the need for some basic if else logic by the way so the last option might not be the best (e.g. needs more code)).
If the data in json has some meaning in the data model of your application, then you should place it in a structure of ember objects and assign them to properties of the controller. If you only need the json data to be displayed and has no meaning in your application's data model, then you can simply assign it directly as a property to the view or controller.
In all cases you will need a handlebars template that will display the data stored in those properties.
For example if you have json data that is composed of objects with a string as title and array of the same objects as children and you want to display all titles of all children regardless of depth you could do something like,
http://emberjs.jsbin.com/UgaVAvIZ/1/edit
hbs
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<h1>{{title}}</h1>
{{partial "childrenPartial"}}
</script>
<script type="text/x-handlebars" data-template-name="_childrenPartial">
{{#each child in children}}
<h2>{{child.title}}</h2>
{{#with child.children}}
{{partial "childrenPartial"}}
{{/with}}
{{/each}}
</script>
js
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return {"title": "Some H1 title","children": [{"title": "Some H2 title","children": []}]};
}
});
Please note that using a partial as shown is only one of many other approaches. In this approach the json data has been assigned as model to the specific route and the corresponding view with the help of a partial display the required data.
Related
I'm getting the response from the server like
category:{
listOfCategory: [
"diabetes",
"general"
],
id:1
}
using Ember data findAll method. I'm returning this to my model and I'm trying to display in hbs file using each loop.
js code
model(){
return this.store.findAll('category');
}
hbs code
{{#each model as |category|}}
<h1>{{category.listOfCategory}}</h1>
{{/each}}
Output :
diabetes,general
I want to display as separate category.
Given the data modify your each loop:
{{#each model as |category|}}
{{#each category.listOfCategory as |cat|}}
<h1>{{cat}}</h1>
{{/each}}
{{/each}}
Your loop is calling for the array of categories, instead of looping through the category array elements.
What I'm trying to accomplish is to return only one object to the model and loop through it's properties in a handlebars template. Thanx-a-Lot for any help!
My response looks like this:
{"U+554A":{
"id":1,
"unihex":"U+554A",
"num_reference":"呵",
"totalStrokes":10,
"kMandarin":"a\n",
"kDefinition":"exclamatory particle\n"},
"U+611B":{
"id":2,
"unihex":"U+611B",
"num_reference":"愛",
"totalStrokes":13,
"kMandarin":"\u00c3\u00a0i\n",
"kDefinition":"love, be fond of, like\n"}
}
I tried everything. The most obvious being these two, just as a tryout, but the ember inspector shows no model is set:
var sinograms = Ember.$.getJSON(apiurl);
return sinograms['U+554A'];
I also tried:
var sinograms = Ember.$.getJSON(apiurl);
return sinograms[0];
NB:
I can change the response format if necessary.
I know it works when I loose the object-keys (without "U+554A"), but then how do I select the matching character.
#abuani: Upon your request. Thanx btw.
//app.js
App = Ember.Application.create();
App.Router.map(function() {
this.resource('signup');
this.resource('login');
this.resource('profile');
this.resource('overview');
this.resource('practice');
});
App.OverviewRoute = Ember.Route.extend({
model: function() {
var url = 'http://localhost/~hiufung/RoadToChinese/index.php/api/sinograms/random?limit=2';
var sinograms = Ember.$.getJSON(url);
return sinograms;
}
});
//index.html (inline template)
<script type="text/x-handlebars" id="overview">
<header class="bar bar-nav">
<a class="icon icon-left pull-left" href="back"></a>
<h1 class="title">RoadToChinese</h1>
<a id="showRightPush" class="icon icon-gear pull-right" href="overview-settings"></a>
</header>
<div class="content">
<div class="content-padded">
{{#each object in model}}
<p>{{object.num_reference}}<p>
{{/each}}
</div>
</div>
</script>
I can't add a comment to your initial question, but can you please post a few other pieces of code:
The router that's loading this(I imagine this is being done in your model function), the setupController if you have one, and the template that's trying to render the object. Without this information, there's little I can do to help.
EDIT Since there's code:
I should have noticed, your model is return an objects when Ember expects the model to be an array of objects. The first thing you should do is make the return from the server an Array. If you don't have control over this, then you can change your model to return
return Ember.A([sinograms]);
And that should work.
After that though, you can remove the nested object within each object. If you don't remove that, then you also need to make the inner objects an Array so you can iterate over it.
Let me know how this goes.
Here's the JSBIN
I have been using ember for a few days now, but i ran to this simple problem:
I made an ajax call and return the results to the model in the route. Basically the call will return a single json object. As i read in the guide, the model will be sent to the 'content' of the controller as below. The problem is how to get the data printed to the template? This is not working:
The Controller:
App.IndexController = Ember.ObjectController.extend({
content: [{name:'smith', age:'20',lastname:'jonnie'}]
});
Handlebars:
<p>{{name}}, {{age}}, {{lastname}}</p>
The content property is an array, so you rather loop over it to get your items out
template
<ul>
{{#each item in controller.content}}
<li>{{item.name}}</li>
{{/each}}
</ul>
or try something like this:
IndexController
App.IndexController = Ember.ObjectController.extend({
content: [{name:'smith', age:'20',lastname:'jonnie'}],
firstObject: Ember.computed.alias('content.firstObject')
});
template
<p>{{firstObject.name}}, {{firstObject.age}}, {{firstObject.lastname}}</p>
Working example.
Hope it helps.
As you'll be able to tell from my question, I'm slowly learning EmberJS. I've read the great guide on routes and I felt ready to take on the world but then...
In my example, I thought the {{somethingView}} would be rendered and not the controller property {{somethingCtrl}}. Is this the correct behaviour? If so how would you render a property from the Ember.View?
The JS
window.App = Ember.Application.create({
ready: function() {
this.initialize();
}
});
window.App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router) {
var controller = router.get('applicationController');
controller.connectOutlet('garments');
}
})
})
})
window.App.ApplicationView = Ember.View.extend({
templateName: 'application',
});
window.App.ApplicationController = Ember.Controller.extend();
window.App.GarmentsController = Ember.Controller.extend({
somethingCtrl: "Something in the controller"
});
window.App.GarmentsView = Ember.View.extend({
templateName: 'garments',
somethingView: "Something in the view"
});
The DOM stuff
<script type="text/x-handlebars" data-template-name="application">
<h1>Hi Ember</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="garments">
<h1>Garments</h1>
{{somethingView}}<br>
{{somethingCtrl}}
</script>
The Fiddle
This behaviour is correct. These are my understandings of these ember core concepts:
Model: These objects represent the date that is handled by your application. These are the business objects that form the domain model of your Application.
Controller: A Controller is responsible for providing access to your models. Controllers have the property content, where models should be injected (a single Object for Ember.Controller and an array of objects for Ember.ArrayController). The Controller passes this content to your View. The Controller is the default context for your view. Therefore the behaviour you describe is expected.
View: The View is just intended for displaying issues. I personally use it mainly to do jQuery animations.
But nonetheless it is possible to access the view instance in the template. You just have to use the variable with the name 'view' in your template. I updated your fiddle with a working example: http://jsfiddle.net/jPK8A/5/
<script type="text/x-handlebars" data-template-name="garments">
<h1>Garments</h1>
{{view.somethingView}}<br>
{{somethingCtrl}}
</script>
But to be clear: The most common case should be to access contents from your controller. It should be not often that you access variables of your view. You want to display date in your App and this date resides in models and should therefore be accessed through controllers. The most likely case might be, that you want to store labels in your view or something like that (labels that have to be computed).
I have a model which defines a property with either markdown or html content.
I am wondering whether using a markdown JS library to output the info or use handlebars to generate the html output inside the view.
Any recommendations, examples will be appreciated.
Using a Markdown converter worked for me.
Here is my view code:
App.ActivityDetailsView = Em.View.extend(
templateName : 'activity-details',
classNames : ['details rounded shadow'],
rawDescriptionBinding: 'App.activityDetailsController.description',
description: (->
converter = new Markdown.Converter().makeHtml
return converter(#rawDescription)
).property('rawDescription')
)
Here is the template code (note the triple handlebars {{{}}} for raw html):
<script type="text/x-handlebars" data-template-name="activity-details">
{{{description}}}
</script>
Here is a link to more details and the showdown.js script
Ember recommends that you use your controller to decorate your model. Given this model, we want to render each of these blog posts using the appropriate rendering engine:
[
{ id: 1, isMD: false, md_or_html: "<p>This is HTML.</p>" },
{ id: 2, isMD: true, md_or_html: "*This is MD.*" }
]
You'll start by creating a route which returns that model:
App.IndexRoute = Ember.Route.extend({
model: function() {
return [
{ id: 1, isMD: false, md_or_html: "<p>This is HTML.</p>" },
{ id: 2, isMD: true, md_or_html: "*This is MD.*" }
];
}
});
Just having the model returned doesn't mean that things get rendered. You also need to make sure the template for the index route attempts to put something on the page:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each}}
<li>{{output}}</li>
{{/each}}
</ul>
</script>
You'll note that we haven't yet created an output property, though we've included it in our template. We need to decorate our model to add the processed HTML or Markdown output:
App.IndexController = Ember.ArrayController.extend({
itemController: 'post'
});
App.PostController = Ember.ObjectController.extend({
output: function() {
var result;
if (this.get('isMD')) {
var converter = new Markdown.Converter();
result = converter.makeHtml(this.get('md_or_html'));
} else {
result = this.get('md_or_html');
}
/*
IMPORTANT!!! Ember automatically escapes HTML upon insertion.
To actually embed the result as HTML you will need tell Ember
that the value is safe to embed as HTML.
DO NOT RETURN SafeStrings UNLESS THE VALUE IS TRUSTED AND SANITIZED!
*/
return new Handlebars.SafeString(result);
}.property('isMD', 'md_or_html')
});
We can't just add the output property to PostController and have everything work without telling IndexController to use PostController for each item in the model. This is accomplished by setting itemController on IndexController (think: "what controller to use for each item"). This allows us to decorate each blog post individually with the output property. We use a computed property to tell Ember that the value of output is dependent upon whether or not the post isMD and the body of the post. If either changes we want Ember to re-render the output.
The complete example includes additional comments and details about how to extend the pattern for introspection into the post body to determine if it is HTML or MD.
I encountered a similar case that I handled with a dynamically inserted handlebars template: I have a field containing a template which may have content bound to application values.
Ember.View.create({
tagName: 'span',
classNames: ['dynamic-content'],
template: Ember.Handlebars.compile(App.preCompileTemplate(template)),
context: someContextObject
});
The App.preCompileTemplate function replaces bindings with valid handlebars expressions, but you could also imagine using Markdown here:
App.preCompileTemplate = function(template) {
return template.replace /{(.*?)}/g, '{{context.$1}}'
}
Using the context object scopes the values that you bind into the template.