Cannot get a div's height on Template.onRendered() Meteor - javascript

I'm trying to get the higher height of some the "big-card" in my DOM to put them all at the same height.
{{#each skills}}
<div class="big-card">
<div class="card-grid add-option-part">
<div class="card-text">
<p>{{this}}</p>
</div>
</div>
<div class="option-part">
<div class="half-option-part white-line-part"><img class="seemore-button" src="/img/expand.png"/></div>
<div class="half-option-part">{{> StarsRating}}</div>
</div>
</div>
{{/each}}
The function to take get their heights is :
function boxContentNormal(){
var elementHeights = [];
$('.big-card').map(function() {
var currentItem = $(this).find('.card-text');
var currentItemHeight = currentItem.height();
var currentItemPaddingTop = parseInt(currentItem.css('padding-top').replace("px", ""));
var currentItemPaddingBottom = parseInt(currentItem.css('padding-bottom').replace("px", ""));
elementHeights.push(currentItemHeight + currentItemPaddingBottom + currentItemPaddingTop);
});
var maxHeight = 0;
$.each(elementHeights, function(i, element){
maxHeight = (element > maxHeight) ? element : maxHeight;
});
console.log("Max height : "+maxHeight);
}
It's called by that :
Template.MyTemplate.onRendered(function(){
boxContentNormal();
$(window).resize(function(){
boxContentNormal();
});
});
This function is used when a new route is called and the template will be displayed at the same time.
It works like that:
I click on a link that goes to a new route
Once arrived to the route, the template will be displayed
When the template is rendered, the function is called for the first time
After that, if the window resizes the function will be called again
The problem is at the third step, when the function is called it doesn't get the height of the cards. Then all the heights are equal to 0. And when I resize the window, it works fine.
So I think the function is called too early and the "cards" don't exist yet. Do you know how I can "wait" for them or another solution ?
Thanks :)

I suppose your skills helper is returning a cursor from a client side collection synced with the server via the Pub/Sub mechanism.
You can use the template controller pattern along with template subscriptions to make sure your template is initially rendered after the published data made its way to the client.
HTML
<template name="skillsController">
{{#if Template.subscriptionsReady}}
{{> skillsList items=skills}}
{{/if}}
</template>
<template name="skillsList">
{{#each items}}
{{! skill item}}
{{/each}}
</template>
JS
Template.skillsController.onCreated(function(){
this.subscribe("skills");
});
Template.skillsController.helpers({
skills: function(){
return Skills.find();
}
});
Template.skillsList.onRendered(function(){
console.log(this.$(".big-card").length == this.data.items.count());
});
Using this pattern, the skillsList template onRendered life cycle event is executed after the data is already there so the {{#each}} block helper will correctly render its initial list of skill items.
If you don't wait for the subscription to be ready, the initial template rendering will run using an {{#each}} fed with an empty cursor. Once the data arrives, the {{#each}} will rerun and correctly render the items, but the onRendered hook won't.

Related

Document does not display only on the first page

I am experiencing this weird scenario that I am unable to figure out what the problem is. There is a pagination for a collection which works fine when navigating. I have 5 documents in a collection with each to display per 2 on a page sing the pagination. Each document has a url link that when clicked it displays the full page for the document.
The challenge now is that if I click a document on the first page, it displays the full record, but if I navigate to the next page and click a document, it displays a blank page. I have tried all I could but haven't gotten what is to be made right.
These earlier posts are a build up to this present one: Publish and subscribe to a single object Meteor js, Meteor js custom pagination.
This is the helper
singleSchool: function () {
if (Meteor.userId()) {
let myslug = FlowRouter.getParam('myslug');
var subValues = Meteor.subscribe('SingleSchool', myslug );
if (myslug ) {
let Schools = SchoolDb.findOne({slug: myslug});
if (Schools && subValues.ready()) {
return Schools;
}
}
}
},
This is the blaze template
<template name="view">
{{#if currentUser}}
{{#if Template.subscriptionsReady }}
{{#with singleSchool}}
{{singleSchool._id}}
{{singleSchool.addschoolname}}
{{/with}}
{{/if}}
{{/if}}
</template>
try this;
onCreated function:
Template.view.onCreated(function(){
this.dynamicSlug = new ReactiveVar("");
this.autorun(()=>{
// When `myslug` changes, subscription will change dynamically.
this.dynamicSlug.set(FlowRouter.getParam('myslug'));
Meteor.subcribe('SingleSchool', this.dynamicSlug.get());
});
});
Helper
Template.view.helpers({
singleSchool(){
if (Meteor.userId()) {
let school = SchoolDb.findOne({slug: Template.instance().dynamicSlug.get()});
if (school) {
return school;
}
}
}
});

Accessing parent 'onRendered' js code from childern generated in each statement

I am quite new to Meteor and Blaze so I am sorry if something is unclear here... But I want to implement this 'Product Quick View' https://codyhouse.co/gem/css-product-quick-view/ in my Meteor project.
The animation is working great when I am not using each statement. In each statement the data context change and when I click cd-trigger nothing happens. How can I access the code inside ParentTemplate.onRendered for this child Template? Is there some workaround to this problem?
<template name="ParentTemplate">
{{> Product}} // works properly
{{#each products}} // does not work
{{> Product}}
{{/each}}
</template>
<template name="Product">
<li class="cd-item">
<img src="img/item-1.jpg" alt="Item Preview">
Quick View
</li> <!-- cd-item -->
</template>
and the .js file
Template.ParentTemplate.onRendered( function () {
//open the quick view panel
$('.cd-trigger').on('click', function(event){
console.log('Hello!');
var selectedImage = $(this).parent('.cd-item').children('img'),
slectedImageUrl = selectedImage.attr('src');
/the code continues but it is very long/
This fixed the problem
Template.ParentTemplate.onRendered( function () {
this.autorun(() => {
if (this.subscriptionsReady()) {
//open the quick view panel
$('.cd-trigger').on('click', function(event){
console.log('Hello!');
var selectedImage = $(this).parent('.cd-item').children('img'),
slectedImageUrl = selectedImage.attr('src');
/the code continues but it is very long/

Reusing the same block helper in Meteor causes odd context issue

I'm attempting to reuse a custom Block Helper that I wrote to provide basic carousel functionality to some of my templates.
simple-carousel.html
<template name="SimpleCarousel">
<div class="simple-carousel {{class}}">
<div class="slides">
{{#each slides}}
{{> UI.contentBlock this}}
{{/each}}
</div>
{{#if showControls}}
{{> SimpleCarouselControls}}
{{/if}}
</div>
</template>
<template name="SimpleCarouselControls">
// control structure here
</template>
simple-carousel.js
var actions = {
back: function() {
// move slide back once
},
forward: function() {
// move slide forward once
}
};
var showSlide = function() {
// code to show the next slide
};
Template.SimpleCarousel.onRendered(function() {
// set up carousel logic here
});
Template.SimpleCarousel.events({
'click [data-sc-move="forward"]': function() {
actions.forward();
},
'click [data-sc-move="back"]': function() {
actions.back();
}
});
breaking_stories.html
<template name="BreakingStories">
{{#SimpleCarousel class="breaking-stories" showControls=false autoForward=8000 slides=breakingStories}}
{{> BreakingStorySlide}}
{{/SimpleCarousel}}
</template>
<template name="BreakingStorySlide">
<div class="breaking-story slide">
<div class=breaking-story-title">{{title}}</div>
</div>
</template>
breaking_stories.js
Template.BreakingStories.helpers({
breakingStories: function() {
return BreakingStories.find();
}
});
daily_summary.html
<template name="DailySummary">
{{#with thisDailySummary}}
{{#SimpleCarousel class="daily-summaries" showControls=true slides=items}}
{{> DailySummarySlide}}
{{/SimpleCarousel}}
{{/with}}
</template>
<template name="DailySummarySlide">
<div class="daily-summary slide">
<div class="daily-summary-title">{{title}}</div>
</div>
</template>
I've tried to simplify the code as there is a lot more HTML involved in the templates. Anyway, as you can see I've defined the #SimpleCarousel block helper and used it in two places: the breaking stories section, and the daily summaries section. These two templates happen to be on the same page (route), so they are near each other on the page. I need one of them to auto cycle through, in which I've provided the autoForward property to the helper, and the other one should just show controls.
Both templates render fine and show the correct data, but the problem lies in that instead of the breaking news template doing any automatic cycling, the other one does (and does it twice), as if they are sharing the same context.
My question is, can I use custom Block Helpers multiple times on the same route safely? I'm open to any suggestions on how to do this a better/different way.
Thanks to #JeremyK for pointing me in the right direction; it happened to be the exact code I left out which was the problem. Of course!
Here's what I had in the old version:
simple_carousel.js
var $slideContainer, $controls, $markers, $activeSlide, $nextSlide;
var actions = {
back: function() {
// move slide back
},
forward: function() {
// move slide forward
}
};
function showSlide() {
// show the "next" slide
}
Template.SimpleCarousel.onRendered(function() {
var data = this.data;
$slideContainer = this.$('.sc-slides');
// rest of this code is irrelevant
});
I had thought that the variables I had declared on the first line were independent of multiple instantiations of the templates I was using, but I was wrong. The first use of $slideContainer = this.$('.sc-slides'); worked fine, but $slideContainer and all the others are shared.
To fix this, I simply moved the local variables/actions into Template.SimpleCarousel.onRendered
Template.SimpleCarousel.onRendered(function() {
var $slideContainer, $markers, ...
this.actions = {
//...
};
});
Template.SimpleCarousel.events({
'click [data-sc-move="forward"]': function( event, template ) {
template.actions.forward();
}
//...
});

Read content of nested template

How can I get JS access to .content of nested <template>?
I am trying to extend <template> with my imported-template element (which fetches template content from external file) and I would like to implement <imported-content> in similar manner to native <content>. To do so, I simply try
this.content.querySelector("imported-content")
but it occurred, that for nested template this.content is empty.
<script>
(function() {
var XHTMLPrototype = Object.create((HTMLTemplateElement || HTMLElement).prototype);
XHTMLPrototype.attachedCallback = function() {
//..
var distributeHere = this.content.querySelector("imported-content");
var importedContent = document.createElement("span");
importedContent.innerHTML = "Imported content";
distributeHere.parentNode.replaceChild(importedContent, distributeHere);
}
document.register('imported-template', {
prototype: XHTMLPrototype,
extends: "template"
});
})();
</script>
<template id="fails" bind>
<ul>
<template is="imported-template" bind="{{ appdata }}">
<li>
<imported-content></imported-content>
</li>
</template>
</ul>
</template>
JSFiddle here
I am not sure if it is a bug, a design issue, or just template shim limitation.
I thought that maybe I am checking it in wrong life-cycle callback, so I tried MutationObserver fiddle here, but mutation does not occur as well.
I changed your selector to this.content.querySelector("[is='imported-content']"). Is this what your trying to do?

Meteor: Hide or remove element? What is the best way

I am quite new with Meteor but have really been enjoying it and this is my first reactive app that I am building.
I would like to know a way that I can remove the .main element when the user clicks or maybe a better way would be to remove the existing template (with main content) and then replace with another meteor template? Something like this would be simple and straightforward in html/js app (user clicks-> remove el from dom) but here it is not all that clear.
I am just looking to learn and for some insight on best practice.
//gallery.html
<template name="gallery">
<div class="main">First run info.... Only on first visit should user see this info.</div>
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
//gallery.js
firstRun = true;
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
firstRun = false;
}
})
if (Meteor.isClient) {
function showSelectedPhoto(photo){
var container = $('#gallery');
container.fadeOut(1000, function(){
Session.set('selectedPhoto', photo);
Template.gallery.rendered = function(){
var $gallery = $(this.lastNode);
if(!firstRun){
$(".main").css({display:"none"});
console.log("not");
}
setTimeout(function(){
$gallery.fadeIn(1000);
}, 1000)
}
});
}
Deps.autorun(function(){
selectedPhoto = Photos.findOne({active : true});
showSelectedPhoto(selectedPhoto);
});
Meteor.setInterval(function(){
selectedPhoto = Session.get('selectedPhoto');
//some selections happen here for getting photos.
Photos.update({_id: selectedPhoto._id}, { $set: { active: false } });
Photos.update({_id: newPhoto._id}, { $set: { active: true } });
}, 10000 );
}
If you want to hide or show an element conditionaly you should use the reactive behavior of Meteor: Add a condition to your template:
<template name="gallery">
{{#if isFirstRun}}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{/if}}
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
then add a helper to your template:
Template.gallery.isFirstRun = function(){
// because the Session variable will most probably be undefined the first time
return !Session.get("hasRun");
}
and change the action on click:
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
Session.set("hasRun", true);
}
})
you still get to fade out the element but then instead of hiding it or removing it and having it come back on the next render you ensure that it will never come back.
the render is triggered by changing the Sessionvariable, which is reactive.
I think using conditional templates is a better approach,
{{#if firstRun }}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{else}}
gallery ...
{{/if}}
You'll have to make firstRun a session variable, so that it'll trigger DOM updates.
Meteor is reactive. You don't need to write the logic for redrawing the DOM when the data changes. Just write the code that when X button is clicked, Y is removed from the database. That's it; you don't need to trouble yourself with any interface/DOM changes or template removal/redrawing or any of that. Whenever the data that underpins a template changes, Meteor automatically rerenders the template with the updated data. This is Meteor’s core feature.

Categories