Optimization of DefineProperty in Angularjs - javascript

I am trying to optimized my code when using DefineProperty. Below is a link to a jsfiddle example of what I need to do in my app.
https://jsfiddle.net/ismohamed/moqbpoku/
var app = angular.module("TestApp", []);
console.log(app);
app.service("MyCalc", function() {
this.addCalcs = function(totals) {
var newTotals = [];
angular.forEach(totals, function(t, index) {
newTotals[index] = {};
Object.defineProperty(newTotals[index], 'Sum', {
get: function() {
console.log("Called!")
var a = parseInt(t.a);
var b = parseInt(t.b);
return a + b;
}
});
Object.defineProperty(newTotals[index], 'Sub', {
get: function() {
var a = parseInt(t.a);
var b = parseInt(t.b);
return a - b;
}
});
});
return newTotals;
}
});
app.controller("MyCtrl", function($scope, MyCalc) {
$scope.myObject = [];
$scope.savedObject = [];
var nums1 = {};
nums1.a = 3;
nums1.b = 2;
$scope.myObject[0] = nums1;
var nums2 = {};
nums2.a = 5;
nums2.b = 4
$scope.myObject[1] = nums2;
$scope.Calcs = MyCalc.addCalcs($scope.myObject);
// Copy the original Object - Copy Messes things up so use slice instead
// to get shallow coppy
//angular.copy($scope.savedObject, $scope.myObject);
$scope.savedObject = $scope.myObject.slice();
var nums3 = {};
nums3.a = 10;
nums3.b = 5;
$scope.savedObject[0] = nums3;
$scope.SavedCalcs = MyCalc.addCalcs($scope.savedObject);
});
Basically I need to calculate in real-time the output when a user is typing in some numbers in the input textboxes. The user has groups of text boxes so each group can have it own real-time outputs, hence the use of forEach in the Calculation service which I have made. This functionality needs to remain as is.
In the example I needed to test what happened when I copied an object array as the user can make some changes and then Cancel his/her changes. By using Angular.Copy it messes things up. Therefore I found out that using slice for a shallow copy is better. Anyone knows the reason for that?
My second and real question is that as you can see I have a "Called" log output in one of the "Get" functions. Every time I change a value in one of the textboxes the Get gets called 4 times in my example. How can I optimize this so that Get is called less? Is it even possible?
Any optimization examples for this code would be greatly appreciated.

Related

Why does every other object I create in a loop turns out blank on old version of Chrome?

It's something that's really bothering me, and it's also kind of important since it's part of my job.
I made an object that basically parses a hostname and puts labels on the different parts of that name.
Sounds pretty straightforward, right? HOWEVER, when I create several instances of that object in a row, every other instance turns out blank, with nothing but _proto and a few functions. No data whatsoever.
It might be important to note that I'm using an old version of Chrome (which I have to use, since the network at work is closed-circuit and it's impossible to update the software beyond what's on the net). The same code works at home.
WHAT AM I DOING WRONG, THEN?
Thanks in advance.
var reg = /([A,B,C,D,E])(\d{3})(\d{2})([F,G,H])(\d{2})/i;
var hostParser = function(hostname) {
var parsed = reg.exec(hostname);
if (parsed) {
this.prefix = parsed[1];
this.arena = parsed[2];
this.waitingRoom = parsed[3];
this.adminStatus = parsed[4];
this.ID = parsed[5];
this.hostname = hostname.toUpperCase();
return this;
}
return false;
};
Array.prototype.eliminateDuplicates = function() {
var r = [];
this.forEach(function(n) {
if (r.indexOf(n) < 0)
r.push(n);
});
return r;
};
Array.prototype.trim = function() {
var r = [];
this.forEach(function (n) {
if (!/^\s?$/.test(n))
r.push(n);
});
return r;
};
var list = [
'A40800G01',
'A40800G02',
'A40800G03',
'A40800G04',
'A40800G05',
'A40800G06',
'A40800G07',
'A40800G08',
'A40800G09'
];
list.trim().eliminateDuplicates().forEach(function (item) {
var itemParser = new hostParser(item);
console.log(itemParser);
});
This was indeed a bug with that particular Chrome version. Not being able to update, I duplicated array items on purpose and it worked.

How to access specific properties in a multiple objects using JS

Here is my object:
$scope.info = [];
$scope.info = [{"name":"kanye","size":"","folder":"Folder"},{"name":"west","size":"","folder":"Folder"}]
$scope.infoObj = $scope.info.name;
console.log($scope.infoObj);
This return me Undefined. The response should be like this:
[{"name":kanye},{"name":west}]
But how to access the specific properties from a multiple object using angularJS or jquery/js?
This should solve the problem:
$scope.infoObj = $scope.info.map(function(obj){
var x = {};
x['name'] = obj['name'];
return x;
})
for ES6, it can be simplified to:
$scope.infoObj = $scope.info.map(x => ({name:x['name']}))
You can actually do a little bit of refactoring to make your code a little cleaner and more readable. Instead of setting your info value twice, set it once, and add the objects in each line after.
Like this:
$scope.info = [];
$scope.info.push({
"name":"kanye",
"size":"",
"folder":"Folder"
});
$scope.info.push({
"name":"west",
"size":"",
"folder":"Folder"
});
See how clean that is? Now it should be fairly obvious that info is an Array of objects, so doing $scope.info.name won't work. What I would recommend is creating a lookup function that can help grab a list based on the key you provide it.
Something like this:
function lookup(key) {
var result = [];
for (var i = 0; i < $scope.info.length; i++) {
var object = {};
object[key] = $scope.info[i][key];
result.push(object);
}
return result;
}
And then call it like this:
$scope.infoObj = lookup('name');
console.log($scope.infoObj);

How to iterate an Knockout ObservableArray? (length 0)

I'm new to knockout.js (and this is also my first stackoverflow post) and I'm now facing the following problem.
I'm not able to bind the data from web api to a ko.observablearray. Why is the length of this Announcements ko.observablearray always 0? The code works fine with client side data (by adding new announcements)..
Here's the JS-code:
var AnnouncementModel = function () {
var self = this;
self.AnnouncementText = ko.observable();
self.AllDepartmentsBool = ko.observable();
self.Editable = ko.observable(false);
self.Add = function () {
viewModel.Announcements.push(self);
viewModel.AnnouncementToEdit(new AnnouncementModel());
};
self.Delete = function () {
ajaxHelper(announcementsUri + self.ID, 'DELETE').done(
viewModel.Announcements.remove(self));
};
self.Edit = function () {
self.Editable(!self.Editable());
};
}
//The ViewModel
function AnnouncementsViewModel() {
var self = this;
self.InitialData = ko.observableArray();
self.Announcements = ko.observableArray();
self.AnnouncementToEdit = ko.observable(new AnnouncementModel());
self.error = ko.observable();
function getAllAnnouncements() {
ajaxHelper(announcementsUri, 'GET').done(function(data) {
self.InitialData(data);
});
};
getAllAnnouncements();
};
var viewModel = new AnnouncementsViewModel();
ko.applyBindings(viewModel, document.getElementById("announcements-container"));
function createAnnouncement(announcementDto) {
var announcement = new AnnouncementModel();
announcement.AnnouncementText = ko.observable(announcementDto.AnnouncementText);
announcement.AllDepartmentsBool = ko.observable(announcementDto.AllDepartmentsBool);
announcement.Editable = ko.observable(false);
return announcement;
}
var length = viewModel.InitialData.length;
for (i = 0; i < length; i++) {
var newAnnouncement = createAnnouncement(InitialData[i]);
viewModel.Announcements.push(newAnnouncement);
}
The HTML:
<div id="announcements-container" style="display: inline-block; float: right">
<ul id="announcements-list" class="newsticker" data-bind="foreach: Announcements">
<li>
<span data-bind="html: AnnouncementText"></span>
</li>
</ul>
#Html.Partial("_AnnouncementsModal")
</div>
The InitialData gets populated from the api as it should:
GOT IT WORKING! :
Thanks for the quick answers. I got the code working by iterating the data with .forEach(). Another problem was that the initialData didn't get populated in it's current scope so I edited the getAnnouncements function to work like this :
function getAllAnnouncements() {
ajaxHelper(announcementsUri, 'GET').done(function(data) {
data.forEach(function (entry) {
var newAnnouncement = createAnnouncement(entry);
self.Announcements.push(newAnnouncement);
});
});
};
This line is the likely culprit:
var length = viewModel.InitialData.length;
Remember that InitialData is a function. Functions have a length (it's their "arity", the number of formal arguments they have), but the observable function for an observable array's length isn't the array's length..
You probably wanted the length of the array inside it:
var length = viewModel.InitialData().length;
// -------------------------------^^
Your various calls to push on observable arrays work even though length doesn't because Knockout provides push (and several other things) on the observable array function, as James points out.
Similarly, this line:
var newAnnouncement = createAnnouncement(InitialData[i]);
probably wants to be using the array as well (and is missing viewModel. in front of InitialData).
So that whole section probably wants to be refactored a bit:
viewModel.InitialData().forEach(function(entry) {
var newAnnouncement = createAnnouncement(entry);
viewModel.Announcements.push(newAnnouncement);
});
or without forEach (but really, it's nearly 2016, and it's shimmable on obsolete browsers);
var data = viewModel.InitialData();
for (var i = 0; i < data.length; ++i) {
var newAnnouncement = createAnnouncement(data[i]);
viewModel.Announcements.push(newAnnouncement);
}
Side note: Your code (at least as it is in the question) was also falling prey to The Horror of Implicit Globals by not declaring the i that you use in that for loop. I've added a var above, but this is another reason for using forEach to loop through arrays.
You can also use EcmaScript 6 style enumeration as follows:
viewModel.InitialData().forEach(item => {
let newAnnouncement = createAnnouncement(item);
viewModel.Announcements.push(newAnnouncement);
});

Are there any behavioural differences before and after this javascript refactoring?

I recently had to refactor a chunk of javascript that is using YUI.
So, originally it was something like this:
YAHOO.namespace('space.time');
YAHOO.space.time = (function() {
var b = document.getelementbyid("aifdsgyalierg");
function c(b) {
var a = new YAHOO.util.Anim(d, b); //just assume the parameters are correct here
a.method = YAHOO.util.Easing.easeOut;
a.animate();
};
return { c:c };
})();
For the sake of being able to inject dependencies, i refactored it to the below:
YAHOO.namespace('space.time');
YAHOO.namespace('space.timefn');
YAHOO.space.timefn = function(yuianim) {
var b = document.getelementbyid("aifdsgyalierg");
function c(d) {
var a = new yuianim(d, b); //just assume the parameters are correct here
a.method = YAHOO.util.Easing.easeOut;
a.animate();
};
return { c:c };
};
YAHOO.space.time = YAHOO.space.timefn(YAHOO.util.Anim);
So..
1) Ignoring any committed fallacies.. Will the behaviour of the two snippets differ?
2) What fallacies have i committed?

Javascript array is undefined... and I'm not sure why

I'm trying to translate a PHP class into JavaScript. The only thing I'm having trouble with is getting an item out of an array variable. I've created a simple jsfiddle here. I cannot figure out why it won't work.
(EDIT: I updated this code to better reflect what I'm doing. Sorry for the previous mistake.)
function tattooEightBall() {
this.subjects = ['a bear', 'a tiger', 'a sailor'];
this.prediction = make_prediction();
var that = this;
function array_random_pick(somearray) {
//return array[array_rand(array)];
var length = somearray.length;
var random = somearray[Math.floor(Math.random()*somearray.length)];
return random;
}
function make_prediction() {
var prediction = array_random_pick(this.subjects);
return prediction;
}
}
var test = tattooEightBall();
document.write(test.prediction);
​
Works fine here, you are simple not calling
classname();
After you define the function.
Update
When you make a call to *make_prediction* , this will not be in scope. You are right on the money creating a that variable, use it on *make_prediction* :
var that = this;
this.prediction = make_prediction();
function make_prediction() {
var prediction = ''; //initialize it
prediction = prediction + array_random_pick(that.subjects);
return prediction;
}
You can see a working version here: http://jsfiddle.net/zKcpC/
This is actually pretty complex and I believe someone with more experience in Javascript may be able to clarify the situation.
Edit2: Douglas Crockfords explains it with these words:
By convention, we make a private that variable. This is used to make
the object available to the private methods. This is a workaround for
an error in the ECMAScript Language Specification which causes this to
be set incorrectly for inner functions.
To see the complete article head to: http://javascript.crockford.com/private.html
You never call classname. Seems to be working fine.
Works for me:
(function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
function pickone(somearray) {
var length = somearray.length;
var random = somearray[Math.floor(Math.random()*length)];
return random;
}
var random_item = pickone(this.list);
document.write(random_item);
}());
Were you actually calling the classname function? Note I wrapped your code block in:
([your_code]());
I'm not sure what you're trying to accomplish exactly with the class structure you were using so I made some guesses, but this code works by creating a classname object that has instance data and a pickone method:
function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
this.pickone = function() {
var length = this.list.length;
var random = this.list[Math.floor(Math.random()*length)];
return random;
}
}
var cls = new classname();
var random = cls.pickone();
You can play with it interactively here: http://jsfiddle.net/jfriend00/ReL2h/.
It's working fine for me: http://jsfiddle.net/YznSE/6/ You just didn't call classname(). If you don't call it, nothing will happen ;)
Make it into a self-executing function like this:
(function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
function pickone(somearray) {
var length = somearray.length; //<---WHY ISN'T THIS DEFINED??
var random = somearray[Math.floor(Math.random() * length)];
return random;
}
var random_item = pickone(this.list);
document.write(random_item);
})();
var test = tattooEightBall();
document.write(test.prediction);
Should be:
var test = new tattooEightBall(); //forgot new keyword to create object
document.write(test.prediction()); // forgot parens to fire method
and:
this.prediction = make_prediction();
Should be:
this.prediction = make_prediction;

Categories