FlowRouter without page reload - meteor

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.

Related

algolia wordpress autocomplete

I'm trying to tweak the WordPress plugin https://github.com/algolia/algoliasearch-wordpress to suit our needs. What we want to be able to do is have a search result that will load the data up in the same page.
I have my WordPress posts and its data being indexed successfully. I have added a search box to the page, and autocomplete is being fired.
Out of the box, the WordPress plugin template wraps the result in an anchor tag and assigns it the URL of the found result. When clicked, this navigates you to that post. However, what I want to do is intercept that click and load the result in the same page without navigating away.
I have removed the href from the anchor. I have also edited the supplied autocomplete.php template where the call to autocomplete:selected occurs. In there I have removed the call to navigate away by removing window.location.href.
Now I have two main issues.
1 - When the user clicks the search result I would like the input to be populate with the title of the item they clicked on. I added this in the autocomplete:selected callback by adding $searchInput[0].value = suggestion.post_title. Which seems to change the value of the input correctly, but as soon as I click away from the input, it is re-set back to the original typed value. So if I type 'may' and click the result 'mayonnaise', the result data can be accessed but the input returns back to 'may'. My function looks this:
/* Instantiate autocomplete.js */
var autocomplete = algoliaAutocomplete($searchInput[0], config, sources)
.on('autocomplete:selected', function (e, suggestion) {
console.log(suggestion);
autocomplete.autocomplete.close();
});
2 - It seems that the autocomplete dropdown does not hide when the user clicks away. To resolve this i've had to use what I think is a bit of a nasty hack with jQuery. I was wondering if this is really required? My code just below the autocomplete:selected call looks like this:
jQuery('body').on("click", function(event){
if (!jQuery(event.target).closest($searchInput[0]).length) {
autocomplete.autocomplete.close();
}
});
Found some answers to my questions.
1 - In order to populate the input with the title of the selected search result I added a call to the setVal method of the autocomplete object. I'[m still not sure why this is required.
/* Instantiate autocomplete.js */
var autocomplete = algoliaAutocomplete($searchInput[0], config, sources)
.on('autocomplete:selected', function (e, suggestion) {
autocomplete.autocomplete.setVal(suggestion.post_title);
});
2 - It looks like the config of the autocomplete object uses the value of WP_DEBUG in order to set the debug value. The options available for the autocomplete component can be found here https://github.com/algolia/autocomplete.js#options. This lead me to find that when debug is set to true, the autocomplete box does not hide on selection. This is to allow for easier debugging and styling of the component.

Print Friendly Page

So I would like to be able to have a print button for entries in our database so users can print an entry via a print friendly "form".
My thought was to create a separate page, add labels and have those labels pull the relevant information.
I know I can add the open widget information via this code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showPage(app.pages.TestPrint);
But I'm running into a few problems:
I can't get the page to open in a new window. Is this possible?
window.open(app.pages.TestPrint);
Just gives me a blank page. Does the browser lose the widget source once the new window opens?
I can't get the print option (either onClick or onDataLoad) to print JUST the image (or widget). I run
window.print();
And it includes headers + scroll bars. Do I need to be running a client side script instead?
Any help would be appreciated. Thank you!
To get exactly what you'd want you'd have to do a lot of work.
Here is my suggested, simpler answer:
Don't open up a new tab. If you use showPage like you mention, and provide a "back" button on the page to go back to where you were, you'll get pretty much everything you need. If you don't want the back to show up when you print, then you can setVisibility(false) on the button before you print, then print, then setVisibility(true).
I'll give a quick summary of how you could do this with a new tab, but it's pretty involved so I can't go into details without trying it myself. The basic idea, is you want to open the page with a full URL, just like a user was navigating to it.
You can use #TestPrint to indicate which page you want to load. You also need the URL of your application, which as far as I can remember is only available in a server-side script using the Apps Script method: ScriptApp.getService().getUrl(). On top of this, you'll probably need to pass in the key so that your page knows what data to load.
So given this, you need to assemble a url by calling a server script, then appending the key property to it. In the end you want a url something like:
https://www.script.google.com/yourappaddress#TestPage?key=keyOfYourModel.
Then on TestPage you need to read the key, and load data for that key. (You can read the key using google.script.url).
Alternatively, I think there are some tricks you can play by opening a blank window and then writing directly to its DOM, but I've never tried that, and since Apps Script runs inside an iframe I'm not sure if it's possible. If I get a chance I'll play with it and update this answer, but for your own reference you could look here: create html page and print to new tab in javascript
I'm imagining something like that, except that your page an write it's html content. Something like:
var winPrint = window.open('', '_blank', 'left=0,top=0,width=800,height=600,toolbar=0,scrollbars=0,status=0');
winPrint.document.write(app.pages.TestPage.getElement().innerHTML);
winPrint.document.close();
winPrint.focus();
winPrint.print();
winPrint.close();
Hope one of those three options helps :)
So here is what I ended up doing. It isn't elegant, but it works.
I added a Print Button to a Page Fragment that pops up when a user edits a database entry.
Database Edit Button code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showDialog(app.pageFragments.FragmentName);
That Print Button goes to a different (full) Page and closes the Fragment.
Print Button Code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showPage(app.pages.ModelName_Print);
app.closeDialog();
I made sure to make the new Print Page was small enough so that Chrome fits it properly into a 8.5 x 11" page (728x975).
I then created a Panel that fills the page and populated the page with Labels
#datasource.item.FieldName
I then put the following into the onDataLoad for the Panel
window.print();
So now when the user presses the Print Button in the Fragment they are taken to this new page and after the data loads they automatically get a print dialog.
The only downside is that after printing the user has to use a back button I added to return to the database page.
1.
As far as I know, you cannot combine window.open with app.pages.*, because
window.open would require url parameter at least, while app.pages.* is essentially an internal routing mechanism provided by App Maker, and it returns page object back, suitable for for switching between pages, or opening dialogs.
2.
You would probably need to style your page first, so like it includes things you would like to have printed out. To do so please use #media print
ex: We have a button on the page and would like to hide it from print page
#media print {
.app-NewPage-Button1 {
display : none;
}
}
Hope it helps.
1. Here is how it is done, in a pop up window, without messing up the current page (client script):
function print(widget, title){
var content=widget.getElement().innerHTML;
var win = window.open('', 'printWindow', 'height=600,width=800');
win.document.write('<head><title>'+title+'/title></head>');
win.document.write('<body>'+content+'</body>');
win.document.close();
win.focus();
win.print();
win.close();
}
and the onclick handler for the button is:
print(widget.root.descendants.PageFragment1, 'test');
In this example, PageFragment1 is a page fragment on the current page, hidden by adding a style with namehidden with definition .hidden{display:none;} (this is different than visible which in App Maker seems to remove the item from the DOM). Works perfectly...
2. You cannot open pages from the app in another tab. In principle something like this would do it:
var w=window.parent.parent;
w.open(w.location.protocol+'//'+w.location.host+w.location.pathname+'#PrintPage', '_blank');
But since the app is running in frame nested two deep from the launching page, and with a different origin, you will not be able to access the url that you need (the above code results in a cross origin frame access error). So you would have to hard code the URL, which changes at deployment, so it gets ugly very fast. Not that you want to anyway, the load time of an app should discourage you from wanting to do that anyway.

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.

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.

jQuery UI Dialog behaves unpredictably

The jQuery UI dialog drives me up the walls. To the best of my understanding, here's how it works:
When you do $('#myDialog').dialog({...}), it copies the #myDialog element and moves it inside this bizarre widget thing at the bottom of your body tag. This is crazy! It will duplicate possibly unique DOM elements (with ids) when it does this.
So what I'm trying to do is make it behave in a predictable way when I refresh the HTML of the original element (#myDialog). If I do this dynamically, sometimes the dialog doesn't open any more:
http://jsfiddle.net/t67y7/3/
Or sometimes the dialog opens with the old HTML (because it's cached at the bottom of the page that way). What is up with this?
Since nobody seems to have any idea how to tame this beastly dialog, here's the best thing I've come up with to date. I'll accept any superior alternatives.
var original = $('#dialogId')[0];
var clone = $(original).clone().attr('id', 'dialogIdClone');
var saveHtml = $(original).html();
$(original).html('');
$(clone).dialog({
... // other options
open: function (){
// add any dynamic behavior you need to the dialog here
},
close: function(){
$(clone).remove();
$(original).html(saveHtml);
}
});
The purpose of this whole craziness is to keep the HTML of the original dialog unique on the page. I'm not really sure why this can't be the built-in behavior of the dialog... Actually, I don't understand why jQuery UI needs to clone the HTML to begin with.
I know this has been posted for a while, but a less extensive way to handle this issue would be:
$('#your-dialog').dialog({
... // other options
open: function (){
// add any dynamic behavior you need to the dialog here
},
close: function(){
}
});
$('#your-dialog').remove();
This is due to dialog widget wants to be able to control the display and will wrap the inner content of the original dialog then create a brand new one at the bottom of the body.
The draw back of this solution is that the dialogs have to be the first to be initialized to ensure all your 3rd party library widget will operate properly.
Why don't you just call $("#dialogId").dialog("destroy") on close function, like this:
$("#dialogId").dialog({
close: function() {
$(this).dialog("destroy");
// you may want empty content after close if you use AJAX request to get content for dialog
$(this).html('');
}
}
The destroy function will remove the decorated code, and your dialog element will not be duplicate next time you show the dialog.
I added a sample code to jsfiddle.net example.
You need to empty the dialog before opening it.
$("#dialogId").html('');
$("#dialogId").dialog({
close: function() {
$(this).dialog("destroy");
}
}

Resources