Deeply nested dynamic templates loses scope of parents - javascript

I'm attempting to use a dynamic template to create a virtual number pad; this works by passing rows of buttons to another template that renders them. The issue I'm having is the template cannot use Templates.parentData() to access the context as it becomes undefined. Moving up in the inheritance by using Templates.parentData(2) or (3) does not function either.
Template.bs_num_pad.helpers({
'number_rows' : function(){
var result = [];
console.log(this);
console.log(Template.currentData());
// true
console.log(Template.parentData(1));
// Template viewName="Template.bs_num_pad"
console.log(Template.parentData(-2));
// true
console.log(Template.parentData(-3));
// true
}
});
<!-- begin snippet: js hide: false -->
<template name="bs_num_pad">
<div class="container bsNumPadNumber">
<span style="display:block;text-align:center;">{{getBsNumPadNumber}}</span>
{{#each number_rows}}
<div class="col-3">
{{>bs_buttonset}}
</div>
{{/each}}
</div>
</template>
The number pad template that references another template to generate the bootstrap buttonset.
<template name="bs_buttonset">
{{!Template.dynamic template="bs_buttonset" data=difficultyOptions }}
<div class="btn-group btn-group-md" style="text-align:center;display:inline-block;" role="group">
{{#each this}}
<button id="{{btnId}}" value={{value}} class="btn {{btnClass}}" type="button">
{{#if btnIcon}}<i class="glyphicon {{btnIcon}}"></i>{{/if}}{{#if btnText}}{{btnText}}{{/if}}
</button>
{{/each}}
</div>
</template>
Template.parentData(-2) should return the contents of optionsModal; instead it returns 'true'.

Related

Can't pull .attr() from button generated via handlebars

I'm in the process of building a web scraper for a homework assignment involving Express, Mongoose, Cheerio/axios, and Handlebars. My "/" route grabs the Mongoose objects and handlebars loads them to the page in individual cards. A save button is generated with an attribute data-id={{_id}}' in these cards. I'm trying to access the attribute with jQuery when it's pressed to save it to another collection but $(this) is returning undefined.
js
$(document).ready(function () {
$("#artDiv").on("click", ".save", (event) => {
event.preventDefault();
let id = $(this).attr("data-id");
console.log($(this).data("id"));
console.log(id);
})
});
html
<div id="artDiv" class="container">
{{#obj}}
{{#each .}}
{{#if headline}}
<div id="articleCard" class="card">
<h5 class="card-header">{{altHead}}</h5>
<div class="card-body">
<h5 class="card-title">{{headline}}</h5>
<p class="card-text">{{desc}}</p>
Visit
<button data-id="{{_id}}" data-control="saveBtn" type="button" class="btn btn-success save">Save</button>
</div>
</div>
{{/if}}
{{/each}}
{{/obj}}
</div>
If you want to have your clicked element in $(this), you cannot use arrow functions. You have to use $("#artDiv").on("click", ".save", function (event) { ... });.

getValue not working on custom template

I've been able to make my own field, using a costum template. This field consist in a textField followed by button at the end, on the same line, but I'm confronted to an issue.
When I'm using form.getValue(), it does return the original values, I mean, if I modify 1 field and then do a getValue(), the modification won't be visible and I will still get the value I had when loading the form.
I'm pretty sure there is a problem with my template but I can't figure what to do ! :(
Here is my templates code :
{% raw %}
<script type="text/x-handlebars-template" id="input-group-addon-template">
<div class="input-group">
<input type="{{inputType}}" value="{{data}}" id="{{id}}" {{#if options.placeholder}}placeholder="{{options.placeholder}}"{{/if}} {{#if options.size}}size="{{options.size}}"{{/if}} {{#if options.readonly}}readonly="readonly"{{/if}} {{#if name}}name="{{name}}"{{/if}} {{#each options.data}}data-{{#key}}="{{this}}"{{/each}} {{#each options.attributes}}{{#key}}="{{this}}"{{/each}}/>
<div class="input-group-btn" id="{{id}}-basic-btn">
<button class="btn btn-default" id="{{id}}-button" onclick="test(event)" >
<i {{#if options.readonly}}class="fa fa-lock"{{else}}class="fa fa-unlock"{{/if}}></i>
</button>
</div>
</div>
</script>
{% endraw %}
<script>
With some help, I've been able to get it to work. So I just had to modify the template, new template :
<script type="text/x-handlebars-template" id="input-group-addon-template">
<div class="input-group">
{{#control}}{{/control}}
<div class="input-group-btn" id="{{id}}-basic-btn" >
<button class="btn btn-default" id="{{id}}-button" onclick="click_function(event)" >
<i {{#if options.readonly}}class="fa fa-lock"{{else}}class="fa fa-unlock"{{/if}}></i>
</button>
</div>
</div>
</script>
So the big difference is that we don't precise the "input-type" anymore so that we don't override all usefull function as getValue etc.
Feel free to add some explication ! :D

Running Javascript at end of loading all Meteor templates load

I have the following template code:
<template name="dashboard">
<div class="content-container-with-sidebar clearfix">
<div class="dashboard-container">
{{#if Template.subscriptionsReady}}
<div id=masonry-grid class="masonry-grid clearfix">
{{> timePieChartCard}}
{{> expensePieChartCard}}
{{> HighlightsCard}}
{{> initializeMasonry}}
</div>
{{/if}}
</div>
</div>
</template>
I'd like to run the initializer for the masonry library after all the templates and their subscriptions/helpers load within the main dashboard template.
Each card has code similar to this:
<template name="HighlightsCard">
{{#if highlightsExist}}
<div class="col-md-6 masonry-grid-item">
<div class="card highlights-card">
</div>
</div>
{{/if}}
</template>
Template.highlightsCard.onCreated(function() {
this.autorun(() => {
this.subscribe('userOwnClientHighlightsData');
});
});
Template.highlightsCard.helpers({
highlights() {
return Highlights.find({}, {
limit: 4,
sort: {createdAt: -1}
}).fetch();
},
highlightsExist() {
return (Highlights.find().count() > 0);
}
});
I currently am setting a session that increases when each onRendered is run within the card templates, but once they all render their contents have still not loaded. I need the class masonry-grid-item to be visible in order for me to initialize masonry.
What's the best way to get a similar result as jquery's $(document).ready() ?
Update: each individual card in the dashboard template has it's own subscription to a different dataset.
Look at Tracker.afterFlush
Schedules a function to be called during the next flush
For example:
Template.dashboard.onRendered(()=>{
Tracker.afterFlush(()=>{
...your code here
});
});
If I understand the problem correctly, you want to initialize your component after the subscription is complete and after all sub-templates (that also depend on the subscription data) are rendered.
Since I don't know what your JavaScript looks like, here is an example of what you can try.
Template.dashboard.onRendered(function() {
this.isReady = new ReactiveVar(false);
this.subscribe('subscriptionName', () => {
this.isReady.set(true);
Tracker.afterFlush(() => {
// Initialize component here
});
});
});
Template.dashboard.helpers({
isReady: () => {
return Template.instance().isReady.get();
},
});
And then use the isReady ReactiveVar in your template. In this case, isReadymay not be necessary (e.g. you could probably use the Template.subscriptionsReady helper), but I like to have explicit control of when to render incase my logic gets more complex in the future. Note, I removed {{> initializeMasonry}} because it looked like you were attempting to initialize your component via a helper.
<template name="dashboard">
<div class="content-container-with-sidebar clearfix">
<div class="dashboard-container">
{{#if isReady}}
<div id=masonry-grid class="masonry-grid clearfix">
{{> timePieChartCard}}
{{> expensePieChartCard}}
{{> HighlightsCard}}
</div>
{{/if}}
</div>
</div>
</template>
The callback to Tracker.afterFlush will get executed on the next Blaze render cycle, which will be after your sub-templates are rendered.

Ember JS Enable dropdown only on element which was clicked

I am an Ember newbie and can't get my head around how to run events on specific elements populated via records on my ember model.
Here is my Template
{{#each user in model }}
{{#each activecredit in user.activecredits}}
<div class="col-lg-2 hive-credit-box active-credit">
<div class="credit-brandname">
{{activecredit.Brandname}}
</div>
<div class="credit-brand-image-container">
<img src="http://localhost:3000/{{activecredit.Imglocation}}" class="credit-image"/>
</div>
<div class="hive-credit-percent"><img class="hive-filled-container" src="imgs/hivefilled9.png"/></div>
<div class="hive-credit-dollars">$xx.xx</div>
<div {{bind-attr class=activecredit.Brandname}} {{action 'enableTrade'}}><img src="imgs/trade_button.png" class="credit-trade-button" /></div>
<div class="credit-brand-amount">
xx%
</div>
<!-- Trade button click dropdown -->
{{#if isSelected}}
<div class="hivetrade">
<div class="arrow_box">
Hi there
</div>
<div class=""></div>
</div>
{{/if}}
</div>
{{/each}}
{{/each}}
Now I want to show a drop down on each element on click of a button .
When I set the enableTrade action , all dropdowns of all the divs show up.
actions:{
enableTrade:function(){
this.set('isSelected',true);
}
}
How do I enable only the dropdown in the particular div that was created.
I suppose I need to bind certain attributes to each div,but how do I access which div was clicked in my controller?
Any help would be appreciated .
Thanks
You can pass params through the action helper, and use them to set isSelected on the appropriate item.
{{#each model as |user| }}
{{#each user.activeCredits as |activeCredit|}}
<button {{action 'enableTrade' activeCredit}}>Enable Trade</button>
{{#if activeCredit.isSelected}}
<div class="hivetrade">Hello World</div>
{{/if}}
{{/each}}
{{/each}}
To handle it:
actions:{
enableTrade:function(credit) {
credit.set('isSelected',true);
}
}
If you need to allow only one credit to be selected at a time, your controller and action could be modified like this:
selectedCredit: null,
actions:{
enableTrade:function(credit) {
// unselect the previously selected credit
if (this.get('selectedCredit')) {
this.set('selectedCredit.isSelected', false);
}
// select, and cache the selection choice
credit.set('isSelected',true);
this.set('selectedCredit', credit);
}
}

Using Handlebars how do I wrap 4 divs in a row div?

I had a hard time wording the question so I apologize. I'm using handlebars to generate listings from a JSON file and I'm stuck. Basically every 4 cards I generate need to be wrapped in a row div. Here's what I tried but didn't work very well
(using coffeescript)
Handlebars.registerHelper "everyOther", (index, amount, scope) ->
if index % amount
scope.inverse this
else
scope.fn this
Here's my template
{{#each data}}
{{#everyOther #index 4}}
<div class = "card-result-row">
{{/everyOther}}
<div class = "card-result with-image">
<img src="{{this.userImgUrl}}" alt="Contacts Image" />
<div class="contact-info">
{{this.user}}
<span class="contact-title">{{this.jobTitle}}</span>
<span class="contact-email">{{this.email}}</span>
<span class="meta-location">{{this.location}}</span>
</div>
</div>
{{#everyOther #index 4}}
</div>
{{/everyOther}}
{{/each}}
So basically on the first iteration I want it to open a row div and after the 4th "card" is generated I want to close out that row and start a new one. Thanks for the help
What are you trying to do is not a good practice in Handlebars as in other logic-less templaters. It will be much better if you make data transformation before passing it to template, instead of inventing such strange helpers.
If you try to group your data first, for example using underscore.js groupBy:
data = _.toArray(
_.groupBy(data, function (item, index) {
return Math.floor(index/4);
})
);
Then your template will look like:
{{#each data}}
<div class = "card-result-row">
{{#each this}}
<div class = "card-result with-image">
<img src="{{userImgUrl}}" alt="Contacts Image" />
<div class="contact-info">
{{user}}
<span class="contact-title">{{jobTitle}}</span>
<span class="contact-email">{{email}}</span>
<span class="meta-location">{{location}}</span>
</div>
</div>
{{/each}}
</div>
{{/each}}
This template cleaner and much easy to read, isn't it?

Categories