I want to load some template files dynamically with the help of ajax. I have added the ajax $.get method for loading the html files and it's working fine with all browsers except safari browser.
In safari it gives me "Failed to load resource: cancelled" error when first time I open the url. However after I refresh my page again, it loads all the files.
When I open my url with http request instead of https, it can load the template file in first time on safari browser.
This issue only happens when I open the url with https. I have successfully installed the certificate and its working fine with other browser. Even there is no certificate issue in safari as well.
Here is my code
var decorator = {
init: function(book, cd) {
this.loadTPL(cd);
},
tpl: {
btnStart: "tpl/startBtn.html",
interfaceTpl: "tpl/interfaceTpl.html",
topMenu: "tpl/topMenu.html",
topMenuItem: "tpl/topMenuItem.html",
},
loadTPL: function(cbTpl) {
var self = this;
var objTpl = {};
async.forEachOf(this.tpl, function(value, key, callback) {
$.get(value, {}, function(data) {
//alert("Load was performed.");
//console.log(value, data);
objTpl[key] = data;
callback();
});
}, function(err, results) {
if (err) {
console.log(err);
}
self.tpl = objTpl;
cbTpl(err);
});
}
}
Any Idea?
While your approach "should" work, it goes into the weird unknown areas of JS, specially using the async lib. So, my solution basically involves refactoring all of it. Instead async you can use jQuery promises to fire all the gets you need, and then handle the responses/errors in each one of them with the promises handlers.
As an example:
$(templatesToLoad).each(function (element, index) {
$.ajax({element.url, cache: false })
.done(function (result) {
objTpl[key] = result;
element.allback(); // callback for each template
})
.fail(function () {
alert( "error" );
})
.always(function () {
alert( "completed" );
});
});
Note:$.get its just a sugar code for $.ajax. By default $.ajax performs a get, unless another method is specified.
The browser, whichever it is, will handle the calls and it will trigger each one of them as soon as permitted, based on each browser capabilities and limitations, so no need to worry about specific implementations.
As general rule, always remember to check the encoding of the calls and responses and their formats, json, text or whatever you use as a response format.
This is likely a cache/timeout issue. Try setting the ajax timeout to something huge. If that works, back it off until you find the sweet spot.
Related
I'm trying to cache a single page webapp with a service worker. It should get all it's files from the cache and update that cache only when a new service worker-version is published.
With a precache function i'm writing some files to the cache as follows:
function precache() {
return caches.open(CACHE).then(function(cache) {
return cache.addAll([
'index.html',
'js/script.js',
'img/bg.png',
'img/logo.svg',
...
]);
});
}
(I've tried to cache with and without "/" before the paths, and even with absolute paths. Makes no difference)
In Chrome's Cache Storage, the content of all those files is exactly as it should be. But when I try to serve the files from cache on reload of the page, none of the requests match with the cache, they all get rejected, even when I'm still online.
self.addEventListener('fetch', function(evt) {
evt.respondWith(
caches.match(evt.request).then(function(response) {
if(response){
return response;
} else {
reject('no result');
}
}).catch(function(){
if(evt.request.url == 'https://myurl.com'){
return caches.match('/index.html');
}
});
)
});
The index.html from the catch-function gets served correctly, and in turn requests the other files, like /js/script.js. Those request show up like this in the console:
Request { method: 'GET', url: 'https://myurl.com/js/script.js', ... referrer: 'https://myurl.com' }
But they do not return a response, only a notice like this shows:
The FetchEvent for "https://myurl.com/js/script.js" resulted in a network error response: an object that was not a Response was passed to respondWith().
Am I missing something here?
Thanks to the link from Rajit https://developer.mozilla.org/en-US/docs/Web/API/Cache/match I've found that the caches.match() function accepts an options-object.
I've updated that line in my service worker to
caches.match(evt.request,{cacheName:CACHE,ignoreVary:true}).then(function(response) {
so it includes the cache-name and an ignores VARY header matching, and now it returns the correct files.
I had the same problem and it seems to have been solved by using the ignoreVary:true parameter. The documentation explicitly states that the cacheName parameter is ignored by Cache.match()
Important note is to add all possible url versions, with, without trailing slash, because even when autocompleted, it seems to bee seen as two different things. So, for example, if you had a pwa in domain/folder/,
calling domain/folder/ online and caching wont make domain/folder work offline (in some cases) unless you previously accessed the later online as well.
Solution:
when adding via caches.addAll or similar, add both
'/folder/'
AND
'/folder'.
What never did a thing for me on the other hand, was ignoreVary .
In an web pure vanilla JavaScript app that does not use service workers, I would like to explicitly cache a JavaScript file that is sitting on an AWS S3 file server. The following script would be sitting in the index.html file for the application (I’ve modified the URL as it's a client project):
<script>
caches.match('https://s3.amazonaws.com/com.myproject/myjavascript.js')
.then(function(response) {
if (response) {
return response;
} else {
fetch('https://s3.amazonaws.com/com.myproject/myjavascript.js')
.then(function(res) {
return caches.open('mycache')
.then(function(cache) {
cache.put('https://s3.amazonaws.com/com.myproject/myjavascript.js',res.clone());
console.log(res.clone());
return res;
});
});
}
});
</script>
I believe this code should do the following: Check if the myjavascript.js file is in the cache. If it is, return the JavaScript file which would then be executed by the browser. If myjavascriptfile.js is not found in the cache, it will be fetched and placed in the subcache ‘mycache’ and finally returned to the browser where it would be executed.
After running this, I find the URL for the file in the cache with a response of “Ok”, but the code is not executed by the browser and I don’t see the file contents in sources within the Chrome browser developer tools.
Why would this not be working? What is wrong with my thinking on this.
Many thanks,
Fred
fetch by itself will not execute JavaScript. It simply makes a request for the specified content and make it available for the code to access. If you really want to continue with this approach it is possible to take the text and eval it.
const url = 'https://unpkg.com/underscore#1.8.3/underscore-min.js';
caches.match(url)
.then(function(response) {
if (response) {
return response;
} else {
return fetch(url)
.then(function(res) {
return caches.open('mycache')
.then(function(cache) {
cache.put(url,res.clone());
console.log(res.clone());
return res;
});
});
}
})
.then(function(response) {
console.log(response);
response.text().then(function(text) {
eval(text);
console.log(_);
});
});
Note: Why is using the JavaScript eval function a bad idea?
The code sample you have is a pattern commonly found in Service Workers. The reason it works in that context is the initial request is from <script> tags and not direction invocation of fetch. Because of the <script> tag the browser handles automatically executing the returned content.
<script src="https://unpkg.com/underscore#1.8.3/underscore-min.js"></script>
Im getting 500 ReferenceError: localStorage is not defined in the controller of my Rendr app. Im trying to fetch my Authorization token from localStorage and set it as a header before I fetch the spec. I've also tried window.localStorage but then I get window is not defined. Do I not have access to the window object in the controller level? If not, how would I fetch from localStorage.
This is my code for the controller.
module.exports = {
show: function(params, callback) {
var spec = {
model: {
model: 'Company', params: { name: params.id }
}
};
var options = {},
Authorization = localStorage.getItem('Authorization');
options.header = {
"Authorization": Authorization
}
this.app.fetch(spec, options, function (err, results) {
// return if there is an error fetching the user
if (err) return callback(err);
// set the title of the page to the users name
this.app.set('title', results.model.get('name'));
// render the page with the results from the fetch
callback(null, results);
}.bind(this));
}
};
Welcome to Rendr :-)
Rendr is Isomorphic (or "Universal), which means a lot of it's code runs both on the server AND in the browser. If you have code that you only want to run on the browser there are two ways to make that happen:
In the views there is a custom method called postRender - that method is not run on the server, and only runs on the browser. It's the standard place to put all of your browser specific code. The downside is that it is run after the page is rendered.
You can wrap the code in if (window !== 'undefined') {...} to ensure that it only runs in a browser. The downside is that it will never run on the server.
In our Rendr app, we do a bit of using localstorage, and kinda have to wedge it into the very top of the base template. It's a bit weird because the concepts of localstorage (the browser has persistence) fight the concepts of isomorpic apps (the server and the browser can be the same). So they don't work together great.
I want to do an ajax call with vanilla js.
In jQuery, I have this working ajax call:
$.ajax({
url:"/faq/ajax",
datatype: 'json',
type:"POST",
data: {search:'banana'},
success:function(r) {
console.log(r['name'])
}
});
Vanilla JS:
var search = document.getElementById('searchbarfaq').value;
var r = new XMLHttpRequest();
r.open("POST", "/faq/ajax", true);
r.onreadystatechange = function () {
if (r.readyState != 4 || r.status != 200) return;
console.log("Success: " + JSON.parse(r.responseText));
var a = JSON.parse(r.responseText);
console.log(a.name); //also tried a['name']...
};
r.send("search=banana");
The vanilla js call just logs this to the console:
"Success: [object Object]"
Array [ ]
Can someone tell me what I am doing wrong?
You haven't told the server how you are encoding the data in the request.
r.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Presumably whatever server side handler you are using to process the data isn't parsing it correctly, so isn't finding the data it needs, and then returns a blank array as the result.
Beyond printing out r.responseText to the console, you can also inspect the HTTP response from dev tools built into the browser itself.
On Firefox, for instance:
Tools -> Web Developer -> Network
(this should open a panel listing all the HTTP requests and responses)
Go through the process you use to execute your AJAX call
Look at the corresponding HTTP request by clicking on the item in the list in the panel shown in step 1 (a panel on the right should appear with more details about the request and subsequent response)
Digging around in these tools can give you a lot of insight into the the HTTP request your code is making and the values it's getting back in the response.
A similar process can be performed for all the major browsers out there.
You can use this simple and lightweight Ajax module with the following syntax:
import {ajax} from '/path/to/ajax.min.js';
ajax('https://api_url.com')
.data('key-1','Value-1')
.data('key-2','Value-2')
.send()
.then((data) => { console.log ('success', data) })
.catch((status) => { console.log ('failed', status)} );
We've spent a better part of yesterday trying to get this resolved. So as a last ditch effort i'm posting here.
Our setup is a Node.js / Express backend. With Socket.io on the same port as express.
app.io = io.listen(server);
app.io.set('origin', '*');
app.io.set('log level', '2');
app.io.enable('browser client minification');
app.io.set('transports', [
'websocket',
'flashsocket',
'htmlfile',
'xhr-polling',
'jsonp-polling'
]);
I've explicitly enabled all of the transports. We were hoping either jsonp or flash sockets would play nice on our least favorite browsers...
But, I guess there is a reason that they're our least favorite. ;-)
Client side we've got an Angularjs application with socket.io. Very loosely based on this tutorial
window.WEB_SOCKET_SWF_LOCATION = 'https://api.ourdomain.com/socket.io/static/flashsocket/WebSocketMain.swf';
var socket = io.connect('https://api.ourdomain.com', {
'reconnect' : true,
'reconnection delay' : 1500
});
We've tried adding the SWF location as seen here to get flashsockets working. It is serving up the policy and getting the files.. So no luck on that.
Anyways on ie7-9 when it attempts to connect via jsonp polling we get this error:
Object doesn't support this property or method
Contents of the jsonp message will be something like this:
io.j[1]("1::");
Occasionally with more content in it.
io.j seems to be being set as an array in the main socket.io.js file.
When I put this in the developer tools console, it throws the same error.
I've tried moving all the meta tags before the scripts like suggested here. That didn't change anything.
XHR-polling doesn't seem to work either. We've seen some posts about changing settings in ie.. But obviously we can't require our clients to go and request their IT department to change their browser settings just to visit our site. So we've ditched that method.
Also tried creating a blank page, and connecting.. That works. So what would be interfering?
Hopefully you guys have something?
We were unable to resolve this issue by simply making Socket.io work. I don't know if this is a Socket.io issue or if this is a combo of Angularjs and Socket.io.
We did however resolve this issue by adding our own fallback. In our Socket.io service we check for the existence of a feature present in ie9+ as well as webkit and firefox browsers.
var use_socketIO = true;
/*
I'd like to detect websocket, or jsonp.
But those transport methods themselves work.
Just not reliably enough to actually use
*/
if (typeof(window.atob) === 'undefined') {
console.log('browser appears to be < ie10.');
use_socketIO = false;
}
if (use_socketIO) {
var socket = io.connect('https://api.ourdomain.com', {
'reconnect' : true,
'reconnection delay' : 1500
});
}
// Fall back http method.
function httpReq(method, url, data) {
var defer = $q.defer();
$http({
method: method,
url: '//www.domain.com/api'+url,
data: data
}).success(function (data, status, headers, config) {
defer.resolve(data)
});
return defer.promise;
}
// Socket.io method.
function socketReq(method, url, data) {
var defer = $q.defer();
socket.emit(method, {url: url, data: data}, function (response) {
try {
var data = JSON.parse(response);
defer.resolve(data);
} catch (e) {
$log.error('Failed to parse JSON');
$log.info(response);
defer.reject(e);
}
});
return defer.promise;
}
// Main Request method
function request(method, url, data) {
if (use_socketIO) {
return socketReq(method, url, data || {});
} else {
return httpReq(method, url, data || {});
}
}
Then we simply just call request('get', '/url');
Server side we do some magic to build a req and res object if its Socket.io and then forward it to express to handle, otherwise express just handles it like normal.