Force view to reload tvml content on Apple TV/tvos - javascript

I have been working on dynamically generating tvml-templates with very frequently changing content for a tvOS app on Apple TV. Generating the templates works fine, however I have not been able to get the app to update/reload the content of a template when navigating back and forth between views or leaving and reentering the app. Only rebooting seems to reload the tvml template.

Your template will refresh itself automatically whenever you manipulate the TVML within the template document.
If you maintain a reference to the document like so:
var myDoc;
resourceLoader.loadResource(templateURL,
function(resource) {
if (resource) {
myDoc = self.makeDocument(resource);
});
}
you can manipulate the TVML using myDoc and your view will automatically change.
So if your template document includes a "collectionList" and you were to run this code:
//Removes the child elements of the first collectionList
var collectionLists = myDoc.getElementsByTagName("collectionList");
var collectionList = collectionLists.item(0);
while (collectionList.firstChild) {
collectionList.removeChild(collectionList.firstChild);
}
your view would no longer display the UI elements within the collectionList. The view will refresh itself the moment the code is run.

The answer by #shirefriendship pointed my in the right direction (thank you!). As another example, if you wanted to change the text of a single element in a template (such as the description), you would need to use the innerHTML property:
function changeDescription(incomingString) {
console.log("inside the change description function")
if (incomingString) {
var theDescription = myDoc.getElementsByTagName("description").item(0);
theDescription.innerHTML = incomingString;
}
}
This changes the description immediately to the viewer.

If you are using atvjs framework, you can easily create and navigate to dynamic pages which are regenerated while navigating.
ATV.Page.create({
name: 'home',
url: 'path/to/your/api/that/returns/json',
template: your_template_function
});
// navigate to your page
ATV.Navigation.navigate('home');

Set this in the header of your API:
Cache-Control:no-cache
Got it from Apple Docs: https://developer.apple.com/library/tvos/documentation/General/Conceptual/AppleTV_PG/YourFirstAppleTVApp.html
IMPORTANT
When serving JavaScript and XML files from your web server, you often
need to ensure that any changes to your pages are always visible to
the client app. To do this, your server must ensure that the client
does not cache any of the pages. When your server responds to an HTTP
request for a page that should not be cached, the server should
include Cache-Control:no-cache in the HTTP response header.

Related

Set $location in AngularJS / Electron app

Is it possible to change the URL for $location in AngularJS within an Electon app but without implicitly loading that URL? The problem is that Electron is loading index.html (and other resources) locally, which is intended. But then of course it also sets $location to the local file system.
Background: The reason why I need $location to point to the server is that there is some existing legacy code (which I must not change) and this code uses e.g. $location.search. So after the Electron app has started I'd need to set the location correctly, so that this legacy code can work.
UPDATE 17.07.2020
Here is the requested example code:
I'm trying to set the location with window.location = "https://example.com?param1=test" so that the AngularJS function $location.search() returns param1=test. The problem is, as mentioned above, that when setting window.location, Electron loads the index.html from that server and replaces the content of the BrowserWindow. But I want to load those resources (index.html, *.js, *.css locally) I also tried:
window.location.href = ...
window.location.assign (...)
window.location.replace (...)
but all of these are reloading the page as well.
I think you'll want to add an event listener for will-navigate. Docs can be found here. The important piece:
[The will-event event will be] emitted when a user or the page wants to start navigation. It can happen when the window.location object is changed or a user clicks a link in the page.
I'd imagine your main.js file will look something like this, it's bare-bones but I hope you get the idea.
const {
app
} = require("electron");
let window;
function createWindow(){
window = new BrowserWindow(){...};
// etc....
}
app.on("ready", createWindow);
// For all BrowserWindows you make, the inner bindings will be applied to each;
// more information for "web-contents-created" is here: https://www.electronjs.org/docs/api/app#event-web-contents-created
app.on("web-contents-created", (event, contents) => {
contents.on("will-redirect", (event, navigationUrl) => {
// prevent the window from changing path via "window.location = '....'"
event.preventDefault();
return;
});
});
FYI: This event listener is mainly used for security reasons, but I don't see why you can't use it in your case.

How to get uniqueID from firebase across several pages

my project is the survey with several pages. I want to upload answers of several pages into the same uniqueID. Now I can get uniqueID from a page js file, but how can I transport this uniqueID to other js files so I can upload the data to the same uniqueID.
The problem can be reformulated to how have a variable value accessible through out varying pages and js files.
My prefered way to do it - with lots of data - is a base page invisible to the user loading a "global script". This base page loads and unloads all other pages (including js and css files). Since the variable in your case the firebaseID is globally defined it works.
If its not sensible data you can also put it to the LocalStorage (preferable encrypted) This would also help if a user wants to pause the survey and later on to finish it. (You have 5MB on mobile devices and 10 MB on desktop).
More details on solution one:
The easy part is to insert HTML into other HTML. I use server generated views so the base page gets HTML added and removed as needed.
The harder part is the page (view) specific loading of js.In my use case the users can jump around via a navigation until they finally submit and finish. If you only allow sequential moving forward and no backward its easier. I actually mimic a restful behavior. Some building blocks in the base script include
// Some global vars
var statMsg = "";
var navTargetInitial= "app";
var navTarget;
var htmlDir = "app"; // directory of the html templates
var scriptDir = "js"; // directory of the user scripts
var routes = [
{
navTarget: "main",
loadScript: true,
navTitle: "Survey page 1",
navMenutext: "Page 1"
}, {
navTarget: "app",
loadScript: true,
navTitle: "Survey page 2",
navMenutext: "Page 2"
},
....
];
// Then docReady beside other stuff
function docReady() {
// other stuff
nav2Page(navTarget);
}
// here we handover data, put relevant things to sessionStorage
window.onbeforeunload = function (e) {
// Do stuff
};
// here we cleanup
window.onunload = function () {
// cleanup
};
The crucial part is loading the new js after the html is in DOM so an essential part is an eventhandler on the event DOM loaded
document.addEventListener("DOMContentLoaded",
loadJScript(navScript, function(response, status, xhr ){// error handling }
The rest is ajax, error handling, reaction to clicks on going back/ forward in the page history and reacting accordingly to it.
Hope this helps

Chrome extension to refresh a page every minute and run a (javascript) script every time it refreshes

What I want:
My propose is to check if new content was added in a page (that I do not own), so I was thinking to make a script that save the last content added in a cookie and refresh the page every minute: If the cookie doesn't match the last content added, that would mean there is new content and I would receive a notification.
Let's try with pseudocode:
main_file:
include: functions.js;
cookie last_content_added= get_first_paragraph();
//Refresh script
do (every_minute){
page_reload();
}
when.page.reload.complete {
run script_check_content
}
functions.js
script_check_content{
var content_check = get_first_paragraph();
if (content_check == cookie[last_content_added])
{
//do nothing
}
else{
//new content was added
play.notification.mp3
cookie[last_content.added] = get_first_paragraph();
}
}
Am I not thinking in an easier solution for what I'm looking for?
I'm new to chrome extensions, if you could separate the code in different files like it was a real extension, I would appreciate very much.
I recommend to use 'chrome.tabs.query', use this to get all tabs that have the specified properties or all tabs if no properties are specified and 'chrome.tabs.executeScript' to inject the javascript code into a page that calls 'window.location.reload(). to refresh the page.
Here's a sample code to get the current tab and reload it using chrome.tab methods:
chrome.tabs.query({active: false, currentWindow: true}, function (arrayOfTabs) {
var code = 'window.location.reload();';
chrome.tabs.executeScript(arrayOfTabs[0].id, {code: code});
});
Also, include 'onCompleted' listener to listen when it is completely loaded and initialized.
chrome.webNavigation.onCompleted.addListener(function callback).
Take a look at MutationObserver, it provides a way to react to changes in a DOM. You can provide a callback to react to DOM changes and don't need to use a timer.

Getting Backbutton to work in single page website and implementing "speaking" URLs

I have a single page website and would like to achieve the following:
back button working as if it was a normal website
and instead of say,
www.mysite.com/index.php?p=#this-is-a-great-product
I'd like to have this url
www.mysite.com/this-is-a-great-product
while still having back button working properly.
Regarding 1.) I use the following code ive found which works great:
<!-- Getting BackButton to work properly -->
<script type="text/javascript">
var times = 0;
function doclick() {
times++;
}
function doclick() {
times++;
location.hash = times;
}
window.onhashchange = function() {
if (location.hash.length > 0) {
times = parseInt(location.hash.replace('#',''),10);
} else {
times = 0;
}
}
</script>
…but of course it just changes any anchors to /#1, then /#2 and so forth ro get the backbutton to work. But as I'm not a programmer I don't know how to change it… :(
Regarding 2.) i can add in htaccess this:
>RewriteEngine On
>RewriteRule ^([^/.]+)/?$ /index.php?page=$1
and this changes /index.php?p=products to /products.
So how do I change the above code (under 1.) so it doesn't change all anchors to #1, #2, etc. but instead references / uses the urls I achieved under 2, like
www.mysite.com/this-is-a-great-product
And (probably a very dumb question, but a very important one) -given I use only the new url links on my site- is there any danger that this still might result in duplicate content in any way?
Regarding this, should I (for that reason or any other) sefreferential my single page index.php to itself using rel canonical link=index.php?
Thanks so much in advance!
As mentioned, you will want to use the HTML5 History API. Please note, this API is relatively new and therefore browser support is a concern. At the time of writing, approximately 71% of global Internet users have support for it (see http://caniuse.com/#feat=history for browser support information). Therefore, you will want to ensure you have a fall-back solution for this. You will likely want to use the older #! solution that was popular before the HTML 5 History API was adopted.
If you use the history API to replace, for example, example.com/#!settings with example.com/settings and a user bookmarks that nicer URL, then when they go to visit it, their browser will make a request to the server for /settings (which doesn't actually exist in the web server's context). Therefore, you will need to make sure your web server has some redirection rules (i.e. RewriteEngine) such that it can take the pretty URLs and redirect them to the #! version (and then if the user's browser supports the history API it can replace that with the nice URL).
If you aren't very comfortable programming yourself, I'd recommend using a JavaScript library that does a lot of the work for you. I did some quick searching and discovered the following, though there might be better ones out there: https://github.com/browserstate/history.js
Basically i have created a small prototype on jsfiddle which tracks all the urls accessed via ajax calls.
Also contains navigation to access links back and forth .
How It Actually Works:
I have created a global array called history, which keeps track of all urls accessed via ajax in sequence.
also there a global index defined to keep track of the url being accessed when navigating back and forth the links in history array.
There is History section at the bottom of the jsfiddle, which shows the sequence in which the links are accessed by capturing the link names and posting them in the order in which they were accessed.
JS Code:
$(function () {
var history = [];
var index = 0;
$('.links').on('click', function () {
$('#history').append($(this).text());
var address = $(this).attr('data-ref');
index += 1;
history[index] = address;
$('.links').attr('disabled', 'disabled');
loadExternalPage(address);
console.log('list:' + history);
});
$('#back').on('click', function () {
console.log(index);
index -= 1;
console.log(index);
console.log(history[index]);
loadExternalPage(history[index]);
});
$('#forward').on('click', function () {
console.log(index);
index += 1;
console.log(index);
console.log(history[index]);
loadExternalPage(history[index]);
});
var loadExternalPage = function (address) {
console.log(history[index]);
$('#result-section').load(address, function () {
console.log('data-loaded');
$('.links').removeAttr('disabled');
});
};
});
Live Demo # JSFiddle:http://jsfiddle.net/dreamweiver/dpwmcu0b/8/
Note: This solution is far from being perfect, so dont consider it as final solution but rather use it as a base to build upon
On using BACK and FORWARD functions in the browser top-left button:
In principle, there is no great problem with this as long as you work with the existing storage object (a stack) for previously visited web pages on your browser. This object is the history object and you can see what is in it anytime by right-clicking and selecting "Inspect", then selecting the "Console" tab, then enter window.history and enter.
Check out the Browser Object Model (BOM) section of Pro Java For Web Developers (Frisbee) for the background to the history object. (Just a few pages, an easy read, don't worry.) Just remember that in this process you are storing the new page that you move to, not the old page that you are leaving !
For a simple SPA example, look at this example. codepen.io/tamjk/pen/NWxWOxL
In regard to the URL, the method that the history object uses to load a new page state into the history stack, i.e. pushState(...), has an optional third parameter for associating a dummy URL for each web page that is stored.
Personally, when I first sorted out the BACK & FORWARD functions, I did not use dummy URLs as the browser was being confused by them and I had enough to do sorting out the history sequence using just the first two parameters, i.e.
the state object - a JSON holding enough data to recreate the page stored
a title for the page I expect that you could also use a dummy URL but I will leave that to the student as an exercise, as they say.
But you can add the URL of the new page if you want to.
In the example above, for the state object I just used the IDs of the page's nav link and its content element.
For the title, I programmatically changed the HTML's page title element with each change of page. I did this after noticing that the browser listed the previous pages according to the title element in the HTML code.
Unfortunately, this title does not show up on CodePen when you right-click on the browser BACK and FORWARD buttons due to CodePen's system not allowing it. But it will show on your own sites.
It's important that whatever method you use to store current web page states when using the navbar links to navigate, you DO NOT ADD page states to the browser history when you arrive at them using BACK or FORWARD buttons. Otherwise your history stack will have repetitions of entries going back and deletion of entries going forward.
In the CodePen, this was achieved by having the addToHistory(..) function separate to and outside the scope of the switchPage(...) function. This allows you use of the switchPage function in both normal navbar navigation and browser BACK/FORWARD navigation. The third parameter of switchPage(...) is a boolean indicating if the page is to be stored in history or not.
Anyway, this is just something to get you started.

How to index dynamic pages to google using html5 pushstate method?

I am building a fully jquery powered website, so i am loading all pages dynamically using ajax to achieve fancy transitions between pages and maximize user experience.
Here is my javascript code:
$(function() {
var path = _.compact(location.pathname.split("/"));
if(path.length<2){
path = 'home'
}else{
path = path[path.length-1];
}
activepage = path;
$('.nav a').click(function(e) {
href = $(this).attr("href");
loadContent(href);
// HISTORY.PUSHSTATE
window.history.pushState('', 'New URL: '+href, href);
e.preventDefault();
});
// THIS EVENT MAKES SURE THAT THE BACK/FORWARD BUTTONS WORK AS WELL
window.onpopstate = function(event) {
var path = _.compact(location.pathname.split("/"));
if(path.length<2){
path = 'home'
}else{
path = path[path.length-1];
}
loadContent(path);
};
});
function loadContent(url){
// USES JQUERY TO LOAD THE CONTENT
var adresa = absurl + "ajax/get_content";
$.ajax({
url: adresa,
contentType: 'application/json',
data: { url: url },
success: function(data) {
switch(url)
{
case "projects": $.projects({ data : data }); $.projects.init();
break;
case "home": $.homePage({ data : data }); $.homePage.init();
break;
default: console.log("nista");
}
}
});
}
Ajax function returns all data needed to build pages in the json format, then i initialize my custom plugin which builds the page using that json data.
All of that works perfectly fine as you can see on this LIVE EXAMPLE, including the browser history (back and forward).
But here is my problem... When the page is fully loaded the main container remains empty when i look at the source of the page. Also its empty when i try to fetch the page as google bot and i am pretty sure that these two are related.
As you can see on this example with the pretty much same code like i have, the source is being changed when you click on the links and it changes the page content as well, but without reloading the page.
My question is, what am I missing here and how to achieve the same effect? Am i missing some php code or what? I am struggling with this over the past few days, I've tried everything and i couldn't make it work.
Note: only home and project links are working for now...
Thanks a lot for all replies!
pushState lets you change the local part of the URI when you update the page contents with Ajax.
For every URI you create that way, allow the server to build the same page without any dependency on JavaScript.
This will:
Give better performance when visitors enter the site by following a deep link
Allow the site to work without JavaScript (including for search engine robots)
Complementing the #Quentin's answer, you need to identify in the PHP if the content is being loaded via ajax or not.
If it isn't, you have to display the full content of the page being requested, including header, footer and the content of the page.

Categories

Resources