Ember.js property and ArrayController in template - javascript

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>

Related

Handlebar Each and JSON API call how do I get it to transfer the data from array response and put it on a handlebars modal

$("#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.

Ember.js - multiple checkeboxes bound to a single array

I have a list of checkboxes and I can only allow the user to pick two. I then need the value of those selected checkboxes added to an array. I also need the ability to remove those values if the user dis-selects its corresponding checkbox.
I've read this question How to push/pop arrays in Ember.js? and Its probably on the right track, but not quite what I need.
I have:
<label {{bindAttr class='checked:active'}}>
{{view Ember.Checkbox valueBinding='id' }}
Select
</label>
I also need the "active" class to be applied when its checked.
Ideas?
This might give you an idea on how to proceed.
Here is a working demo.
Here's the relevant code. You might want to separate this out as a component for reusability.
App.IndexController = Em.ArrayController.extend({
selectedOptions: [],
optionsSelected: function() {
console.log(this.get('selectedOptions').toArray());
}.observes('selectedOptions.[]')
});
App.DeveloperController = Ember.ObjectController.extend({
isChecked: false,
resetFlag: false,
_isCheckedChanged: function(){
if(!this.get('resetFlag')) {
var isChecked = this.get('isChecked');
if(isChecked) {
if(this.get('parentController.selectedOptions').length >= 2) {
this.set('resetFlag', true);
this.set('isChecked', false);
} else {
this.get('parentController.selectedOptions')
.addObject(this.get('content'));
}
} else {
this.get('parentController.selectedOptions')
.removeObject(this.get('content'));
}
} else {
this.set('resetFlag', false);
}
}.observes('isChecked')
});
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model itemController="developer"}}
<label {{bindAttr class='checked:active'}}>
{{view Ember.Checkbox checkedBinding="isChecked"}}
{{item.content}}
</label>
{{/each}}
</ul>
</script>
Here is a working solution, based on Ember Components.
It doesn't handle all edge cases, e.g. bound values that change after the component has rendered, but it does the basics. Can easily be extended if needed.
The array can be constructed using Ember.A(), in order for it to have addObject etc.
Component:
App.ArrayCheckboxComponent = Em.Component.extend({
value: null # included in array when checked
array: null # array to bind to
_checked: null # boolean
init: ->
#_super.apply(this, arguments)
array = #get('array')
unless array.addObject && array.removeObject && array.contains
throw new Error('Array used for `ArrayCheckboxComponent` must implement addObject/removeObject')
if array.contains(#get('value'))
#set('_checked', true)
else
#set('_checked', false)
checkedDidChange: (->
value = #get('value')
array = #get('array')
if #get('_checked')
array.addObject(value)
else
array.removeObject(value)
).observes('_checked')
})
Component template:
{{view Em.Checkbox checkedBinding='_checked'}}
Usage:
{{array-checkbox value='item1' arrayBinding='path.to.array'}}
More on components:
http://emberjs.com/guides/components/

How to convert a Array Object to EmberJS Array

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);

Meteor - return all documents from collection 1 and then query collection 2 as looping through collection 1

I am very new to Meteor (and web programming) and setup two collections, one called Posts and the other Authors (I do realize I could put all this information in one collection but I want to try it this way). I am trying to display all posts so I am doing eachPost in the HTML code which will loop through all my Posts. As I am looping through my Posts, I am trying to save the post id in a Hidden input so I can use that id for each post to query the Authors collection and display the Author name. I added a console.log and it writes undefined every time for my postId - any idea why this is doing this or a better way to solve this problem (without embedding author information in Posts)?
html:
<template name="dashboard">
{{#each eachPost}}
<input type="hidden" id="postId" value="{{_id}}">
<p>{{authorName}}</p>
{{/each}}
</template>
js:
Template.dashboard.helpers({
eachPost: function()
{
return Posts.find({});
},
authorName: function()
{
var postId = $('#postId').val();
console.log(postId);
return Authors.findOne({_id: postId});
}
});
Thanks in advance, I really appreciate any and all help!
I think there is no need to store it in a hidden element
you can access it by using this._id in authorname helper
You can do like below
<template name="dashboard">
{{#each eachPost}}
<p>{{authorName}}</p>
{{/each}}
</template>
Template.dashboard.helpers({
eachPost: function()
{
return Posts.find({});
},
authorName: function()
{
console.log(this._id);
return Authors.findOne({_id: this._id});
}
});
Try this,it may work for you

Is it possible to pass conditionals or other javascript as arguments in ember handlebars?

I would like to pass a true/false statement to my handlebars
{{Gd-text-input label="Specify" name="Specify" key="entry.810220554" hideIf="entry.18110 === "Client""}}
I would like hideIf to be true if the variable entry.18110 is set to "Client
First, add this somewhere -
Handlebars.registerHelper('ifEqual', function (var1, var2, options) {
if (var1=== var2) {
return new Handlebars.SafeString(options.fn(this));
}
return new Handlebars.SafeString(options.inverse(this));
});
Then..
{{#ifEqual entry.18110 "Client"}}
{{Gd-text-input label="Specify" name="Specify" key="entry.810220554" hideIf="true"}}
{{else}}
{{Gd-text-input label="Specify" name="Specify" key="entry.810220554" hideIf="false"}}
{{/if}}
This is pretty much the only way to do it, as the handlebars team has specifically left most logic out of the templates since it generally doesn't belong there. Which is debatable, as sometimes it makes things more complicated not to allow simple logic. But, this is very workaroundable.
The other answer does not work for Ember Handlebars, for your case you could do something like this.
http://emberjs.jsbin.com/agewuxAT/3/edit
Component
App.HideableCompComponent = Em.Component.extend({
isHidden: function(){
var prop = this.get('hideIfProperty');
if(!prop) return false;
// allow lazy comparison? up to you
return this.get('content').get(prop) == this.get('hideIfValue');
}.property('hideIfProperty', 'hideIfValue')
});
Template
<script type="text/x-handlebars" data-template-name="components/hideable-comp">
{{#unless isHidden}}
I am a component I am not hidden!
{{else}}
I am hidden
{{/unless}}
</script>
Usage
{{hideable-comp content=model hideIfProperty='length' hideIfValue=3}}

Categories