I've the following class:
App.Entity = Ember.Object.extend({
id: null,
name : null,
});
And I've the following controller :
App.HomeController = Ember.ObjectController.extend({
entities: null,
init:function(){
var myArray = [];
var a = App.Entity.create();
a.set('id',1);
a.set('name','A');
var b = App.Entity.create();
b.set('id'2);
b.set('name','B');
//and I add another entities dynamycally
myArray.push(a);
myArray.push(b);
console.log( 'isArray: '+ Ember.isArray(myArray) ); //I get true
this.set('entities', myArray );
}
});
The problem is when I try to iterate and render the content over view:
<script type="text/x-handlebars" data-template-name="home" >
{{#if entities}}
{{#each entities }}
{{this.value}}
{{/each}}
{{/if}}
{{outlet}}
</script>
I get the following mistake:
Assertion Failed: The value that #each loops over must be an Array. You passed <App.Entity:ember425>,<App.Entity:ember426>,...
How to fix it?
After reading a bit in their documentation, I have understood that you should use Ember.ArrayController to render arrays.
An example from their documentation would be like this:
Controller:
MyApp.listController = Ember.ArrayController.create();
$.get('people.json', function(data) {
MyApp.listController.set('content', data);
});
Template:
{{#each MyApp.listController}}
{{firstName}} {{lastName}}
{{/each}}
As can be seen here, they first set the key content with the data array directly on the controller. In your case this would be the step this.set('entities', myArray ); that you already did.
In the second step, they use the #each helper on the controller, not the key. In your case this would look like:
<script type="text/x-handlebars" data-template-name="home" >
{{#if entities}}
{{#each App.HomeController }}
{{id}} {{value}}
{{/each}}
{{/if}}
{{outlet}}
</script>
To access the properties you do it as in any handlebars template.
Update
From your comments I assume that you are not deserializing the json string to a javascript object.
The json you receive from the server is a plain string. You must deserialize it using JSON.parse.
Example :
var json = '[{"id":"17","nombre":"Musical dezzer"},
{"id":"172","nombre":"Musical dezzer"}]',
trueResult = JSON.parse(json);
console.log(trueResult);
Related
$("#searchMovieBtn").click(() => {
const movieSource = $("#movie-template").html();
const movieList = Handlebars.compile(movieSource);
const movieSearch = $("#addMovie").val();
console.log(movieSearch);
queryURL = `https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${movieSearch}&page=1&include_adult=false`;
$.ajax({
url: queryURL,
method: "GET"
}).then(response => {
console.log(response);
const results = response.results;
const data = { movies: [] };
for (let i = 0; i < results.length; i++) {
const currentTitle = {
title: results[i].title,
release_date: results[i].release_date
};
data.movies.push(currentTitle);
}
console.log(data);
$("#placeholder").html(
movieList({
data: data
})
);
});
});
});
this is my function to call it
<div id="placeholder"></div>
<script type="text/x-handlebars-template" id="movie-template">
<ul class="list-group">
{{#each movies}}
{{#each this}}
<li class="list-group-item text-dark" id="MovieTitle">{{title}}
<span>
(<span id="MovieYear">{{release_date}}</span>)
<span>
<button class="btn btn-warning text-light btn-sm" id="addMovieBtn">Add to watchlist</button>
</span>
</span>
</li>
{{/each}}
{{/each}}
</ul>
</script>
And what it is going into!
I have been up all night and just cannot figure out what is going on with it! I swear I have looked up and down and still cannot find the right method to use it is driving me mad! Anyways thanks for the help ahead of time I am sure it was something very very simple that I looked over a lot.
There are a few errors in your solution.
First, you must understand the context object that you are passing to your template. It's shape looks like this:
{
data: {
movies: [
{
title: "Movie Title",
release_date: "Year"
}
]
}
}
Notice that the first key in the object is data. However, in your template, you have no reference to data, but try to access movies directly, effectively skipping a reference. Given this data, the first #each in your template would need to be updated to:
{{#each data.movies}}
Your second problem is your second #each in your template. This #each iterates over each property of each movie in the movies array. So when you reference a property like {{title}} within this block, the full path of your reference is something like data.movies[i].title.title. There is an extra '.title' there because you are not within the context of the movie, but of one of its properties (title in this case).
To fix this, simply remove the second #each.
I have created a fiddle for your reference.
As an additional note: I would avoid including static element IDs inside of a loop. It means you will have duplicate IDs in your output and that is not valid HTML.
I have a php array like this one
bookings[] = {"arrived","cancelled","departed"};
When display this array in handlebars template i want to check IF equal condition.
In the following example when value is equals to cancelled i want to display some text.
{{#each bookings}}
{{#if this.value cancelled}}
cancelled
{{/if}}
{{/each}}
This code is not working. What is alternative IF equals condition for handlebars to execute in loop.
Now my code is working,
bookings = ["arrived", "cancelled", "departed"];
Handlebar function:
Handlebars.registerHelper('check_status', function(val1, val2) {
return val1 === val2;
});
Handlebar template:
{{#if (check_status this 'cancelled')}}
If I'm not mistaken you can't do an actual conditional in handlebars, you can only do if true/false which means that the **value** in {{#if **value**}} need to either be true or false
So what you will want to do is in the area of code where this.value is defined create a function like this
valueIsCancelled = function(value) {
return value === 'cancelled';
}
In your template you will do:
{{#each bookings}}
{{#if this.valueIsCancelled this.value}}
cancelled
{{/if}}
{{/each}}
or another option is to define another variable where value is defined that would be a boolean
var isCancelled = value === 'cancelled';
and your template would look like this
{{#each bookings}}
{{#if this.isCancelled}}
cancelled
{{/if}}
{{/each}}
I just started learning emberjs and wanted to display a list of data from the models. My app.js:
var h= [{
name: "Hi",
},
{
name: "Hello",
}
];
App.ListsRoute = Ember.Route.extend({
model: function(){
return h;
}
});
my corresponding lists handlebar:
<div id="list">
<ul>
{{#each}}
<li>
{{ name }}</li>
{{/each}}
</ul>
</div>
I get the error as:
Error: Assertion Failed: The value that #each loops over must be an Array. You passed (generated lists controller)
What is that I can do to display those?
#each wants to iterate over a list. Since you didn't give it an argument, it defaults to trying to iterate over your ListsController. Unfortunately, since you didn't define it, the controller was auto-generated based on the plain Ember.Controller. Thus, the error.
Two ways to fix this:
1) Make ListsController into an ArrayController
App.ListsController = Ember.ArrayController.extend({});
2) Tell #each to target your model instead of controller.
<div id="list">
<ul>
{{#each model}}
<li>{{ name }}</li>
{{/each}}
</ul>
</div>
I have an ArrayController, and I would like to group the contents of that ArrayController based on a the value of a specific key.
For example, if the objects in my ArrayController are:
id status name
-----------------------------
1 1 some name
2 1 some other name
3 2 blah
4 3 blah again
Then I would like to group the contents by status.
To do this, I tried using computed properties in my ArrayController:
App.SomeArrayController = Ember.ArrayController.extend({
itemController: 'some-item',
status1: Ember.computed.filterBy('content', 'status', 1),
status2: Ember.computed.filterBy('content', 'status', 2),
status3: Ember.computed.filterBy('content', 'status', 3)
});
In the template, these are being displayed, but they are not being wrapped by the itemController I specified in the ArrayController:
// item controller used in the ArrayController
App.SomeItemController = Ember.ObjectController.extend({
anotherKey: function () {
return 'hey ' + this.get('name');
}.property('name')
});
<!-- template to display status=1 items -->
{{#each status1}}
// displays the name (as a property from the model)
{{this.name}}
// nothing displays here
// (leading me to believe it is not being wrapped by the item controller)
{{this.anotherKey}}
{{/each}}
What am I doing wrong?
itemController only wraps items when you iterate over the controller collection.
{{#each item in controller}}
{{item.coolItemControllerProperty}}
{{/each}}
It doesn't apply to any collection within the controller. If you tried to iterate the underlying model/content it wouldn't be wrapped.
{{#each item in content}}
{{item.coolItemControllerProperty}} // undefined
{{/each}}
{{#each item in model}}
{{item.coolItemControllerProperty}} // undefined
{{/each}}
fortunately you can specify an itemController in your template for these situations.
{{#each item in status1 itemController='some-item'}}
{{item.coolItemControllerProperty}}
{{/each}}
I've got a setup like this in Ember:
App.ListObject = Ember.Object.create({
knownThings: function() {
var ot = this.openThings.get('content');
var ct = this.closedThings.get('content');
var kt = ot.concat(ct);
var known = Ember.ArrayController.create({content: kt});
return known;
}.property(),
openThings: Ember.ArrayController.create({
content: []
}),
closedThings: Ember.ArrayController.create({
content: []
}),
})
Basically, known things is the combined arrays of openThings and closedThings. I can't seem to figure out how to iterate over knownThings in the template. Just doing
{{#each App.ListObject.knownThings }}
Does not work as the property needs to be accessed like App.ListObject.get('knownThings') but that doesn't work in the template unless I'm doing something terribly wrong. Iterating over the other attributes in the template does work (open and closed things)
So, how would you iterate over knownThings in the template?
Slight Modifications needed...
Firstly,
knownThings: function() {
//use get to retrieve properties in ember, Always !
var ot = this.get('openThings').get('content');
//var ot = this.get('openThings.content') if you are using latest ember
var ct = this.get('closedThings').get('content');
//var ot = this.get('closedThings.content') if you are using latest ember
var kt = ot.concat(ct);
var known = Ember.ArrayController.create({content: kt});
return known;
//Add dependencies to keep your knownThings in sync with openThings & closedThings if at all they change in future
}.property('openThings', 'closedThings')
Coming to Handlebars iterate using
//you forgot content property, and in handlebars you don;t need to use get, dot operator is enough
{{#each App.List.knownThings}}
Let me know if this works...
Update
Working Fiddle...
Unless I didn't understand what you're saying, I think you should have ListObject extending Em.ArrayController instead of Em.Object. Also, if your property depends on content, it should be .property('content.#each'). If you're using the router, your template should look like {{#each thing in controller.knownThings}} and you use {{thin.something}}, if not using router, then {{#each item in App.listObject.knownThings}}. Also, openThings and closedThings don't seem to be correct and the way you're accessing them is wrong too.
I didn't write a fiddle for this specific case cause I don't really know what you're trying to do, but take a look at this fiddle, specifically at App.ResourcesController and the template 'resources-view':
Controller:
// ...
App.ResourcesController = Em.ArrayController.extend({
content: [],
categories: ['All', 'Handlebars', 'Ember', 'Ember Data', 'Bootstrap', 'Other'],
categorySelected: 'All',
filtered: function() {
if(this.get('categorySelected') == "All") {
return this.get('content');
} else {
return this.get("content")
.filterProperty(
"category",
this.get('categorySelected')
);
}
}.property('content.#each', 'categorySelected'),
filteredCount: function() {
return this.get('filtered').length;
}.property('content.#each', 'categorySelected'),
hasItems: function() {
return this.get('filtered').length > 0;
}.property('filteredCount')
);
// ...
Template:
<script type="text/x-handlebars" data-template-name="resources-view">
<h1>Ember Resources</h1>
{{#view Bootstrap.Well}}
The following is a list of links to Articles, Blogs, Examples and other types of resources about Ember.js and its eco-system.
{{/view }}
{{view Bootstrap.Pills contentBinding="controller.controllers.resourcesController.categories" selectionBinding="controller.controllers.resourcesController.categorySelected"}}
<i>{{controller.filteredCount}} Item(s) Found</i>
{{#if controller.hasItems}}
<ul>
{{#each resource in controller.filtered}}
<li>
<a {{bindAttr href="resource.url"
target="resource.target"
title="resource.description"}}>
{{resource.htmlText}}
</a>
</li>
{{/each}}
</ul>
{{else}}
{{#view Bootstrap.AlertMessage type="warning"}}
Couldn't find items for {{controller.categorySelected}}
{{/view}}
{{/if}}
</script>