Cant find variable after javascript minification - javascript

I have an AngularJS web app. The app works great without any minification. But when I minified with grunt, like this:
uglify: {
options: {
report: 'min',
mangle: false
},
dist: {
files: {'app/dist/js/yanpy.min.js': [Many scripts, 'app/js/controllers/shopping-cart.js', more scripts]
I get next error:
ReferenceError: shoppingCart is not defined
at Object.$get (yanpy.min.js:9)
at Object.invoke (yanpy-libs-1.min.js:1)
at yanpy-libs-1.min.js:1
at getService (yanpy-libs-1.min.js:1)
at invoke (yanpy-libs-1.min.js:1)
at Object.instantiate (yanpy-libs-1.min.js:1)
at yanpy-libs-1.min.js:2
at yanpy-libs-1.min.js:2
at forEach (yanpy-libs-1.min.js:1)
at nodeLinkFn (yanpy-libs-1.min.js:2)
The shoppingCart script "shopping-cart.js" in included in the grunt file for minification. The file looks like:
function shoppingCart(cartName) {
this.cartName = cartName;
this.clearCart = false;
this.checkoutParameters = {};
this.items = [];
// load items from local storage when initializing
this.loadItems();
}
// load items from local storage
shoppingCart.prototype.loadItems = function () {
// Do whatever
}
What is happening? And Why is not working when I minified and it´s working without minification?
UPDATE_1: According to the mark as duplicated, please note shopping-cart is not an angular module, but just a javascript script. So, not sure if the approach provided in the original referenced post is the real answer.
UPDATE_2: According to #UncleDave comment. I copy the two parts of code there shoppingCart is referenced.
.directive('adminOfferExtras', function(Extra, ExtraService, LocationService, OfferService, DataService) {
function link(scope, element, attrs) {
var boatId = parseInt(scope.boat.id);
var extrasCart = new shoppingCart("OfferBoatExtrasCart_" + boatId);
and
.factory("DataService", function () {
// create shopping cart
var myCart = new shoppingCart("Store");
return {
cart: myCart
};
})
Please note, the only other place where I see shoppingCart referenced is in the shopping-cart.js file, that I already copied. Maybe is this call that should be marked with a var?
shoppingCart.prototype.loadItems = function () {
// Do whatever
}

Related

Error in web worker using web assembly

I would like to use WebAssembly within a web worker.
From my main application, I launch it like this:
let w = new Worker('test.js');
w.onmessage = (event) => { console.log(event); };
w.onerror = (event) => { console.error(event); };
w.postMessage({ message: "Hello World" });
Then, I created a file test.js as follows:
self.Module = {
locateFile: function (s) {
console.log(s);
return s;
}
};
self.importScripts("main.js");
// note: `main.js` is the JavaScript glue file created by emcc
self.onmessage = function(messageEvent) {
console.log(messageEvent); // works!
console.log(self.Module); // works!
console.log(self.Module.ccall("test")); // crashes!
}
I get an error: Uncaught TypeError: Cannot read property 'apply' of undefined. I don't understand why self.Module is undefined, how is that possible?
I have the feeling there is something about the scope of the web worker and WebAssembly that does not work well together.
Thanks for your input!
The problem is that console.log() does not reveal the true state of the object at execution time. Further digging revealed that in fact the object Module was not ready yet.
I cite from: https://kripken.github.io/emscripten-site/docs/getting_started/FAQ.html
How can I tell when the page is fully loaded and it is safe to call compiled functions?
Calling a compiled function before a page has fully loaded can result
in an error, if the function relies on files that may not be present
[...]
Another option is to define an
onRuntimeInitialized function:
Module['onRuntimeInitialized'] = function() { ... };
That method will be called when the runtime is ready and it is ok for you to call compiled code.
Adjusting my test.js (worker) file fixes the issue:
self.Module = {
locateFile: function (s) {
console.log(s);
return s;
}
// Add this function
onRuntimeInitialized: function() {
test();
}
};
self.importScripts("main.js");
// note: `main.js` is the JavaScript glue file created by emcc
self.data = {};
// to pass data from the main JS file
self.onmessage = function(messageEvent) {
console.log(messageEvent); // works!
self.data = messageEvent; // save the data
}
// gets executed when everything is ready.
self.test = function() {
// we may safely use self.data and self.Module now!
console.log(self.Module.ccall("test")); // works!
}

Pass functions in a vm script context

Let's say I have a library module that looks like this:
module.exports = {
increment: function() {
count++;
}
}
And I'd like to use it in a dynamically generated script that looks like this:
(function() { lib.increment(); })();
by passing it in a sandbox:
var sandbox = {
count: 1
lib: require('./lib')
}
var script = new vm.Script('(function() { lib.increment() })();');
script.runInNewContext(sandbox);
The obvious problem I run into is that I on the one hand can't require "lib" because "count" is not defined in lib.js ; on the other hand if I define var count above the exports of the "lib.js" file, this new count variable will be affected instead of the one in the sandbox.
Here are the constraints that I would like to respect:
Use vm and not a eval() nor a require() on a generated file
Have "lib" defined in a external file
No modification of the automatically generated script, so no use of lib.increment.apply(context) or similar
The only solutions I've found so far is to prepend the lib functions in the generated script as a string, or to define them directly on the sandbox object, which I find to be a less desirable option.
There doesn't seem to be any way of passing a context of variables on the require call.
One way of accomplishing this is have your lib module be a function that takes in a context then returns the correct interface.
lib.js
module.exports = function(context) {
var count = context.count;
return {
increment: function() {
count++;
}
};
};
main.js
var sandbox = {
count: 1
};
sandbox.lib = require('./lib')(sandbox);
var script = new vm.Script('(function() { lib.increment() })();');
script.runInNewContext(sandbox);

requirejs loading same module twice

I'm using the single-page app template from https://github.com/volojs/create-template
I tried to make a simple example below.
Problem
ModuleA.js is being loaded twice, once directly from main.js and again from simulator.js which depends also on that module. This is causing two different references to an object (which I thought would only be one, like a singleton). I thought requirejs would not load the same module twice. Being (relatively) new to JavaScript I realize this may be naivete on my part. I'm trying to follow the template.
Here's a simplified version that demonstrates the problem:
www/index.html
<head>
...
<script data-main="app" src="lib/require.js"></script>
</head>
...
www/app.js
// For any third party dependencies, like jQuery, place them in the lib folder.
// Configure loading modules from the lib directory,
// except for 'app' ones, which are in a sibling
// directory.
requirejs.config({
baseUrl: 'lib',
paths: {
app: '../app'
}
});
// Start loading the main app file. Put all of
// your application logic in there.
requirejs(['app/main']);
www/app/main.js
define(function (require) {
var simulator = require('./simulator.js');
var ModuleA = require('./ModuleA.js');
ModuleA.init();
ModuleA.displayList();
ModuleA.update("joe", 99);
ModuleA.displayList();
simulator.start(); // should display the same list
});
www/app/ModuleA.js
define(function () {
var theList = {};
console.log("loading ModuleA");
return {
displayList: function () {
console.log(Object.keys(theList));
},
init : function () {
theList["fred"] = 10;
},
update : function (k, v) {
theList[k] = v;
}
}
});
www/app/simulator.js
define(["./ModuleA"], function (ModuleA) {
return {
start: function () {
ModuleA.displayList();
}
};
});
console output:
loading ModuleA
loading ModuleA
["fred"]
["fred", "joe"]
[]
The empty [] displayed on the last line is (likely) the second copy of the list due to the second loading of ModuleA.
The problem is the module references are not consistent. In main.js it requires ./ModuleA.js whereas in simulator.js it defines ./ModuleA (without the .js filetype).
Making those references identical corrects the behavior such that the module is only loaded once.
I guess I mixed the styles because of the many examples on the web. It kind of seems like a bug that it works this way, but maybe it's a feature?
If you want to share an instantiated singleton object using requireJS, you can do something like this for ModuleA:
define(function () {
console.log("loading ModuleA");
function myList(){
this.theList = {}
}
myList.prototype.displayList = function () {
console.log(Object.keys(this.theList));
}
myList.prototype.init = function () {
this.theList["fred"] = 10;
}
myList.prototype.update = function (k, v) {
this.theList[k] = v;
}
return new myList();
});

How can I get a list of available modules in AngularJS?

When defining an Angular module I define what are my dependencies like this:
var myModule = angular.module("MyModuleName", ["Dep1", "Dep2", "Dep3"]);
Each dependency has its own dependencies, directives, controllers etc.
Is there a way to ask AngularJS what are the available injectables? Without instantiating them, just getting a list.
The Angular.$injector only has a "get" method, but this means that I'll instantiate it.
Thanks
Gil Amran
Actually that's kind of a hack , so only use it for testing and learning purposes!!
All the $injector DI magic is kept inside this file:
https://github.com/angular/angular.js/blob/v1.2.7/src/auto/injector.js
see the createInjector function (line 598 : angularjs 1.2.7)
The providerCache variable is the container off what's available to $injector in config blocks.
The instanceCache variable is the container off what's available to $injector in all other blocks.
function createInjector(modulesToLoad) {
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap(),
providerCache = {
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function() {
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(servicename) {
var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get, provider);
}));
Those variables are well encapsulated inside that closure and you cannot get them from outside.
unless you add these two lines to the createInjector function:
window.providerCache = providerCache;
window.instanceCache = instanceCache;
How to use it:
Download the source code from here: http://code.angularjs.org/1.2.7/angular.js
Add these 2 lines at line 3549

Javascript Module Pattern confusion

I am trying to use the Javascript Module pattern. I want my module to have a public method that can be called when a json file is loaded. In this simple example, the module loads the json on init and (should) load an image into a DOM element when the public method 'loadPic' is called. The source of the image is in the json file.) When I run the script the first time, I get the following error: Uncaught TypeError: Cannot read property 'src' of undefined. My interpretation is that the method 'loadPic' is called automatically, before the data is loaded... but I don't know how to prevent that. Here is the code (PS: I am using Zepto, the 'light' version of jQuery):
<!-- language: lang-js -->
/* in module file: DualG.js */
;(function ($) {
$.extend($.fn, {
DualG: function (element, options) {
var defaults = { // not used yet...
indexStart:0,
url: 'js/data.json'
},
my = {},
init,
loadPic,
plugin = this;
plugin.settings = {};
plugin.pics = [];
init = function () {
plugin.settings = $.extend{}, defaults, options);
$.getJSON(plugin.settings.url, function (data) {
var l = data.images.length, i;
for (i = 0; i < l; i = i + 1) {
plugin.pics[data.images[i].index] = data.images[i];
}
});
init();
my.loadPic = function (index, container) {
$(container).empty().append($("<img>").attr({"src":plugin.pics[index].src}));
};
return my;
}
});
}(Zepto));
My problem was that data is loaded a-synchronistically - so I was tying to use data that was not loaded yet. I added an event trigger in the init, after loading the data. Then, I listen to the event and call the loadPic method only then... Thanks all for your help.

Categories