code refactoring, how to elegantly overwrite object values - javascript

so I have a call to factory and after everything goes fine I want to reset the values of imput fields and as you can see in the code below I do this by hand.
vm.submitRequest = () = > {
requestedBooksFactory.requestBook(vm.title, vm.author, vm.link, vm.destination, vm.currentUser, vm.comments)
.then(function (newbook) {
let booksInTotal = vm.requestedBooks.allBooks.length + 1;
vm.requestedBooks.allBooks.push(newbook);
vm.requestedBooks.header = requestedBooksFactory.requestedText(booksInTotal);
}).then(() = > {
vm.title = '';
vm.author = '';
vm.link = '';
vm.comments = '';
vm.destination = false;
vm.submitted = false;
})
}
I dont like the way this looks, but cant come up with a way to solve it

My own way to refactore this code:
vm.submitRequest = submitRequest;
/**************/
function submitRequest() {
return requestBook()
.then(bookReceived)
.then(clearController);
}
function requestBook() {
return requestedBooksFactory.requestBook(vm.title, vm.author, vm.link, vm.destination, vm.currentUser, vm.comments);
}
function bookReceived(book) {
vm.requestedBooks.allBooks.push(book);
vm.requestedBooks.header = requestedBooksFactory.requestedText(vm.requestedBooks.allBooks.length);
}
function clearController() {
vm.title = '';
vm.author = '';
vm.link = '';
vm.comments = '';
vm.destination = false;
vm.submitted = false;
}
Source should not to be too zipped. It should be readable and clear.
Don't use anonymous functions for long methods.
They was designed for short iterators.
Don't afraid function names, they here for you, not for parser.
For parser you'll use obfuscators.
But I try to post what are you looking for :)
function clearController() {
['title', 'author', 'link', 'comments'].forEach(f => {vm[f] = '';});
['destination', 'submitted'].forEach(f => {vm[f] = false;});
}

Related

Conditional validation using single piece of code - AngularJS

The code contains two functions. First one is defined as follows
scope.validateContactName = function() {
scope.address.invalidName = false;
if (!scope.address.name) {
scope.address.invalidName = true;
}
}
which is invoked by the function validateContactName();
Now i have another function
scope.validateContactPhone = function() {
scope.address.invalidPhone = false;
if (!scope.address.phone) {
scope.address.invalidPhone = true;
}
}
which is invoked by the function validateContactPhone();
Instead of two functions, is there a way i can use a single function and do conditional validation?
Something like
validateContactInfo('name');
function validateContactInfo(attr) {
//do validation based on the attribute
// There is only one single piece of code for both conditions
}
Maybe smth like this could work:
scope.validateField = function(field, errorField) {
scope.address[errorField] = false;
if (!scope.address[field]) {
scope.address[errorField] = true;
}
}
Or a shorter version:
scope.validateField = function(field, errorField) {
scope.address[errorField] = !scope.address[field];
}
I would suggest something like this(ES6):
scope.address = [
{
type: "phone",
invalid: false
},
{
type: "name",
invalid: false
}
];
const validate = type => {
let data = scope.address.find(a => a.type === type);
if(!data.type) {
data.invalid = true;
}
};
validate("phone");
validate("name");
Assuming contact information is used in a form to get input from the user. I would recommend to use angular's own form validation
If it is not the case, here is a generic way of checking if values exists in a object. Which you can add in project''s utilities
const contactInfo = {
name: 'My name',
phone: '123123123',
address: ''
}
function validateExistence(obj){
const emptyKeys = [];
for(let key in obj){
if(!obj[key]) emptyKeys.push(key)
}
return emptyKeys
}
console.log(validateExistence(contactInfo));

Best practice to handle undefined variables dynamicaly in JavaScript/Nodejs

Ok, maybe is not the best title, but I lacked inspiration, so here goes:
Let's say you have a "global" (not really) variable to store temporary data and sub data as random users interact with your server. Normally on the first interaction with your server, the main variable will be undefined so you need to handle that case.
Now, what puzzled me about this, is what's the best practice performance wise to do this if there are a lot of users and a lot way more interactions with the variable.
Puzzled? Yeah, I know, words are not my strong point so let me show you in code
So you have
var user_data = [];
Then a function that handles user interaction to store data
function writeData(uid, data_name, data)
Now, on first interaction, user_data[uid][data_name] is undefined, and so it's user_data[uid]
I know you can handle this 2 ways:
With if -
if(!user_data[uid]) user_data[uid] = {}
user_data[uid][data_name] = data
With try/catch
try{user_data[uid][data_name] = data}
catch(e) {user_data[uid] = {}; writeData(uid, data_name, data)}
The if will check on every interaction, and like I said there are a lot.
Try catch will trigger once, but it has a cost as a block (afaik)
Which one is better? Or is there a another better way
#Nertan ,
There is a partiality in your proof :P . I have slightly tweeked the ternary way (same as the order of execution in if way). With this you can conclude.
//var present = require('present');
function test(val,ud,fun) {
var k = 10000000;
var t = Date.now();
for(var i=0; i<k;i++)
{
var uid = Math.ceil(Math.random()*1000);
fun(uid,ud,"value");
}
var tf = Date.now()-t;
return tf;
}
function setValue_Opp(uid,ud,value)
{
(!ud[uid] && (ud[uid] = {})) && (ud[uid].value = value);
}
function setValue_Try(uid,ud,value)
{
try{ ud[uid].value = value}
catch(e){ ud[uid] = {}; setValue_Try(uid,ud,value)};
}
function setValue_Cond(uid,ud,value)
{
if(!ud[uid]) ud[uid] = {}
ud[uid].value = value;
}
var k1=0;
var k2=0;
var k3=0;
for(var i=0;i<10;i++){
k1+=test(1,{}, setValue_Cond);
k2+=test(2,{}, setValue_Try);
k3+=test(3,{}, setValue_Opp);
}
console.log(k1,k2,k3)
I feel we can take advantage of ES6 ternaries as below:
let user_data = {}
const writeData = (uid, data_name, data) => {
((user_data[uid] || (user_data[uid] = {})) && (user_data[uid][data_name] = data ))
console.log(user_data)
// perform write action
}
writeData('1',"test","test1");
writeData('2',"test","test2");
writeData('1',"test","test3");
Ok, so I had to rewrite the test because it doesn't work fine in the Snippet
So I made this for node.js:
var present = require('present');
function test(val,ud,fun) {
var k = 10000000;
var t = present();
for(var i=0; i<k;i++)
{
var uid = Math.ceil(Math.random()*1000);
fun(uid,ud,"value");
}
var tf = present()-t;
console.log("END "+val+" at "+tf);
return tf;
}
function setValue_Opp(uid,ud,value)
{
(ud[uid] || (ud[uid] = {})) && (ud[uid].value = value);
}
function setValue_Try(uid,ud,value)
{
try{ ud[uid].value = value}
catch(e){ ud[uid] = {}; setValue_Try(uid,ud,value)};
}
function setValue_Cond(uid,ud,value)
{
if(!ud[uid]) ud[uid] = {}
ud[uid].value = value;
}
var k1=0;
var k2=0;
var k3=0;
for(var i=0;i<10;i++){
k1+=test(1,{}, setValue_Cond);
k2+=test(2,{}, setValue_Try);
k3+=test(3,{}, setValue_Opp);
}
console.log(k1,k2,k3)
And in the end:
3244.328997004777 3695.0267750024796 3437.6855720058084
Which means:
The best is the classical if
The second best is condintional operators method
And the worst is the try-catch
So it seems the classics win
Edited:
With further tests thanks to #CRayen the best method is :
(!ud[uid] && (ud[uid] = {})) && (ud[uid].value = value);

change value array angularJS before POST to API

I have an array :
[{"IDAlokasiEmiten":154,"IDOrganization":12,"NamaOrganization":null,"CodeEmiten":"ANTM","NameEmiten":"AAneka Tambang (Persero) Tbk"},{"IDAlokasiEmiten":0,"IDOrganization":null,"NamaOrganization":null,"CodeEmiten":"ADHI","NameEmiten":"Adhi Karya (Persero) Tbk"}]
How do I change some values before I post to API?
I want POST to API into:
[{"IDAlokasiEmiten":0,"IDOrganization":12,"NamaOrganization":null,"CodeEmiten":"ANTM","NameEmiten":"AAneka Tambang (Persero) Tbk"},{"IDAlokasiEmiten":0,"IDOrganization":12,"NamaOrganization":null,"CodeEmiten":"ADHI","NameEmiten":"Adhi Karya (Persero) Tbk"}]
Here is my AngularJS:
// GET Detail Alokasi Emiten
$scope.emit.detailDaftarEmiten = [];
$scope.addItemAlokasi = function () {
$scope.emit.detailDaftarEmiten.push({
IDAlokasiEmiten: 0,
IDOrganization: 12,
NamaOrganization: '',
CodeEmiten: $scope.emit.detailDaftarEmiten.CodeEmiten,
NameEmiten: $scope.emit.detailDaftarEmiten.NameEmiten
});
$scope.emit.detailDaftarEmiten.IDAlokasiEmiten = '';
$scope.emit.detailDaftarEmiten.IDOrganization = '';
$scope.emit.detailDaftarEmiten.NamaOrganization = '';
$scope.emit.detailDaftarEmiten.CodeEmiten = '';
$scope.emit.detailDaftarEmiten.NameEmiten = ''
};
$scope.resetForm = function () {
$scope.emit.detailDaftarEmiten.IDAlokasiEmiten = '';
$scope.emit.detailDaftarEmiten.IDOrganization = '';
$scope.emit.detailDaftarEmiten.NamaOrganization = '';
$scope.emit.detailDaftarEmiten.CodeEmiten = '';
$scope.emit.detailDaftarEmiten.NameEmiten = ''
};
$scope.deleteItem = function (index) {
$scope.emit.detailDaftarEmiten.splice(index, 1);
};
$scope.getTotalItems = function () {
return $scope.detailDaftarEmiten.length;
};
But it doesn't work :(
I want to set all value array for (IDAlokasiEmiten=0 and IDOrganization=12).
While you raising the post request you keep your array data (your desired data) in the body of post request.for an example:
http.post(url,{(your data(key-ordered pair)})
Which returns the promises from the promises you will access the results.
. Hope its helpfull

Matching text in element with Protractor

I have an element on page. And there could be different text. I am trying to do like (code is below), and it is not printed to console.
this.checkStatus = function () {
var element = $('.message')
browser.wait(EC.visibilityOf(element), 5000).then(function () {
browser.wait(EC.textToBePresentInElement(conStatus, 'TEXT1'), 500).then(function () {
console.log('TEXT1');
})
browser.wait(EC.textToBePresentInElement(element, 'TEXT2'), 500).then(function () {
console.log('TEXT2');
})
browser.wait(EC.textToBePresentInElement(element, 'TEXT3'), 500).then(function () {
console.log('TEXT3');
})
browser.wait(EC.textToBePresentInElement(element, 'TEXT4'), 500).then(function () {
console.log('TEXT4');
})
})
return this;
}
thanks
I see two problems. first, not sure what 'constatus' is? you need to correct that. second, browser.wait will be throwing error/exceptions when it is not able to find matching condition and timeout expires, So, if your first condition doesn't meet, it will throw timeout exception and will never go to second one. Instead, try something like below
var section = "";
this.checkStatus = function () {
var element = $('.message')
browser.wait(EC.visibilityOf(element), 5000).then(function () {
browser.wait(()=>{
if(EC.textToBePresentInElement(element, 'TEXT1')){
section = "Text1";
}
else if(EC.textToBePresentInElement(element, 'TEXT2')) {
section = "Text2";
}
else if(EC.textToBePresentInElement(element, 'TEXT3')) {
section = "Text3";
}
else if(EC.textToBePresentInElement(element, 'TEXT4')) {
section = "Text4";
}
if(section !== "")
return true;
}, 5000).then(()=>{
<here you can do anything based on 'section'>
}
Note - I haven't verified compilation errors.. so check for that.
Not sure what are you up to, but you can join multiple expected conditions with "or":
var conStatus = $('.message');
var containsText1 = EC.textToBePresentInElement(conStatus, 'TEXT1');
var containsText2 = EC.textToBePresentInElement(conStatus, 'TEXT2');
var containsText3 = EC.textToBePresentInElement(conStatus, 'TEXT3');
var containsText4 = EC.textToBePresentInElement(conStatus, 'TEXT4');
browser.wait(EC.or(containsText1, containsText2, containsText3, containsText4), 5000);

Backbone data logic inside view

I am not sure where to perform the action of preparing the data for the template of my view. Currently I have this piece of code.
getTemplateData: function () {
var inventoryStatus = selectedDevice.get("inventoryStatus"),
data = {},
statusName,
inventoryDate;
statusName = getConfigValue("pdp", "statusMap", inventoryStatus);
data.message = getConfigValue("pdp", "statusMessage", statusName);
data.className = "";
data.dataAttribute = null;
data.tooltipValue = null;
data.displayError = false;
var redirectCode = (allDevices.get("thirdPartyRedirectCode") !== null) ? allDevices.get("thirdPartyRedirectCode") : "";
if (redirectCode) {
if (redirectCode === 9999) {
data.buttonDisabled = false;
data.buttonText = "Design Yours";
} else if (redirectCode === 9998) {
data.buttonDisabled = true;
data.buttonText = "Design Yours";
}
return false;
}
switch(inventoryStatus) {
case 1001: //Out of Stock
data.buttonDisabled = true;
data.displayError = true;
break;
case 1002: //Pre Order
data.buttonDisabled = false;
break;
}
return data;
}
This getTemplateData() I call inside my render function of the view. This seems wrong by the looks of it and i am unsure where to place this code.
Should I create different getters in my model or should i leave them inside my main view. Please help.
From what I know the "correct" way of doing this is to put it in the model, and in the view have
getTemplateData: function () {
return this.model.getTemplateData();
}
EDIT
In case of multiple models for a view (which shouldn't happen, without getting into your decisions at the moment) you can have a getTemplateData for each, and call them with something like extend:
getTemplateData: function () {
var data = this.model1.getTemplateData();
data = $.extend(data, this.model2.getTemplateData());
return data;
}
BUT
What you really should do, IMHO, is give each it's own view, where one of them is smaller and intended to be included in the other. (i.e. bigView.$el.append(smallView.el))

Categories

Resources