MVC minification seems to creating duplicate variable names - asp.net

This question seems to be more or less a duplicate of this one, but that one received no answers and is over 2 years old so I don't know what the protocol is (I tried to find out).
Anyway, I've written an ASP.NET MVC5 web app and it all works fine in debug. After publishing to the server with the Release configuration I started seeing the error "Uncaught ReferenceError: Cannot access 'n' before initialization".
After many hours of trawling through the code I think I've isolated the issue. I have this small function (it's a Knockout view model, but that's irrelevant):
eventIndexViewModel = function (params) {
let self = this;
// Map values from params object to properties of self
for (const [key, value] of Object.entries(params)) {
self['_' + key] = value;
}
self.eventsView = ko.observable(self._eventsView);
self.calendarToggleButtonClass = ko.pureComputed(function () {
return self.eventsView() === "calendar" ? "active" : "";
});
self.tableToggleButtonClass = ko.pureComputed(function () {
return self.eventsView() === "table" ? "active" : "";
});
};
After being minified and published to the server, if I view the source in the dev tools console it looks like this:
eventIndexViewModel = function(n) {
let t = this;
for (const [n,i] of Object.entries(n))
t["_" + n] = i;
t.eventsView = ko.observable(t._eventsView);
t.calendarToggleButtonClass = ko.pureComputed(function() {
return t.eventsView() === "calendar" ? "active" : ""
});
t.tableToggleButtonClass = ko.pureComputed(function() {
return t.eventsView() === "table" ? "active" : ""
})
}
It is overkill to map the properties for the params object in this way in this particular instance, but I have much larger view models with many more properties in the same project and I want to keep them code consistent, so go with it.
Unless I'm misunderstanding something, the minified version has renamed both the params variable and the key variable in the for statement to n, and I think that is what is causing my error. Certainly, that line with the for statement is where the error is thrown.
Am I understanding the cause of this problem correctly? If so, is it a bug in the minification process? And either way, how can I get around it?

Related

google.script.run XX is not a function error

Hi I am developing a little app to move files around google drive using google Appmaker.
I have the code working to select the file and the destination directory all fine. The problem is to call the server function to run DriveApp functions as follows:
function onClickbtnMove(widget, event){
var props = widget.root.properties;
fileids=props.FileIdList;
//fileids is a list object of fileIDs, in the following text i removed the loop and just try with one fileID
var i=0;
google.script.run
.withSuccessHandler (function (result) {
console.log (result);
})
.withFailureHandler (function (error) {
console.log (error);
})
.moveFiles_(fileids[i], props.FolderDestinationId);
}
The server script is:
function moveFiles_(sourceFileId, targetFolderId) {
var file = DriveApp.getFileById(sourceFileId);
// file.getParents().next().removeFile(file); // removed until i get it working!!
DriveApp.getFolderById(targetFolderId).addFile(file);
return "1";
}
i am sure there is something totally obvious but i am getting:
google.script.run.withSuccessHandler(...)
.withFailureHandler(...).moveFiles_ is not a function`
any guidance very welcome. thanks in advance.
The problem relies on hiding the server script. The official documentation says:
It is important to note any function you define in a server script is open to all users of your application, even if you do not expose it in the UI. If you want to write a utility function that can only be called from other server scripts, you must append an underscore to the name.
So by appending an underscore to the function name, you are hiding it from the client, hence you are getting that error. In order to call the function using google.script.run, you must get rid of the underscore, i.e., the function moveFiles_(sourceFileId, targetFolderId) should be changed to moveFiles(sourceFileId, targetFolderId).
If you feel you are exposing sensitive information to the client, then in this case, it's important to secure your scripts by implementing your own method. Take for example the following:
function moveFiles(sourceFileId, targetFolderId, role) {
if(role === "Manager" || role === "Admin"){
var file = DriveApp.getFileById(sourceFileId);
DriveApp.getFolderById(targetFolderId).addFile(file);
return "1";
}
}

Randomly occurs with Meteor: "TypeError: Cannot read property 'invalidate' of undefined"

On what seems to be random occasions, when a user on my website tries to answer a question on the quiz page, the following error occurs:
TypeError: Cannot read property 'invalidate' of undefined
at Tracker.Dependency.changed (tracker.js:388)
at ReactiveVar.set (reactive-var.js:82)
at null. (builtins.js:22)
at view.js:191
at Function.Template._withTemplateInstanceFunc (template.js:437)
at view.js:190
at Object.Blaze._withCurrentView (view.js:523)
at viewAutorun (view.js:189)
at Tracker.Computation._compute (tracker.js:294)
at Tracker.Computation._recompute (tracker.js:313)
I am unsure as to why this occurs, and I am not entirely sure when this error even means so if someone could point me in the right direction, that would be great!
Note: this error seems to only occur when using Chrome.
I'll hazard a guess, here's the culprit code in Tracker.Dependency
Tracker.Dependency.prototype.changed = function () {
var self = this;
for (var id in self._dependentsById)
// My Comment - we're expecting this to be a `Tracker.Computation` which has an `.invalidate()` method!
self._dependentsById[id].invalidate();
};
So, the problem is that something is modifying Object.prototype.
eg.
Object.prototype.test = function(){}
var emptyObj = {};
for (i in emptyObj)
console.log('has key=', i, 'isOwnProperty?=', emptyObj.hasOwnProperty(i))
will print out:
has key= test isOwnProperty?= false
A possible solution to try, would be adding this snipped to the client, and seeing if it fixes the problem (At least temporarily):
Tracker.Dependency.prototype.changed = function () {
var self = this;
for (var id in self._dependentsById){
if (self._dependentsById.hasOwnProperty(id)){
self._dependentsById[id].invalidate();
}
}
};
Tracker.Dependency.prototype.hasDependents = function () {
var self = this;
for(var id in self._dependentsById)
if (self._dependentsById.hasOwnProperty(id))
return true;
return false;
};
However - It's bad practice to add to Object.prototype, and lots of code will fail in unexpected ways. You'll need to track this down.
Hard to say without more info, but I'd take a look here, specifically the part under "Subscriptions Don't Block":
https://dweldon.silvrback.com/common-mistakes
As the author states, "In meteor, the majority of "Cannot read property of undefined"errors are caused by an incorrect assumption about the existence of subscribed data."
In other words, you may be trying to access the invalidate property before your subscriptions has returned anything.

$firebase promise resolve in phonegap app

i am stuck with this for few days now.
I have route with resolve, which looks something like this:
.when('/list/',{
controller : 'list',
templateUrl : 'template/list.stache',
resolve : {
list : function($q,$firebase){
var d = $q.defer(),
ref = new Firebase(_config.url+'/list/');
ref.once('value', function(s){
if(s.val() == null){
d.reject('Object not found');
}
d.resolve($firebase(ref));
});
return d.promise;
}
}
})
It works great in any browser, for some reason it fails in Android app ( using phonegap ), it loads data correctly, but when you try to save it ( using $save() ), data updates locally but fails to do so remotely.
Tested few theories, tried to call $firebase within controller, using something like:
$scope.fb = $firebase(new Firebase(_config.url+'/list/'))
$scope.fb.$on('loaded', function(d){
$scope.fb[$scope.fb.$getIndex()[0]].test = 'AAAAAA!'
$scope.fb.$save()
})
The above worked as should, so i assume it has something to do with promises.
Would anyone have any ideas?
EDIT ---
Still struggling to figure out the issue, but was able to narrow it down to resolve:
.when('/list/',{
controller : function(){
new Firebase('https://XXX.firebaseio.com/test/').push('Hey!');
},
templateUrl : 'template/list.stache',
resolve : {
list : function($q,$firebase){
var d = $q.defer(),
ref = new Firebase(_config.url+'/list/');
ref.once('value', function(s){
if(s.val() == null){
d.reject('Object not found');
}
d.resolve($firebase(ref));
});
return d.promise;
}
}
})
It fails. But :
.when('/list/',{
controller : function(){
new Firebase('https://XXX.firebaseio.com/test/').push('Hey!');
},
templateUrl : 'template/list.stache'
})
Works as expected.
Note that both approaches works fine in a browser ( tested on firefox and chrome ). It only fails when compiled to android app using phonegap.
Any ideas are appreciated.
I had the same thing. The connection is lost.
Use the function Firebase.goOffline(); and Firebase.goOnline();. This allows you to manually control the client connection.
Example:
var usersRef = new Firebase('https://samplechat.firebaseio-demo.com/users');
Firebase.goOffline(); // All Firebase instances are disconnected
Firebase.goOnline(); // All Firebase instances automatically reconnect
Link for reference

Web sql/Sql Lite and Binding to Row in Angular

This is for a phonegap angular app. I would have thought binding to the db query return, result.rows in my case would be possible but it seems like it is not. The only way I could get this to work was with the commented out code where I manually push the data into an array row by row. Is this the only way?
The actually error received by binding to .rows is: Error: Duplicates in a repeater are not allowed. Repeater: item in items key: undefined:undefined
The service:
// only portion of code shown
query: function (q) {
var d = $q.defer();
var db = this.getDb();
db.transaction(function (t) {
t.executeSql(q, [], function (tx, results) {
d.resolve(results);
}, function (err) {
d.reject(err);
});
}, function (err) {
d.reject(err);
}
);
return d.promise;
}
The controller is like this:
Sql.query('select * from DEMO').then(function (data) {
console.log(data);
//$scope.items = [];
//for (i = 0, l = data.rows.length; i < l; i++) {
//$scope.items.push(data.rows.item(i));
//}
$scope.items = data.rows; // this errors out
$scope.$safeApply();
});
The repeater is just a simple:
<div ng-repeat='item in items'>{{item.id}} {{item.data}}</div>
Based on the error message it looks like you have more than one undefined item in the data.rows array.
Your working code uses data.rows.item(i) is that creating an new empty object instead of undefined? Try changing data.rows.item(i) to data.rows[i] in your working code does that break as well?
Assuming you are using angular 1.1.5 here are some options:
Use your current workaround
Downgrade to 1.1.4 or to the current stable 1.0.7. I think 1.1.4 will work based on a broken version with 1.1.5 and a working version with 1.1.4.
Remove any duplicate undefined rows from data.rows
Note: For others having a similar type of error Angular generates a $$hashKey to objects when doing an ng-repeat. This error indicates the same object is in the array (with the same $$hashKey) and is not allowed in 1.1.5 (and later?).
See this blog post and google groups post for more info. Also this pull request looks related so I'm not sure if this behavior is intended going forward though it appears to have been fixed in the past.

Sporadic behaviour with Session variables in Meteor

I've been scratching my head as to why this code will work some of the time, but not all (or at least most of the time). I've found that it actually does run displaying the correct content in the browser some of the time, but strangely there will be days when I'll come back to the same code, run the server (as per normal) and upon loading the page will receive an error in the console: TypeError: 'undefined' is not an object (evaluating 'Session.get('x').html')
(When I receive that error there will be times where the next line in the console will read Error - referring to the err object, and other times when it will read Object - referring the data object!?).
I'm obviously missing something about Session variables in Meteor and must be misusing them? I'm hoping someone with experience can point me in the right direction.
Thanks, in advance for any help!
Here's my dummy code:
/client/del.html
<head>
<title>del</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
Hello World!
<div class="helloButton">{{{greeting}}}</div>
</template>
My client-side javascript file is:
/client/del.js
Meteor.call('foo', 300, function(err, data) {
err ? console.log(err) : console.log(data);
Session.set('x', data);
});
Template.hello.events = {
'click div.helloButton' : function(evt) {
if ( Session.get('x').answer.toString() === evt.target.innerHTML ) {
console.log('yay!');
}
}
};
Template.hello.greeting = function() {
return Session.get('x').html;
};
And my server-side javascript is:
/server/svr.js
Meteor.methods({
doubled: function(num) {
return num * 2;
},
foo: function(lmt) {
var count = lmt,
result = {};
for ( var i = 0; i < lmt; i++ ) {
count++;
}
count = Meteor.call('doubled', count);
result.html = "<em>" + count + "</em>";
result.answer = count;
return result;
}
});
I think it's just that the session variable won't be set yet when the client first starts up. So Session.get('x') will return undefined until your method call (foo) returns, which almost certainly won't happen before the template first draws.
However after that it will be in the session, so things will probably behave right once you refresh.
The answer is to just check if it's undefined before trying to access the variable. For example:
Template.hello.greeting = function() {
if (Session.get('x')) return Session.get('x').html;
};
One of the seven principles of Meteor is:
Latency Compensation. On the client, use prefetching and model simulation to make it look like you have a zero-latency connection to the database.
Because there is latency, your client will first attempt to draw the lay-out according to the data it has at the moment your client connects. Then it will do the call and then it will update according to the call. Sometimes the call might be able to respond fast enough to be drawn at the same time.
As now there is a chance for the variable to not be set, it would throw an exception in that occasion and thus break down execution (as the functions in the call stack will not continue to run).
There are two possible solutions to this:
Check that the variable is set when using it.
return Session.get('x') ? Session.get('x').html : '';
Make sure the variable has an initial value by setting it at the top of the script.
Session.set('x', { html = '', answer = ''});
Another approach would be to add the templates once the call responds.
Meteor.call('foo', 300, function(err, data) {
Session.set('x', data);
$('#page').html(Meteor.ui.render(function() {
return Template.someName();
}));
});

Resources