Handlebars: transfer information from a page to another page via href - handlebars.js

Let's say in Handlebars we have this cards-list.html partial:
{{#each data}}
<article class="card card_list-view card_list-view--regular">
<picture class="card-image">
<img src="{{root}}/assets/img/{{this.img}}" alt="">
</picture>
<section class="card-section">
<header>
<h3>{{this.title}}</h3>
</header>
</section>
</article>
{{/each}}
Data are like this:
{"id": "1",
"title": "A",
"img": "imga.jpg",
"url": "card-single.html"
},
{"id": "2",
"title": "B",
"img": "imgb.jpg",
"url": "card-single.html"
}
And - in card-single.html - I would like to render the single card simply with:
<article class="card card_single-view">
<h4>{{title}}</h4}
[etc..]
How can I pass, via href attribute or in another way, the original context of cards-list.html partial to card-single.html?
Thanks!

After creating a partial with Handlebars.registerPartial you can include it in a template like so:
{{> cardSingle }}
This syntax also accepts an object parameter:
{{> cardSingle objectOfThings }}
So in your cards-list.html you could have something like:
{{#each data}}
{{> cardSingle this }}
{{/each}}
And your cardSingle partial could use the properties of this directly:
<h4>{{title}}</h4>

So, basically you have the partial cards-list.html which you need to include inside your card-single.html. For which, you need to first register your partial (cards-list in the below example) using Handlebars.registerPartial.
The challenge here is, as your partial is in a separate file, you need run your application in a server (to allow Cross origin) and use the jQuery get function to access it and then register the partial.
I have created a 'main.js' file for the same.
main.js
$(document).ready(function() {
var template = Handlebars.compile($("#base-template").html());
$.get('cards-list.html').done(function(text) { //Accessing cards-list.html
Handlebars.registerPartial('cards-list', text); //Registering the partial in the name of `cards-list`
/* Setting the JSON data here*/
var context = {
"data": [
{
"id": "1",
"title": "A",
"img": "imga.jpg",
"url": "card-single.html"
},
{
"id": "2",
"title": "B",
"img": "imgb.jpg",
"url": "card-single.html"
}
]
};
$('#results').html(template(context)); //Rendering the result in the webpage
});
});
And my 'card-single.html' looks like this.
card-single.html
<html>
<head>
<script src="http://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> <!-- Includes jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.min.js"></script> <!-- Includes Handlebars.js -->
<script src="main.js"></script> <!-- Includes main.js -->
</head>
<body>
<div id="results">
<script id="base-template" type="text/x-handlebars-template">
<article class="card card_single-view">
<h4>{{title}}</h4> <!-- This won't print anything unless in an each loop or use {{data.[0].title}} instead-->
{{> cards-list}} <!-- Calling the partial -->
</article>
</script>
</div>
</body>
</html>
And finally the 'cards-list.html' provided by you.
cards-list.html
{{#each data}}
<article class="card card_list-view card_list-view--regular">
<picture class="card-image">
<img src="{{root}}/assets/img/{{this.img}}" alt="">
</picture>
<section class="card-section">
<header>
<h3>{{this.title}}</h3>
</header>
</section>
</article>
{{/each}}
All these 3 files are in the same directory and as I'm using a mac, I just need to navigate to the directory and run the command python -m SimpleHTTPServer 8000 to start the server. (For windows we may use an apache tomcat server).
After which, simply access the file in a browser with the url http://0.0.0.0:8000/card-single.html.
GITHUB link. Hope this helps.

Related

How to evaluate an expression in polymer (equivalent to "eval" in PHP)

I am building a simple table component with Polymer. I am passing the list of items (an array of objects), and also the list of properties I'd like to display.
So, for example, I want to tell the component: only display the "name" and "number" properties of each object I'll pass to you.
this is how I the component is called:
<cool-table
items="[[projects]]"
columns='[
{"heading": "Project name", "property": "name"},
{"heading": "Project number", "property": "number"}]'>
</cool-table>
So basically it will create a table with two columns, with headings "Project name" and "Project number", and then each row will show project.name and project.number
Here is the component:
<dom-module id="cool-table">
<template>
<div class="table-header">
<template is="dom-repeat" items="{{columns}}" as="column">
<div class="cell">
[[column.heading]]
</div>
</template>
</div>
<div class="table-content">
<template is="dom-repeat" items="{{columns}}" as="column">
<div class="cell">
<!--
here is where I am getting stuck:
I want to to display [[ item.[[column.property]] ]]
So I need to dynamically generate
the name of the property I'll put in the data binding
-->
item.[[column.property]]
</div>
</template>
</div>
</template>
<script>
Polymer({
is: 'cool-table',
properties: {
items: {type: Array},
columns: {type: Array}
}
});
</script>
</dom-module>
In PHP I would do something like eval("\$item.$column.property")
Any idea how this can be achieved with Polymer?
Update:
I am rephrasing the question since I realized I made a mistake in describing the component. I'm using another example where I've simplified everything.
Basically I need to create a component that displays an array of objects. Each object will be on a row, and each object's key will be in a column.
Like this:
object1.name | object1.number | object1.type
object2.name | object2.number | object2.type
object3.name | object3.number | object3.type
So far, so good, that's easy.
Now what I'd like to do is to tell the component which keys need to be displayed, as I don't want to display all of them.
So, I need to tell the component: display only "name" and "number". Then we'll have:
object1.name | object1.number
object2.name | object2.number
object3.name | object3.number
To do that I'm passing the name of the keys I want to display:
<cool-table item="[[items]]" keys="['name', 'number']"></cool-table>
In cool-table.html I would have this:
<!-- loop through all items -->
<template is="dom-repeat" items="{{items}}" as="item">
<div class="row">
<!-- now loop through the keys we want to display -->
<template is="dom-repeat" items="{{keys}}" as="key">
<div class="cell">
<!--
Here I want to display the item's value for that key
for example if key is "name" I want to display item.name
that's what I can't figure out how to do
-->
</div>
</template>
</div>
</template>
Hopefully this now makes more sense. Thanks for hanging there with me!
Ah, I found the solution. I needed to use a computed binding:
<!-- loop through all items -->
<template is="dom-repeat" items="{{items}}" as="item">
<div class="row">
<!-- now loop through the keys we want to display -->
<template is="dom-repeat" items="{{keys}}" as="key">
<div class="cell">
[[getValueFromKey(item, column.key)]]
</div>
</template>
</div>
</template>
<script>
getValueFromKey: function(item, key) {
return item[key];
}
</script>
If I'm understanding your question correctly, you want an inner loop inside the columns array loop, where you iterate through the various keys of your column object?
If so, perhaps this question / answer would help?
Hi Hubert, again, not sure if I understand your question properly now, as I don't see a name property in your data. But I added one, and maybe this is what you are looking for?
Your component would look like this:
<div class="table-header">
<template is="dom-repeat" items="{{columns}}" as="column">
<div class="cell">
[[column.heading]]
</div>
</template>
</div>
<div class="table-content">
<template is="dom-repeat" items="{{columns}}" as="column" filter="{{isPropertyEqName('name')}}">
<div class="cell">
<!--
here is where I am getting stuck:
I want to to display [[ item.[[column.property]] ]]
So I need to dynamically generate
the name of the property I'll put in the data binding
-->
[[column.name]]
</div>
</template>
</div>
</template>
<script>
Polymer({
is: 'cool-table',
properties: {
items: {type: Array},
columns: {type: Array}
},
isPropertyEqName: function(search){
return function(item){
return item.property === 'name';
}
}
});
</dom-module>
and your use of the component would look as follows, where I addded the name property
<cool-table
items="[[projects]]"
columns='[
{"heading": "Project name", "property": "name", "name": "hello"},
{"heading": "Project number", "property": "number", "name": "world"}]'>
</cool-table>

How to loop through embedded document in Meteor

I have an embedded document in a MongoDB collection "Author" as such:
{"Name": "John Doe",
"Country": "U.S.A",
"Books": [
{"BName": "Book1", "Year": "1950"},
{"BName": "Book2", "Year": "1960"}
]
}
I want to access the Books data, loop through it and display each Book in a table.
This is what my JS file looks like
Template.Author.helpers({
author: function() {
//_id of the Author is passed via the URL
return Author.find({"_id": FlowRouter.getParam('_id')})
}
});
This is the HTML for my report
<template name="Author">
<body>
<div class="row">
<div class="col-md-2">
Book Name
</div>
<div class="col-md-2">
Year
</div>
</div>
{{#each author}}
{{> bookdetails}}
{{/each}}
</body>
</template>
<template name="bookdetails">
<div class="row">
<div class="col-md-2">
{{Books.BName}}
</div>
<div class="col-md-2">
{{Books.Year}}
</div>
</div>
</template>
This works when I have only one record in the embedded Books document but not when I have more than one record - which makes sense since 'Books.BName' is ambiguous at that point.
I need to loop through 'Books' and output each BName and Year. This answer is the closest I found to doing this but I get the error:
TypeError: _.value is not a function
This may be because I have an embedded document as opposed to an array.
Your naming is confusing. You've got Book helper that returns an author, and that's what you're passing to the bookdetails template. You should rename the helper to author to reduce confusion. Then you can loop through the actual array of books with {{#each author.Books}}.
Furthermore, in the bookdetails template access the book parameters directly, e.g. {{BName}} instead of {{Books.BName}}.

Can I render ui-router nested views side by side?

I'm using ui-router for navigation in an angular app. I use nested states/views in a hierarchy like this.
<!-- index.html -->
<h1>Page Title</h1>
<ui-view></ui-view>
<!-- Page1.template.html -->
<h1>Title 1</h1>
<ui-view></ui-view>
<!-- Page2.template.html -->
<h1>Title 2</h1>
<ui-view></ui-view>
<!-- Page3.template.html -->
<h1>Title 3</h1>
<ui-view></ui-view>
This result in as expected like this:
Is it possible to make the page look like this instead, i.e. render child views side by side:
Yes, it is possible. Take a look at the docs - Multiple Named Views. Basically you would have your page like:
<body>
<div ui-view="child1"></div>
<div ui-view="child2"></div>
<div ui-view="child3"></div>
</body>
and a state config
$stateProvider
.state('main',{
views: {
'child1': {
templateUrl: 'child1.html',
controller: 'Child1Ctrl'
},
'child1': {
templateUrl: 'child2.html',
controller: 'Child2Ctrl'
},
'child3': {
templateUrl: 'child3.html',
controller: 'Child3Ctrl'
}
}
});
Each child view can have it's own configuration, the same as original state configs.
Update:
If you want them to be dynamic i.e. specify views you want to render manually, going with this approach you could specify names in some $scope variable and ng-repeat over them:
.controller('SomeCtrl', function ($scope) {
$scope.views = ['child1', 'child3'];
})
HTML:
<div ng-repeat="view in views">
<div ui-view="{{view}}"></div>
</div>
See an example here: http://plnkr.co/edit/wVmR6q1UXqhNsF8afpd2?p=preview

Template.dynamic is not passing data context

I have a list template (#each) in a package that I plan to use across many different collections. Since the template is in a package they are not easily customizable. So I figured this was a great example to use Template.dynamic. Everything works except passing data.
.. I pull the data into the routed page and manipulate the data to match the dynamic template.
Template.usersIndex.helpers({
items: function() {
var users = Meteor.users.find({}).fetch();
var items = users.filter(function(user) {
return user;
}).map(function(user){
return {
name: user.profile.name,
description: user.emails[0].address,
tidbit: "hello"
};
});
return items
}
});
... the data passes perfectly to the usersIndex template.
<template name="usersIndex">
<div id="gc-users-index-navbar">
<h2>Title</h2>
</div>
<div id="gc-users-index" class="inner-content">
{{> Template.dynamic template="strataIndexItem" data="items" }}
</div>
</template>
... But no dice, the dynamic template is rendered but no data.
<template name="themeIndex">
<div class="list-group">
{{#each items }}
<div class="list-group-item">
<div class="row-content">
<div class="least-content">{{tidbit}}</div>
<h4 class="list-group-item-heading">{{name}}</h4>
<p class="list-group-item-text">{{description}}</p>
</div>
</div>
<div class="list-group-separator"></div>
{{/each}}
</div>
</template>
You pass data as string?
{{> Template.dynamic template="strataIndexItem" data="items" }}
You should pass data as variable, without ""
{{> Template.dynamic template="strataIndexItem" data=items }}
Also check if your strataIndexItem template is named strataIndexItem:
<template name="strataIndexItem">
...
</template>

Dynamic templates and routing in Meteor.js

I am developing a meteor.js application. I have two main templates/pages, home.html and groups.html. I render these templates according to the selection of main menu. In home template, I just show basic informations about the website. But in groups template, I have side menu which has timeline, chat, group blog options, and according to the selection of side menu, I render timeline or chat or groupblog templates inside groups template. I succeeded this with two different methods.
Can you please tell me if any of them is correct approach, and if they both are, which one is better to use?
In the first approach, is there any way to wait to render sub-template until data is ready, like waitOn property in iron-router?
First approach: I am using dynamic template, and rendering and setting data contex of sub-templates by using Session variable.
layout.html
<template name="layout">
<div>
{{> header}}
<div id="main" class="container">
{{> yield}}
</div>
</div>
</template>
groups.html
<template name="groups">
<div class="row">
<div class="row-fluid">
<div class="col-md-9 col-lg-9">
{{> dynamictemplate}}
</div>
</div>
</div>
</template>
dynamictemplate.html
<template name="dynamictemplate">
{{#DynamicTemplate template=getTemplate data=getData}}
Default content when there is no template.
{{/DynamicTemplate}}
</template>
dynamictemplate.js
Template.dynamictemplate.helpers({
getData: function () {
var tempname="timeline";
if(Session.get("menuitemselected")!=null){
tempname =Session.get("menuitemselected");
}
if(tempname=="timeline"){
return {test:123};
}else if(tempname=="calendar"){
return {groupId:this._id};
}
},
getTemplate: function () {
var tempname="timeline";
if(Session.get("menuitemselected")!=null){
tempname =Session.get("menuitemselected");
}
return tempname;
}
});
Second approach, I am defining new routes for all sub-templates in routes, and I am using yield with region property in home template.(I do not know if i am supposed to use yield in any place other than layout.html)
layout.html
<template name="layout">
<div>
{{> header}}
<div id="main" class="container">
{{> yield}}
</div>
</div>
</template>
groups.html
<template name="groups">
<div>
<div class="col-md-3">
{{> sidemenu}}
</div>
<div id="main" class="col-md-9">
{{> yield region="detailrender"}}
</div>
</div>
</template>
router.js
.....
this.route('groupdetail', {
path: '/groupsmain/:_id',
layoutTemplate: 'layout',
yieldTemplates: {
'timeline': {to: 'detailrender'}
},
data: function() { return Groups.findOne(this.params._id);
}
});
this.route('groupdetailcalendar', {
path: '/groupsmain/:_id/calendar',
layoutTemplate: 'layout',
template: 'groupdetail',
yieldTemplates: {
'calendar': {to: 'detailrender'}
},
data: function() { return Groups.findOne(this.params._id);
.....
});

Resources