Wierd AngularJS error - javascript

I am rather new to AngularJS programming and was working on a sample application. I came across a problem while returning an object from a service. Consider following code in my custom service:
this.getCompanyInfo = function(companyID)
{
console.log( companyID );
angular.forEach( companyInfo, function( coInf ) {
if( coInf.companyID == companyID )
{
console.log(coInf);
return coInf;
}
})
}
in this code, companyInfo is an array containing information about companies, each company is represented by an object. the second console.log is showing this:
Object {companyID: "CHCL", companyName: "Chilime Hydropower", stockPriceTrend: Array[4]}
in my controller, I have this:
$scope.companyInfo = dataServices.getCompanyInfo( $routeParams.companyID);
console.log($scope.companyInfo);
but here, console.log says 'undefined'.
I don't know what wrong I have been doing, any help will be highly appreciated!
Regards.

You are just returning from the iterator function argument of the forEach (not from getCompanyInfo) which will still go on. But you need to return the actual value from your getCompanyInfo function. You can just use a simple for loop and return the value once you find a match.
this.getCompanyInfo = function(companyID)
{
var cInfo, i, l;
for(i=0,l=companyInfo.length; i<l;i++){
if((cInfo = companyInfo[i]).companyID === companyID ){
return cInfo ;
}
}
}
Returning from the iterator function does not break the looping using angular.forEach. Loop will still go on.

change function to
this.getCompanyInfo = function(companyID)
{
var coInfo;
console.log( companyID );
angular.forEach( companyInfo, function( coInf ) {
if( coInf.companyID == companyID )
{
console.log(coInf);
coInfo = coInf;
}
})
return coInfo;
}
you are not returning value from function but from forEach and that will not return the value from the function.
it has nothing to do with angular, btw.
EDIT
also you can use libraries like lodash/underscore for search/filter functionalities

Related

Creating custom angular filters

i have a json of items and a barcode scanner, the barcode scanner inputs directly into my app.
Now the items have a primary part number and a secondary part number, more often than not the result from the barcode scanner will be the primary part number, but i have to check with both to be sure.
I'm trying to implement a custom filter to do this, but it doesn't seem to be working, can anyone maybe let me know what i'm doing wrong ?
storeApp.filter('barcodeScanner', function() {
return function(parts, barcode) {
angular.forEach(parts, function (vals, key) {
if( !angular.isUndefined(vals.Part_Number) && vals.Part_Number !== null )
if (angular.equals(vals.Part_Number,barcode))
return parts[key];
});
angular.forEach(parts, function(vals, key) {
if ( !angular.isUndefined(vals.Other_Part_Number) && vals.Other_Part_Number !== null )
if (angular.equals(vals.Other_Part_Number,barcode))
return parts[key];
});
};
});
i then call the filter later in the controller,
$scope.addItemToCart = function() {
$scope.shoppingCart.push($filter('barcodeScanner')($scope.parts, $scope.ItemToAdd));
console.log($scope.Cart.itemToAdd);
console.log($filter('barcodeScanner')($scope.parts, $scope.Cart.itemToAdd));
$scope.Cart.itemToAdd = "";
console.log($scope.shoppingCart);
};
however the result from the filter keeps returning undefined. i know for a fact that the entry i want does exist, because when i use a normal $filter('filter') it works fine, but i cannot risk such a widespread filter for my app.
thanks for any help :)
I believe the problem lies in the forEach part of your function. A forEach function does not return a value. You are returning a value to your iterator function and since the forEach is not returning that returned value from the iterator then you will have nothing to push in your $scope.shoppingCart.push($filter('barcodeScanner')($scope.parts, $scope.ItemToAdd));
Saving to a variable ie. matchedPart declared inside the anonymous factory(wrapper) function and returning it outside of the forEach function should solve the undefined:
storeApp.filter('barcodeScanner', function() {
return function(parts, barcode) {
// declare a variable here
var matchedPart;
angular.forEach(parts, function (vals, key) {
if( !angular.isUndefined(vals.Part_Number) && vals.Part_Number !== null )
if (angular.equals(vals.Part_Number,barcode))
// save it to new variable
matchedPart = parts[key];
});
angular.forEach(parts, function(vals, key) {
if ( !angular.isUndefined(vals.Other_Part_Number) && vals.Other_Part_Number !== null )
if (angular.equals(vals.Other_Part_Number,barcode))
// save it to new variable
matchedPart = parts[key];
});
// return it outside the forEach function
return matchedPart;
};
});
last note:
I would also think you should refactor by combining your forEach functions. Not have 2 separate ones. Combining your isUndefined check with !angular.isUndefined(vals.Part_Number) && !angular.isUndefined(vals.Other_Part_Number) && vals.Part_Number...
Instead of .equals you need to check == because string will not exactly equal to number
angular.equals is nothing but strongly check in javascript === which check both values are equal with their type or not.
if(angular.equals(vals.Other_Part_Number,barcode))
Changed to
if(vals.Other_Part_Number == barcode)
If you want to strictly check then you need to convert both the value to number using parseInt and then check
if(angular.equals(parseInt(vals.Other_Part_Number),parseInt(barcode)))
Hope this could help you. Thanks.

Angular q all dynamic results

I need to wait for two promises to resolve before doing something.The problem is that sometimes I can have only one promise to send based on what the client want, and I need to count on the results that comes from this $q.all. How can I dynamically define the vars?
var makePurchaseToResolve = [];
if( CartService.items.draws.length ) {
var drawOrder = {};
drawOrder.price = $scope.getTotal();
drawOrder.paymentMethodId = $scope.payment.paymetMethodId;
drawOrder.orderItems = CartService.items.draws;
drawOrder.payFromBalance = false;
makePurchaseToResolve.push(DrawService.makePurchase(drawOrder));
}
if( CartService.items.hunters.length ) {
var hunterOrder = {};
hunterOrder.paymentMethodId = $scope.payment.paymetMethodId;
hunterOrder.orderItems = CartService.items.hunters;
makePurchaseToResolve.push(HunterService.makePurchase(hunterOrder));
}
$q.all(makePurchaseToResolve).then(function( res ) {
$q.all([HunterService.getPurchase(res[0].data.data),DrawService.getPurchase(res.data.data)]).then(function() {
//here is the problem, because i dont know if i have the two promises to resolve or only one
$scope.hunters = res[0].data.data[0];
$scope.drawOrder = res[1].data.data;
})
}
A little known fact is that $q.all can also take an object rather than an array which can simplify your life in this case:
var purchases = {}; // an object rather than an array for named properties
if( CartService.items.draws.length ) {
//...
purchases.drawOrder = DrawService.makePurchase(drawOrder); // named assign
}
if( CartService.items.hunters.length ) {
//...
purchases.hunters = HunterService.makePurchase(hunterOrder);
}
// any more additions to the objects here..
$q.all(purchases).then(function(resolved){
// resolve anything further you need
$scope.purchases = resolved; // Add $scope.purchases.hunters etc
});
The promises are resolved in an object so they're named, instead of having an array you get a resolved object with properties so instead of doing arr[0] which might be out of order you'd do resolved.hunters or resolved.drawOrder. This solution just nests into $scope one level and does it automatically.
You can also keep the order of the promises, if you'd like, and pass in null for those that were not applied based on your condition.
This is an alternative for when your service gets a dynamic number of purchase requests from the caller and naming is not an option.
$q.all([null, huntersOrderPromise])
.then(function(data){
$scope.drawOrder = data[0]; // will be null
$scope.hunterOrder = data[1];
});

Mongoose Setters only get called when create a new doc?

I use Mongoose getter and setter I recently found that setter only applies when I create a new doc (UserSchema.create...) but setter will not get called for update (UserSchema.findByIdAndUpdate...). Am I right or am I missing anything. If I'm right, is there setter for update?
(http://mongoosejs.com/docs/2.7.x/docs/getters-setters.html
Thanks.
UPDATED
I found git issue: https://github.com/LearnBoost/mongoose/issues/751 , it means this is expected behavior. Not sure I'm right.
Found out that I should not use findByIdAndUpdate, instead I should query object, edit and save.
Found a nice method that encapsulates that from https://groups.google.com/forum/?fromgroups=#!topic/mongoose-orm/8AV6aoJzdiQ:
function findByIdAndSave( model, id, data, next ){
model.findById( id, function( err, doc ) {
if( err ){
next( err, null );
} else {
if(! doc ){
next( new Error("Object to save not found"), null );
} else {
// There must be a better way of doing this
for( var k in data ){
doc[k] = data[k];
}
doc.save( next );
}
}
});
}
(This really belongs as a comment, but the answer allows better code formatting)

Passing parameters to a jQuery closure not working on Multisuggest plugin

I have a question of which someone might find this much simpler than I do, but alas, I don't have much experience with custom jQuery plugins.
The previous developer at my place of work left me with a lot of left-over plugins that don't seem to work very well, most which I've been able to fix but this which has been bugging me for a while.
It is a custom Multiple Suggestion plugin (called multisuggest) written in jQuery, and it has a set of functions that it uses internally (*e.g. setValue to set the value of the box, or lookup to update the search)*
It seems he's tried to call these plugin functions from an external script (this exteranl script specifically imports newly created suggestions into the multisuggest via user input and sets the value) like this:
this.$input.multisuggest('setValue', data.address.id, address);
This seems to call the function as it should, except the second and third parameters don't seem to be passed to the function (setValue receives nothing), and I don't understand how I can get it to pass these. It says it is undefined when I log it in the console. The functions are set out like this (I've only including the one I'm using and an internal function from multisuggest called select that actually works):
MultiSuggest.prototype = $.extend(MultiSuggest, _superproto, {
constructor : MultiSuggest,
select: function () { // When selecting an address from the suggestions
var active, display, val;
active = this.$menu.find('.active');
display = active.attr('data-display');
val = active.attr('data-value');
this.setValue(display, val, false); // This works, however when I do it as shown in the above example from an external script, it doesn't. This is because it doesn't receive the arguments.
},
setValue : function(display, value, newAddress) { // Setting the textbox value
console.log(display); // This returns undefined
console.log(value); // This returns undefined
if (display && display !== "" &&
value && value !== "") {
this.$element.val(this.updater(display)).change();
this.$hiddenInput.val(value);
this.$element.addClass("msuggest-selected");
}
if(newAddress === false){
return this.hide();
}
},
});
Why does it listen to the function, but not the values passed to it? Do I need to include an extra line of code somewhere to define these arguments?
Anyone with jQuery experience would be of great help! This is bottlenecking progress on a current project. Thanks for your time!
EDIT:
I've missed out the code of how the arguments are trying to be passed from the external script to the internal function of the plugin. Here is the plugin definition with how the external call is handled, can anyone see a problem with this?
$.fn.multisuggest = function(option) {
return this.each(function() {
var $this = $(this), data = $this.data('multisuggest'), options = typeof option === 'object' && option;
if (!data) {
$this.data('multisuggest', ( data = new MultiSuggest(this, options)));
} else if (typeof(option) === 'string') {
var method = data[option];
var parameters = Array.prototype.slice.call(arguments, 1);
method.apply(this, parameters);
}
});
};
The "usual" plugin supervisor looks like this :
// *****************************
// ***** Start: Supervisor *****
$.fn.multisuggest = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || !method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist in jQuery.' + pluginName );
}
};
// ***** Fin: Supervisor *****
// ***************************
All the looping through this should be inside the method functions, not in the supervisor.
I'm a little worried that new MultiSuggest(...) appears in the current supervisor. That sort of thing is totally unconventional. The original author clearly had something in mind.
You need to extend the jQuery plugin function which is attached to $.fn['multisuggest'], that function is probably only taking and passing one parameter.

Why can I not see my object's properties

I'm stumped here and would love to know what I'm mising.
I was just re-writing some code and cleaning some stuff up. I have a method that does an ajax call and returns a json string. Since I use the method a couple of places I abstracted to this method here within a name space.
This is json object returned
{"charge_ID":"4","price":"37","description":"corporate rate"}
This is the abstracted method.
wwdb.getChargeRate = function( chargeID ){
var rate = {}, prop;
$.post(baseUrl + 'dashboard_job/jx_get_charge_rate',
{
'charge_ID': chargeID
},
function( data ){
for( prop in data) {
rate[prop] = data[prop];
}
},
'json'
);
return rate;
};
I thought I would just be able to assign data to rate but had to iterate through to get that to work. In fact I thought I would be able return data direct from within the function() part of $.post() method, but that didn't work. So question 1. is why can I not just return data?
But much more basic and frustrating is the fact that in the code below I cannot access the object properties. Question 2. is why?
$('#charge_rate_slt').change(function(){
var t = $(this), v = $(t).val(), rate;
console.log(wwdb.getChargeRate( v )); //returns object ok
rate = wwdb.getChargeRate( v ); //assigns object ok
console.log(rate); //displays in firebug as object with properties
console.log(rate.price) //undefined
for(p in rate)
console.log( p ); //undefined?
});
Remember this is asynchronous -- the function has not been called when you are looking at it.
When the function returns rate the call has not finished. If you look at the rate variable a later it will have the data. I didn't look at the 2nd part of the code, but I expect is a similar issue.
To understand this issue make the following change:
wwdb.getChargeRate = function( chargeID ){
var rate = {}, prop;
$.post(baseUrl + 'dashboard_job/jx_get_charge_rate',
{
'charge_ID': chargeID
},
function( data ){
for( prop in data) {
rate[prop] = data[prop];
}
},
'json'
);
return { "price" : "I am not undefined" };
};
Now your calling function will have something to print.

Categories

Resources