Reinitialize library when new data is available - meteor

I'm currently developing a Meteor application where I use the video.js-Library.
I have the following template:
template(name='foo')
.video.embed-responsive.embed-responsive-16by9
with richMediaContent
video#video.video-js.vjs-default-skin.vjs-big-play-centered(controls='' preload='auto')
source(src='{{video.videoUrl}}' type='video/mp4')
p.vjs-no-js {{i18n 'videoTagNotSupported'}}
Initializing the video.js-Library after the template is rendered works fine.
Template.foo.onRendered ->
videojs document.getElementsByClassName('video-js')[0], {}
But the videojs-Library is not reinitialized if the same template is rendered with a different video (with a different richMediaContent).
I've already tried to move the video-Part in an own template and included it in the foo-Template so that the onRendered-Call should get called every time a new video is loaded. But this doesn't seem to work.
Do you have any idea how I can reinitialize the library if the video changes?
Thanks in advance!

New answer
Indeed, when your route changes but uses the same template, the said template does not get rendered again, therefore your js plugin call will not trigger a second time. What you can do instead is call your js plugin in an onAfterAction call, within your route definition:
Router.route('/video/:_id', {
name: 'video_page',
template: 'foo',
// ...
onAfterAction: function () {
videojs document.getElementsByClassName('video-js')[0], {}
}
});
Previous answer
I think you are looking for the almighty this.autorun. At the end of your onRendered function, it should look like this (I type it in pure javascript)
this.autorun(function () {
var video = Session.get("video"); // reactive data
videojs document.getElementsByClassName('video-js')[0], {}
});
The idea is that the first line must include, within the autorun function, a way to get your reactive data. In that case, I use the Session which is reactive. Collections are also reactive, so another way would be something like Videos.findOne();. This will depend on how you get that video element.
What this does is that any time the reactive data changes, the callback for this.autorun will run again, and your video plugin will be reset.

Related

Using `within` in custom helpers

I'm using CodeceptJS and I'm trying to write a custom helper that asserts an text and clicks "OK". This dialog pops up as a iframe modal to consent with cookies.
If I write following steps in my scenario
I.amOnPage('/some-path');
within({frame: '#iframeID'}, () => {
I.see('Headline text for dialog');
I.click('OK');
});
// ...
...my test seems to work just fine.
But when I make an custom helper out of that and configure it properly so I can use it:
const { Helper } = codeceptjs;
class CookieConsent extends Helper {
consentWithCookies() {
const { Puppeteer } = this.helpers;
within({frame: '#iframeID'}, () => {
Puppeteer.see('Headline text for dialog');
Puppeteer.click('OK');
});
}
}
module.exports = CookieConsent;
...and use it as a step:
I.amOnPage('/some-path');
I.consentWithCookies();
// ...
...it doesn't seem to work as the consent dialog doesn't get clicked away as it was when implementing this directly in the scenario. According to some console.log() debugging the within callback doesn't get called at all. Console doesn't throw any errors about undefined within or anything suspicious.
I suspect that using within in a custom helper isn't working or I'm doing something wrong that I can't figure out from the documentation.
This warning at documentation doesn't really clarify when within is being used incorrectly, and using await doesn't help the problem.
within can cause problems when used incorrectly. If you see a weird behavior of a test try to refactor it to not use within. It is recommended to keep within for simplest cases when possible. Since within returns a Promise, it may be necessary to await the result even when you're not intending to use the return value.
iFrames can be a pain to work without when it comes down to automation. There are a number of factors that can make an iFrame unreachable to a framework such as cross-domain iFrames, commonly used for increased security on the content served.
Now to fix your issue, all you have to do is use switchTo() - Docs in CodeceptJS which is a function available for all helpers made available. The order should be
I.switchTo('your iframe');
..... some actions here;
I.switchTo(); // You do this so that you get out of the iFrame context when done

How to focus an input on subscriptionsReady using FlowRouter in Meteor

I'm currently getting used to using FlowRouter after a while using Iron Router and trying to set up some best practices. I'm subscribing to my collection at a template level.
Previously I've waited for a template to render using onRendered and then targeted my input field and applied focus(), however I am now trying to only show my template in Blaze when the subscriptions are ready using the following (please excuse the Jade but I think it's pretty clear in this case)
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
form
input(type="text" name="name")
So the basic idea is that until the subscriptions are ready the spinner shows. The issue I'm having is that now even when the template renders, the focus won't apply. I've tried various methods of wrapping it in an autorun call but not sure the best way of trying to target the first field when combined with this approach?
Template.subjectNew.onRendered(function() {
console.log('rendered');
$('input').first().focus();
});
Is it possible?
Many thanks for any ideas.
Your subjectNew is considered rendered even when it is only showing the spinner. Just stick your:
form
input(type="text" name="name")
Into a separate template and then attach your focus code to the onRendered handler of that other template.
template(name="subjectNew")
unless Template.subscriptionsReady
+spinner
else
+myForm
template(name="myForm")
form
input(type="text" name="name")
js:
Template.myForm.onRendered(function(){
$('input').focus()
});
I think using an autorun would be a good approach but then you would have to employ Tracker.afterFlush() to wait to set the focus after the form is rendered.
Something like:
Template.subjectNew.onRendered(function() {
this.autorun(() => {
if (this.subscriptionsReady()) {
Tracker.afterFlush(() => $('input').first().focus());
}
});
});

Component initialization with Iron Router, Jquery, Materialize

Struggling here with what would otherwise be a simple $( document ).ready().
Not sure what I'm doing wrong.
Materialize needs jquery components to be initialized on DOM ready. Finding a way to initialize components on all views is surprisingly tricky.
Here is the online DEMO
From reading the docs: this should initialize everything the sub-templates require:
Template.layout.rendered = function(){
$('ul.tabs').tabs()
}
}
However, this only works on a hard page refresh, and not with links routing the views.
So instead you would have to initialize on each template that element will be used
Template.x.rendered ...
Template.y.rendered ...
Here is the github code
BTW We've tried iron-router events:
onRun
onBeforeAction
onAfterAction
All of these seem to happen before the route's template content is present. I noticed that onBeforeAction required a call to this.next() to go on, I even tried looking for the DOM content after the next call.
I also tried rewriting our routes like this:
Router.route('someRoute', function() {
this.render('someRoute');
// look for DOM content, still not found
});
Just to be clear, the reason this is happening is because your layout is only firing the rendered hook once. When you switch routes the layout template will not be rerendered, only the templates in the yield region will be. The previous template in that region gets destroyed and the next one rerendered. This means you have to run $('ul.tabs').tabs() again for that Template as the DOM elements it contains are rerendered.
Putting that code in the rendered function of the template that uses it works because that rendered hook gets run every time that particular template gets rendered again.
A way you could get around this could be to create a Template specifically for your tabs, like a control in a way, that calls $('ul.tabs').tabs() in its own rendered function. You could then put this control on a template that needed it and pass the required arguments, like number of tabs and content for each tab etc. It's a bit of work though, and I'd only consider it if I had a really large number of templates that used the tab control.

Meteor: Proper way to add 'confirm delete' modal

I want to create a confirm delete popup with Bootstrap 3. Is there any good comprehensive examples how to build one. I am very new to Meteor.
Use whatever example from Codrops, etc, just remember put the JSCode inside a
Template.nameTemplate.rendered = function() {}
So thats telling meteor to load that jscode, when the template has beed rendered and it can load any modal, etc...
So just follow whatever example you want, and just put whatever jQuery plugin etc, inside Rendered function
Also in some case the rendered its not enough, you need to use too,you can see timer docs here, anyways if you are having bad time, feel free to upload, some meteorPad, free nitrous box o repo on github and i can help you (i have a bad time with those modals on meteor to, they are a little trickys =p)
update answer
try to add meteor add iron:router, and on the client /app.js
Router.route('/', function () {
this.render('leaderboard');
});
And keep the same rendered like this.
Template.deleteBtn.rendered = function(){
$('.open-modal').on('click', function(e){
$('#confirm').modal()
.on('click', '#delete', function (e) {
// Remove selected player
Players.remove(Session.get("selectedPlayer"));
});
});
}
UPDATE
So using the peppelg:bootstrap-3-modalPackage, you can easy do the follow
First Create a template with the modal content
<template name="modal">
<!-- Modal Stuff -->
</template>
and easy call it on a Event handler.
Template.example.events({
'click #exampleButton':function(){
Modal.show('modal')
}
})
Now back to this example check this meteorpad from line 1-23 on app.'s and 41-62 on main.html

How can I listen to when a template is rendered in Meteor?

I have some post-processing that I have to do on a list of HTML elements whenever they are updated from the backend. Is there some event (like, onRender) that I can listen to on the template to facilitate this?
For Meteor starting from 0.4.0 preview, you can use Template.myTemplate.rendered for this.
Template.myTemplate.rendered = function() {
console.log('Template has been (re-)rendered');
}
See also this conversation: Callback after the DOM was updated in Meteor.js
I used the technique to set Bootstrap tooltip popup events in the DOM after template (re)rendering in my CoffeeScript port of the Leaderboard example.
This in the template:
{{enable_tooltips}}
Calls this (CoffeeScript) template property method on the client:
enable_tooltips: ->
# Update tooltips after the template has rendered.
Meteor.defer -> $('[rel=tooltip]').tooltip()
''
Template.userDashboard.onRendered(function() {
// Initialize animate panel function
$('.animate-panel').animatePanel();
}
renamed in Meteor 1.0.4 from rendered to onRendered. Also it now takes a callback function.
http://docs.meteor.com/#/full/template_onRendered

Resources