Requirejs not processing call in errback on failed module load - javascript

I have a require call (angular app):
require(['app','angular','jquery']
, function () {
angular.bootstrap(document, ['MyApp']);
});
There is a path in my require config file that is getting a 404 error, so my onError handler is as so:
require.onError =
function(modError) { //on error
var failedModule = modError.requireModules && modError.requireModules[0];
if (failedModule === 'testrender') {
require.undef(failedModule);
delete configJSON.paths.testrender; //remove testrender from paths
require.config(configJSON); //re-initialize the config
require(['app','angular','jquery'], function() { });
}
};
I followed the instructions in the require documentation: http://requirejs.org/docs/api.html#errbacks,
however the require call is not being called that second time. Am i missing something? Please help!

Related

Dynamic google javascripts loading failing

I have several listeners on modals that look like this:
$('#modaledithistory').on('shown.bs.modal', function() {
loadjscssfile("js/editable_table.js", "js", function(){ setHistory(true); });
});
The function being called is:
function loadjscssfile(filename, filetype, callback){
if (filesadded.indexOf("["+filename+"]")==-1){
if (filetype=="js"){
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", filename);
fileref.onreadystatechange = callback;
fileref.onload = callback;
}
if (typeof fileref!="undefined"){
// Fire the loading
document.getElementsByTagName("head")[0].appendChild(fileref);
console.log("added to html: " + filename);
}
filesadded+="["+filename+"]";
} else {
window[callback]();
console.log("already loaded: " + filename);
}
console.log(callback);
}
This works just fine and as expected.
The problem happens with this listener:
$('#modalreporting').on('shown.bs.modal', function() {
loadjscssfile("https://www.gstatic.com/charts/loader.js", "js", null);
loadjscssfile("js/graphs.js", "js", function(){ initGraphs(); drawGraphs(); mobileRotateScreen(true);
} );
The console errors I get from this don't make any sense considering the code with the callback actually states to wait until the file is completely loaded BEFORE executing code.
Errors:
graphs.js:20 Uncaught ReferenceError: google is not defined
at graphs.js:20
graphs.js:313 Uncaught TypeError: Cannot read property 'DataTable' of undefined
at drawGaugegraph (graphs.js:313)
at drawGraphs (graphs.js:49)
at HTMLScriptElement.onreadystatechange (javascript.js:1030)
The lines generating the errors:
// line 20
google.charts.load('current', {
packages: ['corechart']
});
// line 313
var data = new google.visualization.DataTable(),
max = 0;
So what the console is saying is that the object 'google' doesn't exist.
The file however, should be loaded. I went through it and there isn't even a google object created in there.
When placing the script element directly in the html it works, but for performance reasons I'd rather not do that.
Can anyone help me make sense of this?
Thx!
EDIT:
I noticed the window[callback](); also isn't working as it should be.
On doing that line I get javascript.js:1011 Uncaught TypeError: window[callback] is not a function.
first, try waiting for the google charts script to load, before loading the next script...
$('#modalreporting').on('shown.bs.modal', function() {
loadjscssfile("https://www.gstatic.com/charts/loader.js", "js", function () {
loadjscssfile("js/graphs.js", "js", function() {
initGraphs();
drawGraphs();
mobileRotateScreen(true);
});
});
});
next, you need to wait for google's load statement to finish,
before using the google.visualization namespace...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = new google.visualization.DataTable(),
max = 0;
...
});
Disclaimer: in order to be able to make a long comment and add formatting I need to post an answer.
I adapted it a bit, cause the 3 functions after the loading of the JS file actually call the datatable etc and need to be re-executable (hence window[callback](); in loadjscssfile).
$('#modalreporting').on('shown.bs.modal', function() {
loadjscssfile("https://www.gstatic.com/charts/loader.js", "js", function () {
loadjscssfile("js/graphs.js", "js", function() {
console.log("loaded graphs2");
initGoogleLibraries("googleCharts").then(function () {
initGraphs();
drawGraphs();
mobileRotateScreen(true);
});
});
});
});
graphs.js
function initGoogleLibraries(googleLib) {
if (filesadded.indexOf("["+googleLib+"]")==-1){
google.charts.load('current', {
packages: ['corechart','gauge']
}).then(function () {
filesadded+="["+googleLib+"]";
console.log("loaded google Lib");
});
} else {
return;
}
}
The then is giving issues.
Uncaught TypeError: Cannot read property 'then' of undefined at HTMLScriptElement.onreadystatechange
Basically the logic behind it should be:
Click modal
Load JS files
When JS loaded, load the google 'corechart' and 'gauge' libraries
When libraries loaded execute initGraphs(), drawGraphs() and mobileRotateScreen(true)
When modal is closed and clicked again only initGraphs(), drawGraphs() and mobileRotateScreen(true) should be executed again (because loading the js all over again would be even less performant than it was before.

angular watch causing looping Iligal invocation error

I have a modal dialog where the user can select files to be uploaded. The actual file select/upload is handled by ng-file-upload. When the user selects one or more file, they are added to a list in the dialog, showing progress, completion and failure statuses for each element. The list of items are handled inside a custom directive, since it's used other places as well.
I need to prevent the user from dismissing the dialog while files are still uploading, and that's a challenge for me, cause the button for closing the dialog is in one controller, while the list of uploads is in another (the directive controller). I have solved that by giving and empty list to the directive like this:
//extract from directive:
var directive = {
...
scope: {
'files': '='
}
}
//extract from usage
<uploadFiles files="files" />
Now the outer controller and the inner controller shares the list of files uploading.
So when the user tries to dismiss the dialog by clicking the Close button, I first check if the list contains files still uploading, and if so, I disable the button and display a spinner and a 'please wait'-text.
//from the outer controller
function onModalOk() {
if (uploadInProgress()) {
waitForCompletionBeforeClosingDialog();
} else {
closeDialog();
}
}
the waitForCompletionBeforeClosingDialog() is implemented by setting up a deep watch on the files array. Each time the watch is triggered, I loop through to see if every file has completed. If so, I delete the watch and dismiss the dialog.
function waitForCompletionBeforeClosingDialog() {
$scope.showWaitText = true;
var unregisterWatchForCompletion = $scope.$watch('files', function(files) {
if (allCompleted(files)) {
unregisterWatchForCompletion();
closeDialog();
}
}, true);
}
Everything is working ok, except for one little thing...
In the console, I get this error:
TypeError: Illegal invocation
at equals (angular.js:931)
at equals (angular.js:916)
at Scope.$digest (angular.js:14302)
at Scope.$apply (angular.js:14571)
at angular.js:16308
at completeOutstandingRequest (angular.js:4924)
at angular.js:5312
and it's fired in a tight loop.
I have tried debugging this error, but with no luck..
Do anyone have any ideas?
Is there better ways of doing this all together?
What about using an $httpInterceptor to keep count of the amount of active requests?
something like:
angular.module('someModule').provider('httpStatus', ['$httpProvider', function ($httpProvider) {
var currentRequestCount = 0;
var interceptor = ['$q', function ($q) {
return {
request: function (config) {
currentRequestCount++;
return config;
},
response: function (response) {
currentRequestCount--;
return response;
},
responseError: function (rejection) {
currentRequestCount--;
return $q.reject(rejection);
}
}
}];
$httpProvider.interceptors.push(interceptor);
this.$get = function () {
return {
isWaiting: function () {
return currentRequestLength > 0;
}
}
};
}]);
You could inject the httpStatus service into your dialog and use it to disable the buttons if there are any active requests. May need to add the requestError handler also.
https://docs.angularjs.org/api/ng/service/$http

require a library module in requirejs

I want to execute Nav.init(), Nav1.init() both after dom ready so I call DomReady module inside Nav, Nav1 each module before.
Now I try edit the code to call DomReady module before require them both, then just need to write one time but because there is a require parameter I don't know how should I do?
Does it like this define(['require','DomReady!'], function (require, DomReady) { ?
define(['DomReady!'], function (require, DomReady) {
var Nav = require('custom/Nav');
Nav.init();
var Nav1 = require('custom/Nav1');
Nav1.init();
});
Try this
define(['DomReady!'], function (DomReady) {
require(['custom/Nav'], function(Nav){
Nav.init();
});
require(['custom/Nav1'], function(Nav1){
Nav1.init();
});
});

how to use jquery file upload angular version?

here is how I use angular jquery file upload
var album = angular.module('album', ['restangular', 'blueimp.fileupload']),
.controller('somecontroller',function($scope,){
$scope.options = {
something
}
})
all I did was set the scope.options, change the controller ,and everything just magically works
setup the jquery file upload seems quite easy, but there are something really confuse me
how can I call the jquery file upload's callback function. for example, if the files uploaded successfully,I want to update the ui by calling fileuploaddone function ,it confuse me because there is no added file in my controller.
I'm new to angularJS, please help me to understand the workflow of angular jquery file upload
the blueimp.fileupload uses events that are fired via $emit to notify parent scopes:
on([
'fileuploadadd',
'fileuploadsubmit',
'fileuploadsend',
'fileuploaddone',
'fileuploadfail',
'fileuploadalways',
'fileuploadprogress',
'fileuploadprogressall',
'fileuploadstart',
'fileuploadstop',
'fileuploadchange',
'fileuploadpaste',
'fileuploaddrop',
'fileuploaddragover',
'fileuploadchunksend',
'fileuploadchunkdone',
'fileuploadchunkfail',
'fileuploadchunkalways',
'fileuploadprocessstart',
'fileuploadprocess',
'fileuploadprocessdone',
'fileuploadprocessfail',
'fileuploadprocessalways',
'fileuploadprocessstop'
].join(' '), function (e, data) {
if ($scope.$emit(e.type, data).defaultPrevented) {
e.preventDefault();
}
})
That means that you can simply add an event listener in one of the parent scope controllers, e.g.:
$scope.$on('fileuploadprocessdone', function(event, files){
$.each(files, function (index, file) {
//do what you want
});
});
You can also override the default handleResponse function in your config phase, e.g.:
angular.module('myApp', ['blueimp.fileupload']).
.config(['fileUploadProvider', function (fileUploadProvider){
fileUploadProvider.defaults.handleResponse = function (e,data){
var files = data.result && data.result.files;
if (files) {
data.scope().replace(data.files, files);
// do what you want...
} else if (data.errorThrown || data.textStatus === 'error') {
data.files[0].error = data.errorThrown ||
data.textStatus;
}
};
}]);

RequireJS define callback returning wrong module dependencies

I'm having a very strange and frustrating problem with RequireJS. When I call require for a module with a list of dependencies, all dependencies available in the callback reference a single module. This is probably easier to explained with code:
Including require.js script (with no data-main attribute)
<script type="text/javascript" src="/js/common/require.min.js" ></script>
Below that I include require my main.js (used in all pages of the site) which in the callback requires my page specific js.
<script type="text/javascript">
require(['/js/require/main.js'], function () {
require(['page/home_page']);
});
</script>
main.js
requirejs.config({
baseUrl: 'js/require'
});
requirejs(['base'],
function() {
var base = require('base');
base.init();
});
home_page.js
define(['home','searchbar'], function (home,searchbar){
console.log(home.init == searchbar.init); // This is always true !!!
home.init();
searchbar.init();
});
home.js
define(function(){
this.init = function(){
console.log('in home page');
}
return this;
});
searchbar.js
define(function(){
this.init = function(){
console.log('Now in the searchbar init')
}
return this;
});
The issue is in home_page.js both modules home and searchbar reference the same thing. What's strange is that now that I've simplified this example, it seems pretty random which one it chooses. Most times it's searchbar but every few refreshes it will be home.
Anyone have an ideas? Is it something terribly obvious?
EDIT: Simplified example and provided all module source.
You are assigning to this in both modules. This is not a good idea (excuse the pun). this will possibly be the window object in both cases. You could check by adding a
window.init === searchbar.init
test.
Rewrite the modules to return unique objects, like so:
define(function() {
return {
init: function() {
console.log('in home page');
}
};
});
and
define(function() {
return {
init: function() {
console.log('Now in the searchbar init');
}
};
});

Categories

Resources