Ionic 3 : How to preload tabs? - javascript

I'm in a particular case where I absolutely need to make sure that document.querySelectorAll('.scroll-content')[0], document.querySelectorAll('.scroll-content')[1], etc are defined before the user enters the app. In other words : I need to preload my tabs.
The current behavior of tabs :
doc...All('.scroll-content')[0] is defined.
doc...All('.scroll-content')[1] is undefined.
doc...All('.scroll-content')[2] is undefined.
doc...All('.scroll-content')[3] is undefined.
The reason for this "state" is that the content of the page of a tab is loaded only when it is visited.
The behavior I expect from the tabs
doc...All('.scroll-content')[0] is defined.
doc...All('.scroll-content')[1] is defined.
doc...All('.scroll-content')[2] is defined.
doc...All('.scroll-content')[3] is defined.
So I need to be able to load the tabs pages before the user is visiting them for the first time.
Sadly the ionic team did remove the preloading of tabs in beta 12 because supposedly the AOT compilation is fast enough for us "not to need" to preload tabs. 😤
I also tried to set my #IonicPages priority to high and specifying that I want to preload modules in app.module.ts.
No success.
Any idea ?
PS : Please, no ugly stuff like programmatically selecting each tab before showing content. 🤮
EDIT : I'm still looking for a clean workaround, but I also posted a feature request on github. Feel free to participate.

Note that I speak from my own experience.
Background:
I came across very similar approach where I render some content on the page with some external engine and need to make a reference to HTML generated by it within an Angular component. The problem is that putting the code in after view init lifecycle function wasn't enough.
Possible Solutions:
You could use setTimeout to wait for the HTML to be rendered
You can tap into ngDoCheck and check if the HTML has been rendered (if document.querySelector is truthy then you know you can move to your script).
Tapping to `ngDoCheck is expensive (after your initial load, your checks will still be fired). Hence it is better to use a combination of while loop and setTimeout. See pseudo-code below:
-
ngOnViewInit() {
let isReady = false;
do {
if (document.querySelectorAll('.scroll-content').length > 0) {
isReady = true;
}
while (!isReady);
this.runMyCodeThatNeedsThePagePreloaded();
}

If you are using AOT and the ionic team is right (it is fast enough), I could see no reason why you would want to preload your tabs. Except maybe because you want to fetch some data?
If loading data is the case, you could fetch the data in something a tabService which you could inject in each tab.

Related

Dynamic DFP Javascript

I'm sure this has been asked and answered before but I haven't been able to track down an answer so I'm asking again.
I'm trying to implement Google DFP. I was able to dynamically create inline javascript that was serving ads, so I know the admin side should be configured correctly.
We don't have any inline JS in our application though, so I'm trying to build it out dynamically with JS and I haven't had much luck.
I've pasted my JS here. Worth noting is that the top part (prior to ///// !EVENTS) is loaded immediately, whereas the bottom part is loaded inside a $(document).ready() call in case that could be a culprit.
The idea with the code is that each advertisement div has data attributes with the url, size, and id of the advertisement. Using this I loop through each add, defining the slot, once all slots are defined, I display all slots, and once that happens, I call the remaining DFP functions. Sounds like it should work (to me anyway), but its not.
Each console.log() call is occurring as expected, and when I inspect the googletag object logged by that last call, the debug log contains entries like: "Created slot: /[userid]/[ad div id]","Google service JS loaded","Created service: publisher_ads", "Associated publisher_ads service with slot /[userid]/[ad unit id]"; for each record.
I'm not sure if perhaps the Google service JS or created service calls happening for each ad unit is causing problems or expected behaviours, but nothing is being logged to the console except what I've logged and there doesn't seem to be any indication that I've found that something has gone wrong, its just not serving ads.
In the documentation, and in the version of the implementation I had working, there were inline scripts that did the display calls. I'm not sure if perhaps this has to be handled inline and somehow displays the results in the parent of the script tag that called it or something, but even when I make the display calls inline it isn't working, with no more or less logging going on, which leads me to believe something isn't working correctly during the defineSlot() step, but like I mentioned the googletag object's debuglog does indicate the slot was defined.
In my network pane, it seems that there is two calls to Google at http://partner.googleadservices.com/gampad/google_ads_gpt.js and http://www.googletagservices.com/tag/js/gpt.js which returns the required Javascript, but those are the only calls to Google being made (relating to ads, there is other analytics calls) and there are 4 ads on this page that should be loading so I would expect there would be more calls if the ads were actually attempting to be served.
Needless to say I'm at a loss. I'm sure the answer is staring me in the face but I haven't worked much with DFP, or ads at all really, so I'm not sure where else to look to track this down.
Thanks in advance for any direction.
I've actually got a working script which I'm pretty sure does what you are after.
https://github.com/coop182/jquery.dfp.js
You should be able to get some pointers from my script but just from looking at your codepen, one issue which I have spotted is that you are not using the actual DOM id of the adunit div which DFP will need. Anywhere you are using:
$(that).attr('data-id');
should be:
$(that).attr('id');
The calls to googletag.display also need to come after
googletag.pubads().enableSingleRequest();
googletag.enableServices();

WP - JS - Get Instance of Tinymce Editor

I'm creating a Wordpress plugin, which adds a metabox right under the post editor containing a button. The plugin also loads a Javascript file right below the closing </body> tag.
PURPOSE
At the moment, what I am trying to achieve with the plugin is simple. When a user enters content to the editor and then clicks the button inside the metabox, I want to modify the editor's content.
JS CODE
In its simplest form:
jQuery(document).ready(function($) {
$('#button').on('click', function(e) {
e.preventDefault();
var editor = tinyMCE.get("content");
editor.setContent(some_content);
});
});
PROBLEM
The problem is that editor variable returns undefined.
FIREBUG (when trying to set var editor)
wpActiveEditor : "content"
editors : [ ]
activeEditor : null
WHAT HAVE I TRIED
I have tried many, many things (also many small tweaks) found on Tinymce's documentation and here on Stackoverflow but the problem still remains the same.
Any help would be very appreciated.
PS. The content textarea is visible when running my tests.
When the Editor first loads with the "Text" mode active, tinymce does not get initialized, therefore you cannot use tinyMCE.get(), as opposed to the "Visual" mode.
(I hadn't noticed that it actually works on the "Visual" mode, as I was keep testing it on the "Text" mode)
So, a conditional statement is necessary to determine first which tab is active. I solved my problem with this method:
function setEditorContent(val) {
var $_editorTextArea = $('#content');
$_editorTextArea.is(':visible') ? $_editorTextArea.val(val) : tinyMCE.get('content').setContent(val);
}
Hope this answer will prevent some headaches :)
Well, a live example would help a lot.
This way i can only guess: It looks a bit as if you cannot get the editor you want.
There are two possible reasons that come into my mind:
The editor id you are using is not the id of your editor
To verify this you check the id of your editors soure html element (in most cases a textarea).If there is no id set tinymce will use "content" as default.
There iy no editor initialized at all
To verify this you can use console.log(tinymce.editors) in your javascript console. If no editor is initialized then you will get an empty array.
Many years later but maybe this will help someone...
In addition to everything said above some consideration needs to be paid to the JS event model. Consider:
TinyMCE may not initialize (and the tinymce global may not be available) until the document is done loading. The OP correctly wrapped calls in jQuery(fn), which will solve this. This is relevant if you're using an added framework that initializes and tries to manipulate the editors (like AngularJS directives).
Parts of initialization seem to be asynchronous so even if you wrap everything in jQuery(fn) the editors may not be available until later. WP loads Underscore as part of Backbone so wrapping initial attempts to locate editors in _.defer(fn) seems to get me lots of mileage. This could be done with the native JS setTimeout as well.
Beyond the fantastic answer by #m.spyratos, it may be helpful to note that you can hook mode change events (Visual/Text) by adding a jQuery click event handler to button.switch-tmce[data-wp-editor="my_editor_id"] and button.switch-html[data-wp-editor="my_editor_id"] for when the user selects Visual or Text, respectively. Your version may vary but I found that the textarea goes away when switching to Visual mode and the tinymce.editor instance goes away when switching to Text mode. Hooking to these events gives a persistent means to re-hook when the user decides to change modes.
As a quick reference, you can attach to the editor object (activeEditor or something in editors[], which is keyed by editor ID) to receive any and all changes in visual editor content with by hooking to the editor with editor.on('NodeChange keyup', fn) and a single event callback. I included blur in my solution as well, for posterity. The text editor content can be hooked with jQuery('textarea#my_editor_id').on('keyup', fn).
I have successfully managed multiple editors on a page that are entirely two-way bound entirely through JS; the editors are created with wp_editor and no initial content then loaded asynchronously (via AJAX in my case) and managed through multiple edit cycles without a server round-trip. This is possible, if not slightly convoluted.

Dynamically loading Javascript and maintaining objects

I am building an ajax based website, where almost every page has it's own Javascript file which is being loaded along with html.
I have couple questions if I'm doing things in a right way, so let's get started :)
First of all, the way I execute the loaded script. All of my code is encapsulated in a function, so say I have loaded the register page, what i would do is:
page_class = "register"; //this will change when new page is loaded
new window[page_class];
So this works the way I want it to work, but I'm concerned about one thing, when the user comes back from the other page to register one more time and new object are going to be created one more time will it create memory leaks or some other bad things? If so what can I do to rerun my whole script?
My next question is fairly simple, I attach my jquery events using on and when I switch the page I loop through all elements that will be removed and detach every element, is that enough to prevent memory leaks? If not what could i improve?
So considering what we've figured out from the comments, here's my 2 cents:
I would suggest making your site sectionally ajaxed. By that I mean all music areas as one subsection, videos in another, etc. So within each section you ajax around and when transitioning to another section you gracefully fade and naturally page load the next.
.detach() in my opinion is not enough to ensure proper memory handling since detach keeps the removed section referenced. Good for when you want to recall it but useless if the user just clicked an area and backed out never to return. Now you have this reference to an entire section stored in memory.
How to handle memory in each section? You can maintain section objects. An example:
var music = {
loaded: 0,
current_track: null,
init: function(){
if(this.loaded == 0)
{
$.ajax... get your music section html and write it to the #music element
this.loaded = 1;
}
},
selectTrack: function(id)
{
// just an example
}
};
while maintaining a constant site object:
var site = {
init: function(){
// call selected page, etc
},
move: function(page){
if(page == 'music')
{
music.init();
$('#content').animate(...);
}
}
};
$(document).ready(function(){
site.init();
});
Where the site obj is initted on page load and when the move function is called it calls the init method for the respective object, which does nothing if the page has already been loaded. This example obviously depends on there being a #music element as well as a container element called #content. This part could be totally irrelevant depending on how you handle transitions. All your call, but the general idea is the same. You can always set display to none to avoid rendering bottlenecks for complex markup.
You mentioned, at least briefly, that the project was expected to pretty big. Not to be too pessimistic, but "big" is defined after the fact and not in the plan. Unless you have millions of dollars to throw at this. I'm not trying to dissuade you. By all means, make your site as you expect it to be. But consider that scaling up when demand calls is a much more sane approach than going full throttle fancy before anyone knows about it.
I only say this because I've worked for clients who have thrown millions into the gig claiming it will be the next youtube and 6 months later they were gone. Where they focused on stability and security, they failed in design and marketing. That - and pretty much that alone - is why I suggest making only sections ajaxed. It keeps it compartmentalized, the focus is easy to keep track of and you only have that one piece to worry about. When you get 10,000 users, consider going full ajax. When you get there you may also know why it might be a better move to consider ipads and tablets over a fancy ajax site. Your cool transitions and fades will be a wounded dog on mobile devices. Considering the direction things are going, is that what you want?
Again, this is only my opinion. I don't know your site or skill set or even whether or not you have already considered all I mentioned. If you have any specific questions I can try to answer or update this post.
Good luck.
Regarding the js options mentioned:
Standard jquery will likely suffice, unless you have time to fully understand something like bootstrap
Node.js is awesome but you need to make sure your server can deploy it. Dreamhost, for example, does not support it. At least they didn't last I checked. They're a good example of a decent, common host. Same for Media Temple, I think. Please correct me if I'm wrong.

How is it possible to run multiple instances using Backbone.Paginator.js?

I am trying to use Backbone.Paginator.js to run more than one app (multiple instances of paginator) on the same page.
I created a test page. (Navigate to backbone.paginator/examples/netflix-infinite-paging).
I left the code as is for app.js and create app2.js, which is a clone of app.js but all the javascript code is located in one file and the app has been renamed to app2.
Two instances work on first load of the page but subsequent request/refreshes only load app2.js's data.
Is it possible to run multiple instances on the same page?
I am interested in using an auto-paging (infinite/endless scroll) so
I tried to use Paul Irish's jQuery Infinite Scroll plugin but
I am unable to get it to work.
I am initiating the plugin to run on document ready (which does not
work, as expected), but also running the code in the app2's
ResultView, which does not work as well.
Any ideas on how to get an auto-paging infinite scroll solution?
I ran into https://github.com/joneath/infiniScroll.js but I am not
sure how to integrate it with Backbone.Paginator.js.
I am still learning and any help would be greatly appreciated! :)
UPDATE: After further testing across different browsers, it seems like the problem might be to caching issue/differences. For example, in Safari, it works sometimes (randomly) when refreshing the page. I am not sure how to debug that. Any ideas?
Questions:
1- Are you including the jQuery Javascript framework dependencies as well in your codebase?
2- I have downloaded the zip file, ran it on Xammp locally and it appears to be a downloaded demo not a test page, can you please confirm which page is your test page from the compressed file attached to your question?
3- Can you create a mockup (in case that there is some server side code happening) in jsfiddle?
4- The link provided for the Infinite scroll jquery plugin is broken, it should be: https://github.com/joneath/infiniScroll.js
If you want to make the jQuery paginator plugin to be independent, you might want to trigger it considering the container element as well
....
From the suggested link, I think that we should experiment with this. Else you might want to create a cookie or something for the browser to remember the changes to the plugin on multiple instances.. Here are some thoughts?
1#
Backbone.InfiniScroll(collection, **options**)
Instantiate a new InfiniScroll object after your Backbone view has been rendered.
myView = Backbone.View.extend({
initialize: function(){
_.bindAll(this, "render");
this.render();
this.infiniScroll = new Backbone.InfiniScroll(this.collection, {success: this.appendRender});
}
)};
2# At a glance from the Options menu
target: $(window),
Perhaps we should try:
$(body).find('#container1'),
-or-
$(window).children('div').hasClass('container'),
Just some ideas, haven't experimented it myself-
3# You might want to make (1) to be a javacript function and trigger it based on a class or on it's id for initializing the scroll over a desired container.
That's all the ideas I could come up by taking a look real quick, but feel free to reply if it helps out or at least gives some direction.
4# Another thought is that myView can be a variable with an id of a timestamp in Javascript, that way you can ensure uniqueness and since you are calling new then you could have several instances of the plugin running for your view.

How to use page-mod to modify element loaded by JavaScript

I'm creating firefox addon to add onclick event to the specific button. ("input" element)
The button is placed in http://example.com/welcome#_pg=compose
but when I open the page, following error occures:
TypeError: document.querySelector("#send_top") is null
#send_top is id of the button which I want to modify. So, the button is not found.
This error occurs because http://example.com/welcome and http://example.com/welcome#_pg=compose is completely different pages.
In this case, the addon seems loading http://example.com/welcome but there is no button whose '#send_top' ID.
When #_pg=compose anchor is added, the button is loaded by JavaScript.
How can I load http://example.com/welcome#_pg=compose to modify the button?
Three thoughts to help you debug this:
to correctly match the url you should consider using a regular expression instead of the page-match syntax - this might allow you to react to the anchors in a more predictable way
I've found that when using content scripts with pages that are heavily modified by JS, you can run into timing issues. A hacky workaround might be to look for the element you want and, if it isn' there, do a setTimeout for a 100 milliseconds or so and then re-check. Ugly, yes, but it worked for some example code I used with the new twitter UI, for example.
You can use the unsafeWindow variable in your content script to directly access the page's window object - this object will contain any changes JS has made to the page and is not proxied. You should use unsafeWindow with great caution however as its use represent a possible security problem. In particular, you should never trust any data coming from unsafeWindow, ever.

Categories

Resources