jQuery widget (jPanelMenu) stops working on meteor route change - meteor

I have implemented jPanel Menu via a rendered template, which works great, until a route has been changed, then the menu stops working. Here is the code I am using to evoke the plugin.
Template.mobileMenu.rendered = function(){
var jPM = $.jPanelMenu({
menu: '#mobile-menu',
trigger: '.menu-trigger'
});
jPM.on();
};
The template is loaded on all pages in the footer. I am thinking it needs to be rerun on route change, OR prevented from being rerun. I am not sure which. Thanks for any tips.

This sounds similar to the problem many people have with using 3rd-party ui components. I put together a working example of using a modal dialog component which may be of some help:
https://github.com/alanning/meteor-modal-example
Also I should point out that the new Meteor UI rendering system, "Blaze", should eliminate these kinds of issues. I would expect Blaze to be released soon.
(For those visitors coming from the future, at the time of writing Meteor v0.7.0.1 is the latest version.)

The solution was to wrap it in an if not rendered, to prevent it from being re-rendered on template / route changes.
Template.mobileMenu.rendered = function(){
if (!this.rendered){
var jPM = $.jPanelMenu({
menu: '#mobile-menu',
trigger: '.menu-trigger'
});
jPM.on();
this.rendered = true;
}
};

Related

FlowRouter without page reload

I am following this example https://kadira.io/academy/meteor-routing-guide/content/rendering-blaze-templates
When I click on my links the whole page is being reloaded. Is there any way to load only the template part that is needed and not the whole page?
Edit: Also I noted another problem. Everything that is outside {{> Template.dynamic}} is being rendered twice.
Here is my project sample. https://github.com/hayk94/UbMvp/tree/routing
EDIT: Putting the contents in the mainLayout template and starting the rendering from there fixed the double render problems. However the reload problems happen because of this code
Template.mainLayout.events({
"click *": function(event, template){
event.stopPropagation();
console.log('body all click log');
// console.log(c0nnIp);
var clickedOne = $(event.target).html().toString();
console.log('This click ' + clickedOne);
//getting the connID
var clientIp = null // headers.getClientIP(); // no need for this anymore
var clientConnId = Meteor.connection._lastSessionId;
console.log(clientIp);
console.log(clientConnId);
Meteor.call("updateDB", {clientIp,clientConnId,clickedOne}, function(error, result){
if(error){
console.log("error", error);
}
if(result){
}
});
}, // click *
});//events
Without this event attached to the template the routing works without any reloads, however as soon as I attach it the problem persists.
Do you have any ideas why this code causes such problems?
EDIT 2 following question Rev 3:
event.stopPropagation() on "click *" event probably prevents the router from intercepting the click on link.
Then your browser performs the default behaviour, i.e. navigates to that link, reloading the whole page.
EDIT following question Rev 2:
Not sure you can directly use your body as BlazeLayout target layout.
Notice in the first code sample of BlazeLayout Usage that they use an actual template as layout (<template name="layout1">), targeted in JS as BlazeLayout.render('layout1', {});.
In the tutorial you mention, they similarly use <template name="mainLayout">.
That layout template is then appended to your page's body and filled accordingly. You can also change the placeholder for that layout with BlazeLayout.setRoot() by the way.
But strange things may happen if you try to directly target the body? In particular, that may explain why you have content rendered twice.
Original answer:
If your page is actually reloaded, then your router might not be configured properly, as your link is not being intercepted and your browser makes you actually navigate to that page. In that case, we would need to see your actual code if you need further help.
In case your page does not actually reload, but only your whole content is changed (whereas you wanted to change just a part of it), then you should make sure you properly point your dynamic templates.
You can refer to kadira:blaze-layout package doc to see how you set up different dynamic template targets in your layout, and how you can change each of them separately (or several of them simultaneously).
You should have something similar in case you use kadira:react-layout package.

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());
}
});
});

jQuery component stop working when routing

I'm using Bootstrap Select in Meteor (using this package if relevant: https://github.com/amrali/bootstrap-select-meteor), but everytime I switch page and go back (IronRouter) the select has reverted to a standard HTML select.
I initialize it in the rendered callback but it's only called once and breaks when navigating:
Template.temp.rendered = function() {
$('.selectpicker').selectpicker();
};
How can I make sure it's always inited when the template is shown?
I can think of a few hackish ways of solving it but I'm sure there's a better way..
I've seen a similar issue, it makes me think that when you are "changing pages" you are not actually "destroying" the template temp, as the selectpicker() doesn't get called again when you "change back". You don't show enough information to answer this accurately, but you can try
Destroying the selectpicker() during temp.onDestroyed
Initializing selectpicker() in every template you need it for

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

Unable to trigger modal-dialog show in Meteor template

I have a modal dialog in my template. This dialog needs to be triggered from the code programatically. So I need to show the modal through javascript, as I cannot have a data-toggle button to launch the modal-dialog.
The modal was working with bootstrap but with bootstrap-3 its not showing up, even though I can show it from the console directly. the problem here is how can I execute javascript post the template render, to launch the modal-dialog.
There is a Template.rendered/created function which is called, and inside this this.autorun(runFunc) is supposed to run the code to update the DOM element. This is called correctly, but I still cannot trigger the modal to show-up.
Template.createDialog.created = function() {
console.log("teamplate created");
this.autorun(function(){
$('#myModal').modal('show');
});
};
Update:
This works:
Template.createDialog.rendered = function() {
console.log("teamplate created");
this.autorun(function(){
$('#myModal').modal('show');
});
};
Using the rendered function, I am able to trigger the modal to show up. But the problem is that rendered and created both are only called once. And I need a way to trigger the modal dialog consistently if a condition is reached.
This bootstrap modal dialog with meteor is turning out to be painful and hacky. Is it not possible to show/hide modal using some class parameters?
Modals can be tricky to get right in Meteor for exactly the reasons you've discovered. I don't use Bootstrap, but the basic principle is that you need to trigger the modal programatically so that you can run the relevant framework code once you know the html has been rendered but still retain reactivity (this is certainly the case with Foundation and Semantic-UI modals) .
In your use case (which appears to be a single modal), this shouldn't be too much of a problem. Set a reactive variable modalVisible (a Session variable or similar), and use that to show or hide the modal as required.
this.autorun(function(c) {
if (Session.get('modalVisible')) {
$('#myModal').modal('show');
} else {
$('#myModal').modal('hide');
}
});
If you put all of that in the rendered callback then it will only try to show the modal once it's been added to the DOM (without which you'll get an error and the computation will stop running, breaking reactivity). Note that you shouldn't make rendering of the template dependent on a reactive variable - it should always be rendered but only visible based on the value of the modalVisible Session variable.
Apologies if this is too simple for your use case - if so I would recommend investigating the several packages on Atmosphere for Bootstrap modals as others will almost certainly have faced the same problem.

Resources