In the codebase that I am trying to figure out, I see that a js file (myloopfile.js) being imported into another js file. I am trying to make sense of some of the code used there
this is myloopfile.js
function method1(value) {
// return something
}
var myLooper = function (obj, iterator, context) {
var key;
if (obj) {
if (typeof obj === 'function') {
for (key in obj) {
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
iterator.call(context, obj[key], key);
}
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context);
} else if (isArrayLike(obj)) {
for (key = 0; key < obj.length; key++)
iterator.call(context, obj[key], key);
} else {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
iterator.call(context, obj[key], key);
}
}
}
}
return obj;
};
……………………………………………………………………………….
the myLoop in myloopfile.js is called like this
var looper = require(‘../myloopfile.js);
looper({
loop1: function(Home) { //do something },
loop2: function(Home) { //dosomething }
}, function(return1, return2) {
//do something else
});
I am trying to find out where this
function(return1, return2) {
//do something else
});
coming from ? I don’t see anything in that file that suggests that there is a method attached to it. Also where are the parameters return1 and return2 coming from? is this some javascript way to attach things ?
var myLooper = function (obj, iterator, context) {
/* .... */
iterator.call(context, obj[key], key);
/* .... */
};
You pass:
looper({
loop1: function(Home) { //do something },
loop2: function(Home) { //dosomething }
}, function(return1, return2) {
//do something else
});
So
obj = {
loop1: function(Home) { //do something },
loop2: function(Home) { //dosomething }
}
and
iterator = function(return1, return2) {
//do something else
}
The Function.prototype.call() method calls a function with a given this value and arguments provided individually. Therefore, inside you iterator function:
this = context;
return1 = obj[key];
return2 = key;
So javascript has function that are called anonymous function that don't need a function name.
Basically it is used (in this instance) as a way to be an expantion of a parameter.
Take for example the javascript function setTimeout
Well setTimeout can take an anonymous function as one of its parameters i.e
var timer = setTimeout(function{
//do something
},
2000);
// setTimeout(function, time, paramters)
So you don't have to declare a function and pass it in as a parameter
Back to your case, you have this anonymous function that takes return1 and return2
So in the end:
return1 = obj[key];
return2 = key;
Related
I have an AngularJS factory for some common local storage manipulation. It's a common set of functions against different variables. I am constructing it so that the functions are repeated depending on which variable needs to be manipulated. Likely not an elegant way to go about this so open to options.
The factory looks as follows. Is there a way to reuse functions depending on the variable without so much code bloat?
angular.module('app.datastore', [])
.factory('DataStore', function() {
var venue = angular.fromJson(window.localStorage['venue'] || '[]');
var prize = angular.fromJson(window.localStorage['prize'] || '[]');
function persist_venue() {
window.localStorage['venue'] = angular.toJson(venue);
}
return {
list_venue: function () {
return venue;
},
get_venue: function(venueId) {
for (var i=0; i<venue.length; i++) {
if (venue[i].id === venueId) {
return venue[i];
}
}
return undefined;
},
create_venue: function(venueItem) {
venue.push(venueItem);
persist_venue();
},
list_prize: function () {
return prize;
},
get_prize: function(prizeId) {
for (var i=0; i<prize.length; i++) {
if (prize[i].id === prizeId) {
return prize[i];
}
}
return undefined;
},
create_prize: function(prizeItem) {
venue.push(prizeIem);
persist_prize();
}
};
});
My approach is to return in the factory a function which will return a store of a type (venue, prize, ...)
angular.module('app.datastore', [])
.factory('DataStore', function () {
var getStoreFunction = function (storeName) {
var store = angular.fromJson(window.localStorage[storeName] || '[]');
function persist() {
window.localStorage[storeName] = angular.toJson(store);
};
return {
list: function () {
return store;
},
getItem: function (id) {
return store.find(function (elem) {
return elem.id === id;
});
},
createItem: function (item) {
store.push(item);
persist(store);
}
}
};
return { getStore : getStoreFunction };
});
you can create unlimited store by using
var venueStore = DataStore.getStore('venue');
//use of your store
venueStore.createItem({
id : venueStore.list().length + 1,
name : 'myVenue' + venueStore.list().length + 1
});
$scope.venues = venueStore.list();
you can create a factory per type if you want or use it directly in your controller as in this example : https://jsfiddle.net/royto/cgxfmv4q/
i dont know if your familiar with John Papa's angular style guide but you really should take a look it might help you with a lot of design questions.
https://github.com/johnpapa/angular-styleguide
anyway - i would recommend you use this approach -
angular.module('app.datastore', [])
.factory('DataStore', function () {
var venue = angular.fromJson(window.localStorage['venue'] || '[]');
var prize = angular.fromJson(window.localStorage['prize'] || '[]');
return {
list_venue: list_venue,
persist_venue: persist_venue,
get_venue: get_venue,
create_venue: create_venue,
list_prize: list_prize,
get_prize: get_prize,
create_prize: create_prize
};
function persist_venue() {
window.localStorage['venue'] = angular.toJson(venue);
}
function list_venue() {
return venue;
}
function get_venue(venueId) {
for (var i = 0; i < venue.length; i++) {
if (venue[i].id === venueId) {
return venue[i];
}
}
return undefined;
}
function create_venue(venueItem) {
venue.push(venueItem);
persist_venue();
}
function list_prize() {
return prize;
}
function get_prize(prizeId) {
for (var i = 0; i < prize.length; i++) {
if (prize[i].id === prizeId) {
return prize[i];
}
}
return undefined;
}
function create_prize(prizeItem) {
venue.push(prizeIem);
persist_prize();
} });
i like this approach because on the top you can see all the functions available in this factory nice and easy,
and you can also reuse every function you expose outside, inside also, so its very effective and organized,
hope that helped,
good luck.
I have create a filter but this filter is not working with array inside array.
'http://plnkr.co/edit/oygy79j3xyoGJmiPHm4g?p=info'
Above plkr link is working demo.
app.filter('checkboxFilter', function($parse) {
var cache = { //create an cache in the closure
result: [],
checkboxData: {}
};
function prepareGroups(checkboxData) {
var groupedSelections = {};
Object.keys(checkboxData).forEach(function(prop) {
//console.log(prop);
if (!checkboxData[prop]) {
return;
} //no need to create a function
var ar = prop.split('=');
//console.log("ar is - "+ar);
if (ar[1] === 'true') {
ar[1] = true;
} //catch booleans
if (ar[1] === 'false') {
ar[1] = false;
} //catch booleans
/* replacing 0 with true for show all offers */
if(ar[0]=='SplOfferAvailable.text'){
ar[1]='true';
}else{
}
//make sure the selection is there!
groupedSelections[ar[0]] = groupedSelections[ar[0]] || [];
//at the value to the group.
groupedSelections[ar[0]].push(ar[1]);
});
return groupedSelections;
}
function prepareChecks(checkboxData) {
var groupedSelections = prepareGroups(checkboxData);
var checks = [];
//console.log(groupedSelections);
Object.keys(groupedSelections).forEach(function(group) {
//console.log("groupedSelections- "+groupedSelections);
//console.log("group- "+group);
var needToInclude = function(item) {
//console.log("item- "+item);
// use the angular parser to get the data for the comparson out.
var itemValue = $parse(group)(item);
var valueArr = groupedSelections[group];
//console.log("valueArr- "+valueArr);
function checkValue(value) { //helper function
return value == itemValue;
}
//check if one of the values is included.
return valueArr.some(checkValue);
};
checks.push(needToInclude); //store the function for later use
});
return checks;
}
return function(input, checkboxData, purgeCache) {
if (!purgeCache) { //can I return a previous 'run'?
// is the request the same as before, and is there an result already?
if (angular.equals(checkboxData, cache.checkboxData) && cache.result.length) {
return cache.result; //Done!
}
}
cache.checkboxData = angular.copy(checkboxData);
var result = []; // this holds the results
//prepare the checking functions just once.
var checks = prepareChecks(checkboxData);
input.every(function(item) {
if (checks.every(function(check) {
return check(item);
})) {
result.push(item);
}
return result.length < 10000000; //max out at 100 results!
});
cache.result = result; //store in chache
return result;
};
});
above code is for check box filter.
when i click on checkbox called "Availability" it does not filter the result.
Please help me out.
Thanks.
I think that the way you are navigating through json is wrong because if you put in this way it works
"Location": "Riyadh",
"AvlStatus": "AVAILABLE"
"Rooms": {.....
You have to go in some way through Rooms and right now I think you're not doing that
How to rewrite this code, to get the desired o/p.
I would like to use the AgentReply object after filling in the data.
Inside the switch case, this object has data. But once outside, it is null again. Understood that it is because of the async,
But what should I do, to be able to use 'AgentReply' once it has data.
$scope.ActionItems = function (actionItem) {
var AgentReply = {};
switch (actionItem) {
case "SendOTP":
var SentStatus = "";
DataFactory.SendOTP('39487539847')
.then(function (response) {
SentStatus = JSON.parse(JSON.parse(response.data));
SendOTPFailed();
}, function (error) {
});
break;
}/*End of switch*/
function SendOTPFailed(){
if (SentStatus == "200") {
AgentReply = {
IsCustomer: false,
UserText: "Request Failed.",
}
}
}
if (Object.keys(AgentReply).length > 0) {
//do something with AgentReply
}
}
Just pass a function in to where the AgentReply is available, and define it underneath, ie:
$scope.ActionItems = function (actionItem) {
var AgentReply = {};
switch (actionItem) {
case "SendOTP":
var SentStatus = "";
DataFactory.SendOTP('39487539847')
.then(function (response) {
SentStatus = JSON.parse(JSON.parse(response.data));
if (SentStatus == "200") {
AgentReply = {
IsCustomer: false,
UserText: "Request Failed.",
}
}
doSomethingWithAgentReply(AgentReply);
}, function (error) {
});
break;
}
console.log(AgentReply); //null here
function doSomethingWithAgentReply(reply) {
if (Object.keys(reply).length > 0) {
//do something with AgentReply
}
}
}
If you need to use this code :
if (Object.keys(AgentReply).length > 0) {
//do something with AgentReply
}
}
Outside the .then() function :
DataFactory.SendOTP('39487539847')
.then(function (response) {
})
You can try this:
$scope.ActionItems = function (actionItem) {
var def = jQuery.Deferred();
var AgentReply = {};
switch (actionItem) {
case "SendOTP":
var SentStatus = "";
DataFactory.SendOTP('39487539847')
.then(function (response) {
SentStatus = JSON.parse(JSON.parse(response.data));
if (SentStatus == "200") {
AgentReply = {
IsCustomer: false,
UserText: "Request Failed.",
}
def.resolve(AgentReply);
}
console.log(AgentReply); //available here
}, function (error) {
def.reject(error);
});
return def.promise();
break;
}
//console.log(AgentReply); //null here
//if (Object.keys(AgentReply).length > 0) {
//do something with AgentReply
// }
//}
// This is unusable in this case.
The usage is:
var someActionItem = 'SomeActionItemInfo';
$scope.ActionItems(someActionItem)
.then(function(agentReply) {
if (Object.keys(agentReply).length > 0) {
//do something with agentReply
}
}, function(error));
EDIT:
$scope.ActionItems is the same function. What happening when you using promise?
First you defining the deffer object. var def = jQuery.Deferred(). This object is at jQuery, but all frameworks/libraryies that support promise working at the same way.
As you see, you returning def.promise(). That is the object which contain .then property. Because of that obj you can use $scope.ActionItems().then() method. That actually make def.promise().
And inside your async code (this code that consuming some time and it's not executed immediately) you defining def.resolve() or def.reject().
When the work is done. You calling def.resolve(withSomeData) and this will activate .then() method to the $scope.ActionItems.
For example:
var foo = null;
function doPromise() {
var def = jQuery.Deferred();
setTimeout(function(){
foo = 2;
def.resolve(foo + 1) // This will call the .then() method with foo + 1
}, 500);
return def.promise();
}
doPromise();
console.log(foo) // foo = null here. cuz the function waiting 500ms.
// Here the .then() method will be executed after ~500+ ms.
doPromise().then(function(fooValue) {
console.log(fooValue) // foo value = 3 here. cuz function is done
});
Let's say I have a following template:
"foo['x'] = '{{ myVar }}';"
Is there an angular way of checking if evaluating this against my current scope will give myVar some value ? I've got an array of such small templates and I only want to include them in the document when values are truthy. I was hoping either $interpolate, $parse or $eval might come in handy here. I know for sure that $interpolate is useless. What about the other two ? Maybe it's at least possible to get the name of the assigned value/expression ?
EDIT
I wasn't specific enough. What I was trying to achieve, was checking in advance if for example template '{{ myVar }}' evaluated against the current scope will return an empty string or value of the scope variable (if it exists). The case was really specific - when traversing an array of short templates I wanted to know if a template will return as an empty string or not, and only include it in my final html if it doesn't.
I'm not sure what are you trying to achieve, but to if you want to check if myVar is truthy in current scope, you can:
{{myVar ? "aw yiss" : "nope"}}
Evaluates to "aw yiss" if myVar is truthy and "nope" otherwise.
I ended up with a modified $interpolate provider but maybe someone knows a shorter solution :
app.provider('customInterpolateProvider', [
function $InterpolateProvider() {
var startSymbol = '{{';
var endSymbol = '}}';
this.startSymbol = function(value){
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
};
this.endSymbol = function(value){
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
};
this.$get = ['$parse', '$sce', function($parse, $sce) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length;
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
index = 0,
expressions = [],
parseFns = [],
textLength = text.length,
exp;
var getValue = function (value) {
return trustedContext ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
};
var stringify = function (value) {
if (value == null) {
return '';
}
switch (typeof value) {
case 'string':
break;
case 'number':
value = '' + value;
break;
default:
value = angular.toJson(value);
}
return value;
};
var parseStringifyInterceptor = function(value) {
try {
return stringify(getValue(value));
} catch(err) {
console.err(err.toString());
}
};
while(index < textLength) {
if ( ((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1) ) {
exp = text.substring(startIndex + startSymbolLength, endIndex);
expressions.push(exp);
parseFns.push($parse(exp, parseStringifyInterceptor));
index = endIndex + endSymbolLength;
} else {
break;
}
}
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (!mustHaveExpression) {
var compute = function(values) {
for(var i = 0, ii = expressions.length; i < ii; i++) {
if (allOrNothing && angular.isUndefined(values[i])) {
return;
}
expressions[i] = values[i];
}
return expressions.join('');
};
return angular.extend(function interpolationFn(context) {
var i = 0;
var ii = expressions.length;
var values = new Array(ii);
try {
if (ii && !parseFns.length) {
return expressions[0];
} else {
for (; i < ii; i++) {
values[i] = parseFns[i](context);
}
return compute(values);
}
} catch(err) {
console.err(err.toString());
}
}, {
exp: text,
expressions: expressions,
$$watchDelegate: function (scope, listener, objectEquality) {
var lastValue;
return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
var currValue = compute(values);
if (angular.isFunction(listener)) {
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
}
lastValue = currValue;
}, objectEquality);
}
});
}
}
return $interpolate;
}];
}
]);
Lines below were added because in some cases I have a predefined text in my short template and I always want to render it :
if (!expressions.length && !text.contains(startSymbol) && !text.contains(endSymbol)) {
expressions.push(text);
}
if (ii && !parseFns.length) {
return expressions[0];
} else {
I would like to extend some properties recursive (aka. deep copy).
much like jQuery does. I'm not including jquery only b/c of one thing.
jQuery.extend( true, target, object1 )
is there any elegant way you know of that does it with simple javascript or angularjs?
update
please take a look and try to accomplish the same result
http://plnkr.co/edit/GHabYbyhsqtfBPtplksO?p=preview
i did look into .copy() but the "properties (for objects) are deleted"
Here is an extendDeep function based off of the angular.extend function. If you add this to your $scope, you would then be able to call
$scope.meta = $scope.extendDeep(ajaxResponse1.myMeta, ajaxResponse2.defaultMeta);
and get the answer you are looking for.
$scope.extendDeep = function extendDeep(dst) {
angular.forEach(arguments, function(obj) {
if (obj !== dst) {
angular.forEach(obj, function(value, key) {
if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
extendDeep(dst[key], value);
} else {
dst[key] = value;
}
});
}
});
return dst;
};
Note: This function has the side-effect of copying values from later arguments into the earlier arguments. For a simple fix to this side effect, you can change dst[key] = value to dst[key] = angular.copy(value).
All the answers here are valid for versions of Angular before 1.4
As of Angular 1.4, you can use angular.merge to do exactly that:
Unlike extend(), merge() recursively descends into object properties of source objects, performing a deep copy.
https://docs.angularjs.org/api/ng/function/angular.merge
function deepExtend(destination, source) {
for (var property in source) {
if (source[property] && source[property].constructor &&
source[property].constructor === Object) {
destination[property] = destination[property] || {};
arguments.callee(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
}
Plunker
Src: https://gist.github.com/gregdangelo/2343158
Building on Ryan's code, you can shorten the object check and you should also NOT extend functions so you don't override object pointers.
var extendDeep = function extendDeep(dst) {
angular.forEach(arguments, function(obj) {
if (obj !== dst) {
angular.forEach(obj, function(value, key) {
if (dst[key] && angular.isObject(dst[key])) {
extendDeep(dst[key], value);
} else if(!angular.isFunction(dst[key])) {
dst[key] = value;
}
});
}
});
return dst;
};
The same solution as Ryan but with support for array merge
function extendDeep(dst) {
angular.forEach(arguments, function (obj) {
if (obj !== dst) {
angular.forEach(obj, function (value, key) {
if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
extendDeep(dst[key], value);
} else if (dst[key] && dst[key].constructor && dst[key].constructor === Array) {
dst[key].concat(value);
} else if(!angular.isFunction(dst[key])) {
dst[key] = value;
}
}
);
}
}
);
return dst;
}
Angular has a copy method:
angular.copy