I have a Meteor app for storing my bookmarks. To do that there is a Links collection with a count attribute. Every time I click the bookmark, the external site must be visited, but also the count must be increased with 1, so I can sort the bookmarks with the most visited at the top.
So now I have a link with the info in it:
{{title}}
Should I use a template instead, update the count and do a Router.go to the external site?
A little modification:
{{title}}
Then you could use:
Template.body.events({
'click a.tracked' : function(e) {
var href = $(e.currentTarget).attr('href');
ClickedLinks.insert({href: href, when: new Date()});
}
});
Where ClickedLinks is a collection. The main idea is that we track all click events on meteor body as a catch-all since everything is rendered in the body anyway even if it's in another template.. everything still winds up getting rendered there. Then we try to depend on events propagation to do our magic.
Here it is in action:
http://meteorpad.com/pad/Wtz6autqgCDEaJTLw/Leaderboard
Related
Google Tag Manager (GTM) has a built in trigger called "Just Links". In my VueJS application, using Vue Router, GTM fires a "History Change" event before firing the "Just Links" trigger.
(the "History Change" event has nothing to do with page view events)
Because of this, the Page Path GTM data-layer variable, which is supposed to be the path that the event was triggered on, is the same value as the Click URL GTM data-layer variable, which is the href value in the <a/> tag.
For instance:
User is on /support
User clicks on link to /about
Vue Router update browsers history
History Change event fires and updates all the internal values of Google Tag Manager data layer (including location and page path)
"Just Links" event fires, Page Path and Click URL values are now both /about
I'm assuming GTM/Google Analytics have some type of built in deferment strategy in place to not interfere with other things running on the main Javascript thread. So Vue Router changes routes (hence triggering the history change) before GTM/Google Analytics fire the "Just Links" trigger event.
(Strangely enough, when you use the built in Click - All Elements in GTM, the events fire in the right order.)
Has anyone else encountered this issue and come up with any type of solution? There might be a solution with just using Click - All Elements but that by default doesn't traverse the DOM tree with <a/> and strip the href for the Click URL value, which means extracting the href value on nested elements within an <a/> doesn't work. However, Just Links does do this.
Thanks!
You can look at tag firing priority. Adjust the click tag to have higher priority than the tag that's dependent on the history change trigger.
I just ran into this problem due to a site update. Instead of switching all of my "Just Links" to "All Elements" and dealing with the many headaches, I created a new Variable to reference for the page path instead of the built-in "Page Path" Variable. This Variable is basically storing what "gtm.linkClick" now treats as the previous page path because of the History Change. I'm just at beginner-level JS, so I'm sure there is a better way to write the code, but it works.
The first step is to create a new Custom HTML Tag (name it whatever you want) set to fire on All Pages and History Changes. The Tag will first attempt to store the current page path into Session Storage, or fallback to a cookie if Session Storage is not accessible. I used the {{JS - setCookie}} Variable method that was created by Simo Ahava to set cookies. There is a 2 second delay before writing the page path to storage, which is plenty of time for the "Just Links" Trigger to fire and receive the "correct" value before it is overwritten on the next pageview or history change. I gave the sessionStorage and cookie the same name "gtm.truepage".
Custom HTML Tag
<script>
function truePage() {
try {
var page = document.location.pathname;
var storage = window.sessionStorage;
if(storage) {
setTimeout(function() {
storage.setItem("gtm.truepage", page);
},2000);
} else {
setTimeout(function() {
{{JS - setCookie}}("gtm.truepage",page,1800000,"/",window.location.hostname);
},2000);
}
} catch (e) {
}
}
truePage();
</script>
Custom HTML Tag Screenshot
1st Party Cookie Variable
The next step is to create a new 1st Party Cookie Variable named "gtm.truepage". This Variable will be used as a reference for the Custom Javascript Variable. Therefore, if you change the name, you will also need to change the name in the next part.
Cookie Variable Screenshot
Custom Javascript Variable
The final step is to create a Custom Javascript Variable (whatever name you want) that you will use for your "Clicks - Just Links" Trigger. The script will first attempt to retrieve the page path that was set by the Custom HTML Tag from Session Storage and then try the 1st Party Cookie Variable.
function() {
return window.sessionStorage.getItem('gtm.truepage') || {{gtm.truepage}};
}
Custom Javascript Variable Screenshot
You can add an after hook to your router to create a custom path variable like this:
router.afterEach((to) => {
window.vuePath = to
}
Then in Google Tag Manager create a new User-Defined Variable with the JavaScript Variable type called Vue Path. Set Global Variable Path to window.vuePath.
Now you can use Vue Path instead of Page Path in your Triggers to get the correct page path.
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.
I'm implementing an infinite scroll on Meteor in order to display a grid of pictures linked to a large collection.
When the user is at the end of the page I subscribe to more elements and I increase the number of pictures displayed (through my template.helper).
//SERVER
Meteor.publish('generalMusics', function(limit){
return Musics.find({}, {limit: limit});
});
//CLIENT: when the template is created and when the limit of data increases
//it subscribes again
Template.t_elementSubHeader.onCreated( function() {
Session.set('reqLimit',50);
var self = this;
//Everytime reqLimit changes I redo the subscribe
this.autorun(function(){
self.subscribe('generalMusics', Session.get('reqLimit'));
});
//CLIENT: more elements are sent to the template when reqLimit increases
Template.t_elementSubHeader.helpers({
data: function() {
return Musics.find({}, {limit : Session.get('reqLimit')});
}
});
//Also on client, when the user reach the bottom of the page
Session.set('reqLimit',Session.get('reqLimit')+50);
It works well but all the template elements are re-rendering and it also takes some time to do so. It's very inconvenient for the user, I think it takes time because I'm displaying pictures and not text (we already compress the pictures to a minimum size).
The problem is due to the subscribe that rerender all the template elements.
How can I do to just add the new elements and prevent the re-rendering of the already displayed elements when I subscribe ?
My app will be on mobile devices so I can't subscribe to a lot of elements and then just increase the limit in the template helper.
Finally I got it, i've added some code in the html to wait the subscription to be ready and I forgot about it.
I removed:
{{#if Template.subscriptionsReady}}
{{> Template.dynamic template="t_elementList" data=tabData}}
{{/if}}
Infinite scroll is working like a charm.
I've got this in my routes:
Router.route('/videos/:id', {
name: 'VideoPage',
data: function(){
return Videos.findOne(this.params.id);
}
})
This template shows up at the route above:
Template.VideoPage.helpers({
'videoIds': function(){
var myVideoIds = [
"23456",
"h5e45t",
"f4e4w",
"g6h4h"
];
return myVideoIds;
}
});
HTML:
<template name="VideoPage">
{{videoTitle}}
<p>Click the links below to get to a new video page</p>
{{#each videoIds}}
<a href="/videos/" + {{this}}>
{{/each}}
</template>
When I click on a link, the URL in the browser changes from something like /videos/23456 to something like /videos/f4e4w, but the request never actually goes through Iron Router and the Router.route function. I put a debugger before Router.route and it triggers on initial page load but does NOT trigger when the links are clicked.
I understand that Iron Router's default behavior is NOT to re-render a template the user is currently on, but this is the same template with different url params that are used to change the data in the template, so IMO it should still re-render.
Ok, false alarm. It appears that Router.route DOES fire and updates the data context every time the params._id changes. I was placing the debugger outside of the Route function when I should have been placing it inside of the data context function.
The example that I gave in this answer was also a highy, highly simplified example. It wasn't working in my real-life project due to something else (a complex video iframe generator) that was being refreshed improperly.
But rest assured that going from /videos/f4e4w to /videos/23456 by clicking a link DOES still go through Iron Router and the params does get read and the data does get updated.
I have a Meteor app using iron-router which displays rosters in tabs (example here). I would like the URL to include a hash that depends on which tab is showing, ie. http://example.com/myrosters#first.
I have event code like:
Template.roster.events({
'mousedown .nav-tabs li a': function (evt) {
# want to set the URL here
}
});
How do I set the URL here to include a hash, without actually triggering an unnecessary page reload? (I believe Router.go() would trigger a page reload.)
Thanks!
If the path stays the same then it seems go doesn't reload the page.
Router.go(window.location.pathname+"#test3")
When I execute the above, I see no activity in the network tab of the developers console, so I think this should work for your purposes.
Instead of Router.go You Can Use Router.url:
Router.url('yourrouterpathname', {_id: id}, {query: 'q=s', hash: 'hashFrag'});