How i can create dynamic accordion component EmberJS - javascript

I found a ready accordion solution. I tried to use it, but I don't know how to give this component a variable number of records:
My component template:
{{#search-results-accordion
currentPage=currentPage
totalPages=meta.totalPages
items=items
onPageChange=(action "onPageChange")
as |item|
}}
{{requisite-accordion-item item=item}}
{{else}}
...
{{/search-results-accordion}}
Parent's template(search-results-accordion):
{{#if (gte items.length 1)}}
<div class="items" data-test-search-results>
{{#cp-panels accordion=true as |panels|}}
{{#each items as |item index|}}
{{yield item panels=panels}}
{{/each}}
{{/cp-panels}}}
</div>
...
{{/if}}
Child's template(requisite-accordion-item):
{{#panels.panel as |panel|}}
{{#panel.toggle}}
<p>Panel A</p>
<div class="name"><b>{{item.name}}</b></div>
{{/panel.toggle}}
{{#panel.body}}
{{item.info}}
{{/panel.body}}
{{/panels.panel}}
Then I get EmberError in console: "Assertion Failed: A helper named "panels.panel" could not be found"
enter image description here
How I can use this for variable amount of records?

Here are my observation in your code,
In search-results-accordion.hbs, instead of {{yield item panels=panels}} say {{yield item panels}}.
But you are receiving only item in components template but you should receive item,panels and include both arguments to requisite-accordion-item item=item panels=panels}}
I haven't used this addon. For debugging, you can try {{log 'panels object' panels}} that will print object in console.

Related

Handlebars: How to reuse template after dynamically updating array of objects

Okay so here is the the template I'm using
<script id='goalNavTemplate' type="text/x-handlebars-template">
{{#each this}}
<div class="bubble" data-dismiss="modal" data-Name="{{name}}"
style="background:{{color}} url(images/{{icon}}IconSmall.png)
no-repeat 13px 14px;"></div>
{{/each}}
</script>
And then I'm adding my objects in the array to the DOM with handlebars.
var template = Handlebars.compile($('#goalNavTemplate').html());
$('#selectGoalModal .modal-body').append(template(goals));
This works how I like, yet what I would like to do next is be able to update the DOM when a new object is added to the array (through user input). Yet if I call:
$('#selectGoalModal .modal-body').append(template(goals));
again it will add all the objects to the DOM and not just the last one. My current workaround is to remove the previously added elements and then use the above line of code after the array is updated. I was wondering if there is a more efficient way of doing this using handlebars? Like is there some way to bypass #each and just append the last object in the array? Would you use a helper? If so, how? Any insight into this would be awesome.
You can add some flags to goals item, e.g. datatime and check it by HB:
{{#each this.items}}
{{#ifCond this.dt '>=' ../options.dt }}
<div class="bubble" id="bub-{{id}}" style="background-color:{{color}};">
{{id}} - {{name}}
</div>
{{/ifCond}}
{{#updateDOM "#bub-" id dt}}{{/updateDOM}}
{{/each}}
http://jsfiddle.net/2jvndb0L/5/

Is it possible to switch collection focus in Meteor spacebars templates?

I am trying to display some relational data in a Meteor spacebars template. Specifically I two collections, Location and Weather. They look something like this:
Location {
_id: 'full-name',
title: 'Full name',
lat: '123.456',
long: '123.456',
order: 1
}
Weather {
locName: 'full-name', // this name always matches a location _id
temperature: '41.3'
}
I'd like to display information from both of these collections on a single page. So that I can show the latest weather from each location (there are 4-20 of them per page). To do this, I've published a Mongo request of both collections like so on the server side:
Meteor.publish('allLocations', function() {
return [
Locations.find({}, { sort: { order: 1 } }),
Weather.find({}) // The weather
]
});
I then subscribe to this publication in my router (iron-router):
Router.map(function() {
this.route('locations', {
waitOn: function () {
return Meteor.subscribe('allLocations');
}
}
});
However, I get stuck when I get to my spacebars template. I can't figure out the syntax for switching collection focus in spacebars.
Here's the psuedo-code for the template that I'm trying to parse, but I know that this doesn't currently work.
<template name="locations">
<div class="locations-grid">
{{#each locations}}
<div class="location {{_id}}">
This is the location template
<h1>{{title}}</h1>
{{#each weather}}
<!-- Trying to pass the _id along to the weather template for filtering -->
{{> weather _id}}
{{/each}}
</div>
{{/each}}
</div>
</template>
<template name="weather">
This is the weather template
{{#with weather}}
<!-- Ideally, we've now switched contexts to the weather collection -->
<h2>Temperature: <div class="temp">{{temperature}}</div></h2>
{{/with}}
</template>
So my question is, where do I tell spacebars to switch contexts to the weather collection? How can I pass along the _id variable to the weather template so that I can select the right data from the collection? I know I'm missing a big step here, I just can't figure out which portion of the Meteor space to examine. I know I might need to specify a subscription for the weather template, but I'm not sure where to do that since it's not really a route, since it won't have its own page. It just lives as a sub-template within the locations template.
Thanks for any tips, or possible suggestions on restructuring.
Before we begin, please read A Guide to Meteor Templates & Data Contexts - that will correctly orient you on contexts inside of #each blocks.
Your goal is to join the correct weather documents to their corresponding location documents. This is most easily accomplished by introducing sub-templates for both types. Let's begin with the top-level template:
<template name="locations">
<div class="locations-grid">
{{#each locations}}
{{> location}}
{{/each}}
</div>
</template>
Which has a locations helper like this:
Template.locations.helpers({
locations: function() {
return Locations.find();
}
});
Next, the location template:
<template name="location">
<div class="location">
<h1>{{title}}</h1>
{{#each weathers}}
{{> weather}}
{{/each}}
</div>
</template>
Which has a weather helper like this:
Template.location.helpers({
weathers: function() {
return Weather.find({locName: this._id});
}
});
The key insight here is that the context of a location template is a single location document so weather will return only the weather documents for this location instance. Finally, your weather template can look like:
<template name="weather">
<h2>Temperature: {{temperature}}</h2>
</template>
Note we are now in a weather context so the #with is no longer needed.
Side note - using sort in your publisher has no affect in this case.

ember-cli support for Handlebars #vars in each helper (i.e., #index, #key, #first, #last)

I am getting a compilation error in ember-cli whenever I have a Handelbars template that uses #vars variables (i.e., #index, #key, #first, #last) inside of the each helper. (See http://handlebarsjs.com/#iteration for documentation on these #vars variables inside the each helper.) Below is a simple application built using ember-cli and containing only two files added to the program: routes/application.js and templates/application.hbs. At the bottom of this post is a screenshot of the compilation error message given by ember-cli. Is there an error in my code? Or is this a bug I should report on github # https://github.com/stefanpenner/ember-cli?
routes/application.js
export default Ember.Route.extend({
model: function() {
return ['red', 'blue', 'green'];
}
});
templates/application.hbs
{{#each model}}
{{#index}}: {{this}}
{{/each}}
Screenshot of ember-cli compilation error message:
Here are the versions of the various tools involved:
ember-cli: 0.0.40
node: 0.10.30
npm: 1.4.21
Handlebars: 1.3.0
Ember: 1.6.1
That really isn't related to ember-cli. Ember Handlebars doesn't support the #keyword items.
It's possible to mimic behavior of following Handlebars keywords: #index, #key, #first, #last.
#index
{{#each array as |item index|}}
Index of item: `{{item}}` is: `{{index}}`
{{/each}}
#key
{{#each-in object as |key value|}}
{{key}}: {{value}}
{{/each-in}}
#first
You could also mimic behavior of #first using ember-truth-helpers addon and taking advantage of eq helper - thanks to kristjan reinhold for this idea:
{{#each array as |item index|}}
{{#if (eq index 0)}}
<!-- first element specific html -->
{{else}}
<!-- other html -->
{{/if}}
{{/each}}
Instead of (eq index 0) you can use (eq item array.firstObject).
#last
As dwickern suggested you can use Ember.Array.lastObject to mimic #last behavior.
{{#each array as |item|}}
{{#if (eq item array.lastObject)}}
<!-- last element specific html -->
{{else}}
<!-- other html -->
{{/if}}
{{/each}}

If statement within another if statement in Handlebars templates

These are all objects which I am passing to the handlebars template.
self.template = template({ data: self.model, lang:self.lang, page:self.mainPage, subpage:self.param });
Then inside foreach loop I have an if statement where I am not able to access the parent context element. The code which i have inside the template and the problem is shown bellow:
{{#each data}}
<h1>{{../subpage}}</h1> --> This is giving me a subpage value
{{#if this.brand}}
{{#if ../subpage}}
<h1>WHY IS THIS NOT SHOWING?</h1> --> This is what i am trying to achieve
{{/if}}
{{/if}}
{{/each}}
Any ideas how to achieve this? Thank you very much for all answers/comments!
{{#if this.brand}} goes down into context another level, so you must escape to the parent context not once, but twice.
Your template should thus be:
{{#each data}}
<h1>{{../subpage}}</h1>
{{#if this.brand}}
{{#if ../../subpage}}
<h1>This shows now!</h1>
{{/if}}
{{/if}}
{{/each}}
This should work with a self.model value similar to:
[
{
brand: "Brand A",
otherFields: "here"
}
]
Here is a jsFiddle that demonstrates the solution: nested if statement context escaping
I hope that helps!

Meteor: Re-use template with different data

I've got a few templates which list items (log lines, users) and style them accordingly in tables. Now I've added a "search" page which searches for a string in different item types and displays them on one page. In terms of layout, I want to reuse the original templates, but obviously with the data returned by the search.
How can I do that without duplicating the template itself in the HTML file?
Put another way: How can I use the actual data from the top-level template in the sub-templates.
Small example:
<template name="user_list">
{{#each user_list}}
</template>
<template name="users">
{{> user_list}}
</template>
<template name="search">
{{> user_list}}
{{> group_list}}
</template>
In .coffee:
Template.users.user_list = ->
[a,b,c,d]
Template.search.user_list = ->
[b,c]
Maybe this is an easy one, which would show how little I really understood about Meteor.
Just put the template you want to use inside the {{#each}} statement. Then each item in the array will be the context for that inner template.
HTML file
<template name="users">
<ul>
{{#each UserArr}}
{{> userItem}}
{{/each}}
</ul>
</template>
<template name="userItem">
<li>{{username}} {{profile.name}}</li>
</template>
JS file
Template.users.UserArr = function()
{
return Meteor.users.find({});
};
another solution is to put your request in a Session attribute and check for its existence when querying for the users. By default Session.get("userQuery") would return undefined but once you enter something in the search field you change that with Session.set("userQuery", {type: "something"})
Now in the coffeescript you could say:
Template.users.user_list = ->
if(Session.get("userQuery"))
[a,b,c,d]
else
[b,c]
or alternatively use a MiniMongo query because it is much nicer :-)
the nice thing is that your HTML will rerender because it is reactive to the Session's content.

Categories