I need to write a data aggregation method for my cart, but i'm stuck. I have const payload with empty array and loop that will run getData() method for every product in my cart and push the result to my empty array. Also i need my getData() method, that will return for me id, amount, price and priceSingel. All of those we got in CarProduct constructor. I am still new and i do get stuck sometimes with the basics.
const payload = {
products: [],
address: 'test',
totalPrice: thisCart.totalPrice,
};
for(let product of thisCart.products) {
product.getData();
}
payload.products.push(product);
/* get data for .getData */
getData() {
return id;
return amount;
return price;
return priceSingle;
return params;
}
/* part of cart product constructor */
class CartProduct {
constructor (menuProduct, element) {
const thisCartProduct = this;
thisCartProduct.id = menuProduct.id;
thisCartProduct.name = menuProduct.name;
thisCartProduct.price = menuProduct.price;
thisCartProduct.priceSingle =menuProduct.priceSingle;
thisCartProduct.amount = menuProduct.amount;
Hard to help you with this little view of your code.
However, some problems that you can resolve:
A method can't have more than ONE effective return, the first encountered is used.
So for your getData(), you should return an object:
getData() {
return { // this is a shortcut where key is named like the variable
id,
amount,
price,
priceSingle,
params
}
}
I don't know what's your params here.
Secondly, add data to your array while you loop, not after (and here you don't use the getData() return)
for(let product of thisCart.products) {
payload.products.push(product.getData());
}
Related
Why do I receive an error message in the console that getStoredQuests.push is not a function at Object.addQuestionOnLocalStorage
class Question{
constructor(id, questionText, options, correctAnswer) {
this.id = id;
this.questionText = questionText;
this.options = options;
this.correctAnswer = correctAnswer;
}
}
let questionLocalStorage = {
setQuestionCollection: (newQuestion) => {
localStorage.setItem('questionCollection', JSON.stringify(newQuestion));
},
getQuestionCollection: () => {
return JSON.parse(localStorage.getItem('questionCollection'));
},
removeQuestionCollection: () => {
localStorage.removeItem('questionCollection');
}
}
newQuestion = new Question(questionId, newQuestText.value, optionsArr, corrAnswer);
getStoredQuests = questionLocalStorage.getQuestionCollection();
getStoredQuests.push(newQuestion);
questionLocalStorage.setQuestionCollection(getStoredQuests);
The error is because getStoredQuests is not an array. You have probably not considered if there is no question in local storage initially, then localStorage.getItem('questionCollection') will return empty string or it might be case that you have some value with key questionCollection in local storage but is not in proper format to to be parsed as JSON array object.
For the first case solution is to check if localStorage.getItem('questionCollection') is empty then return empty array from getQuestionCollection and for the second case you to need to properly format the array '(serialize the array to be stored with JSON.stringify)'.
To let your problem understand properly, open up console see if there are red lines
I'm using cloud firestore for my DB, having no problems with read and writes. My issue is that i don't understand how to know when real time update has ended!
I'm using real time update as described in official cloud firestore documentation. It returns a function instead an observable. Now i'm not sure how to use it correctly.
I need to execute some code after data is loaded, but i have no subscribe((data) => {...}) to put it there!!
How to do it?
If i show lack of knowledge, please guide me to some documentation.
Thanks
This code is working fine and i'm using it directly on html like ListService.places$ to access data array.
public places$: BehaviorSubject<Array<Place>> = new BehaviorSubject<Array<Place>>([]);
private unsubscribe: Function;
public list(city: string, country: string) {
return this.unsubscribe = this.firestore.firestore.collection('places')
.where("location.country", "==", country)
.where("location.city", "==", city)
.orderBy("startDate", "desc")
.onSnapshot(querySnapshot => {
const list: Place[] = [];
if(!querySnapshot.docs.length) {
this.places$.next(list);
} else {
querySnapshot.docs.forEach(doc => {
let places = new Place();
place = doc.data() as Place;
places.push(place);
if(list.length === querySnapshot.docs.length) {
this.places$.next(list);
}
});
}
});
}
You need to subscribe to this.places$
this.places$.subscribe((data) => {
console.log(data);
});
What the real time function returns does not metter, because the real time function adds the output to the BehaviorSubject.
See here
let places = new Place();
place = doc.data() as Place;
places.push(place);
if(list.length === querySnapshot.docs.length) {
this.places$.next(list);
}
That means if you want to do something with the data you just need to subscribe to places$.
Also i think you have a small typo there, you never pushed values to the list array.
It should be something like:
...
.onSnapshot(querySnapshot => {
const list: Place[] = [];
if(!querySnapshot.docs.length) {
this.places$.next(list);
} else {
querySnapshot.docs.forEach(doc => {
let place = new Place();
place = doc.data() as Place;
list.push(place);
if(list.length === querySnapshot.docs.length) {
this.places$.next(list);
}
});
}
});
Little context, I have an API that returns a competition with a link to its matches and each match has a link to the teams that played.
My intention for now, is to build a standings table based on the competition matches therefore, my first step is to print a list of unique "teams" from that array of matches. That is I need to go through all the matches, check the home and away teams, and if the team is not present, push it into the list.
Here is the tricky part, I have the teams like this:
match.away = this.teamService.getTeam(match._links.awayTeam.href);
match.home = this.teamService.getTeam(match._links.homeTeam.href);
And my getTeam() method is returning a promise Team.
getTeam(id: string): Promise<Team> {
return this.http.get(id)
.toPromise()
.then(response => response.json() as Team)
.catch(this.handleError);
}
The Team class has teamName: string; which is the value I need to verify to build the array and that is the part I need help with. I have an OnInit implemented that goes like this:
ngOnInit(): void {
this.teams = [];
this.route.params
.switchMap((params: Params) => this.competitionService.getCompetition(+params['id']))
.subscribe(competition => {
this.competitionService.getMatches(competition)
.then(
matches => {
matches.forEach(match => {
match.away = this.teamService.getTeam(match._links.awayTeam.href);
match.home = this.teamService.getTeam(match._links.homeTeam.href);
match.away.then(away => {
var found = this.teams.find(team => {
return team.teamName.toLocaleLowerCase().includes(away.teamName)
});
if(!found) {
this.teams.push(away);
}
});
match.home.then(home => {
var found = this.teams.find(team => {
return team.teamName.toLocaleLowerCase().includes(home.teamName)
});
if(!found) {
this.teams.push(home);
}
});
});
return matches;
}
).then(matches =>
this.matches = matches
);
this.competition = competition
});
}
What I tried to do there is resolve the Promise first and check for the teams' presence in my array and if it is !found then push it to the list.
My HTML looks like this:
<md-list>
<md-list-item *ngFor="let team of teams">
<button md-raised-button>{{team.teamName}}</button>
</md-list-item>
</md-list>
But the result is a list of each team in each match, so appearantly the push part is working because the array teams: Team[]; is being populated and printed correctly, but the verification is not.
Please don't kill me for my code cause I have 0 idea on this, I'm just learning on the fly, ot only Angular but to code.
So my Question Is:
How do I build an array of unique teams from that promise of matches?
Furthermore, how do you usually split arrays for these calculations? Every research I found points me to a "Custom Pipe" but it seems to be filtering the view only, After I have my array of teams, I have to make calculations on each of the matches for Goals, points, etc, so it does not seem that it will work for me.
Any ideas or suggestions?
I'd add a new method to delegate the teams array population:
addToSet(team): void {
let teamNames: string[] = this.teams.map((team) => team.teamName.toLocaleLowerCase());
if (teamNames.lastIndexOf(team.teamName.toLocaleLowerCase()) === -1) {
this.teams.push(team);
}
}
And then, substitute this part
var found = this.teams.find(team => {
return team.teamName.toLocaleLowerCase().includes(home.teamName)
});
if(!found) {
this.teams.push(home);
}
for
this.addToSet(home);
Also, to be sure that both promises are fulfilled before going on with the array loading, you could wrap both getTeam calls with Promise.all.
Promise.all([
this.teamService.getTeam(match._links.awayTeam.href),
this.teamService.getTeam(match._links.homeTeam.href)
]).then((teams) => {
match.away = teams[0];
match.home = teams[1];
});
EDIT: putting it all together
addToSet(team): void {
let teamNames: string[] = this.teams.map((team) => team.teamName.toLocaleLowerCase());
if (teamNames.lastIndexOf(team.teamName.toLocaleLowerCase()) === -1) {
this.teams.push(team);
}
}
ngOnInit() {
// rest of the initialization
matches.forEach(match => {
Promise.all([
this.teamService.getTeam(match._links.awayTeam.href),
this.teamService.getTeam(match._links.homeTeam.href)
]).then(teams => {
match.away = teams[0];
match.home = teams[1];
this.addToSet(away);
this.addToSet(home);
});
});
}
EDIT 2: added toLocaleLowerCase() in addToSet() for check. I'm assuming the getTeam returns a single object, otherwise, you'll need to enforce the returned object schema.
I'm building a webshop where users are able to add products for one of more stores in their basket and checkout (like AliExpress).
On the cart overview page, the content of the basket is shown sorted by store. If the same product is added multiple times over different stores, the product is show by every store.
Now, I want to create an order for every store with the products ordered by that store. I'm using Angular to create the list with products ordered/filtered by store.
That data will be sent to my Node.JS server, to loop the contents and create some orders with items.
The problem, I think, is that the data is processed like a 'object' and not an 'array'. I have found a function which converts a object to an array, but the length is still '0'.
How can I process the data so I can loop through the different items?
AngularJS code to sort cart by store
$scope.filterProducts = function(groupName) {
$scope.productList = [];
$http({
method: 'GET',
xhrFields: {withCredentials: true},
url: '/loadCart'
}).then(function successCallback(response){
if (response.data) {
var mapByShop = function(arr, groupName) {
return arr.reduce(function(result, item) {
result[item[groupName]] = result[item[groupName]] || {};
result[item[groupName]][item['productId']] = item;
console.log('GROUPNAME en RESULT', groupName, result);
return result;
}, {});
};
if (response.data.length > 0) {
if (groupName == 'shopName') {
$scope.productList = mapByShop(response.data, groupName);
} else {
$scope.checkoutList = mapByShop(response.data, groupName);
}
}
}
}, function errorCallback(response){
console.log(response);
});
}
The $scope.productList is sent as 'data' in a $http POST function.
Node.JS code to convert an object to an array
function convertObjectToArray(object, cb){
var cartContent = [];
for (var i in object) {
cartContent[i] = object[i];
}
console.log("convertObjectToArray");
return cb(cartContent);
}
Code to process the data (where length is zero)
convertObjectToArray(req.body.cart, function(result){
console.log(isArray(result));
console.log('result', result);
console.log("lenght", result.length);
})
FYI: the isArray function
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;
}
if array order is not important, you should use
cartContent.push(object[i]);
It will update the .length property automaticly
Your problem is that you are adding properties to the array object, and not using the Array API to insert at integer locations in the object. This means the array essentially remains "empty". If you key on an integer when inserting into the array then your code will work better.
The broken bit:
for (var i in object) {
cartContent[i] = object[i];
}
i is a string key here and will not increment the length of the Array unless the value coerces to an integer value (I think).
Something like this might work:
// Untested...
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++) {
cartContent[i] = object[keys[i]];
}
Or like the other answer suggested, use the push API.
Aside:
If you are in a modern JS engine you can:
Use Object.values, or
Write a couple of utility functions and convert an object to an array using the following
:
var o = iterable({foo:'fooValue', bar: 'barValue'});
console.log([...o]);
The utility functions:
function iterable(o) {
if(o[Symbol.iterator]) {
return o;
}
o[Symbol.iterator] = iter.bind(null, o);
return o;
}
function* iter(o) {
var keys = Object.keys(o);
for (var i=0; i<keys.length; i++) {
yield o[keys[i]];
}
}
I have application on AngularJs.
I have variable in scope, which is initialized with data from API. $scope.receivedRequests = CurrentUserData.incomeRequests();
Cause request to API takes some time, $scope.receivedRequests is empty at start.
$scope.receivedRequests I've used with ng-repeat
<div class="full-row" ng-repeat="row in receivedRequests | filterByStatus:[State.PENDING] | partition:3 track by $index">
This data is also filtered.
But when I tried to replace this filtering into controller, like
$scope.filterByStatus = function (statuses) {
return $filter('filterByStatus')($scope.receivedRequests, statuses);
};
$scope.pendingRequests = $scope.filterByStatus([State.PENDING]);
$scope.pendingRequests were always empty.
How can I filter data in the controller?
.filter('filterByStatus', function () {
return function(arr, statuses) {
if (!arr) { return; }
return arr.filter(function(value) {
return statuses.some(function(val) {
return value.status == val;
});
});
};
});
$scope.receivedRequests is just array of elements, that have string property status (alos id, date etc.) as example : status : "Pending"
In that case I would use promises and split the code like this.
Factory service
app.factory('myService', function($http){
return {
getRequests: function(){
return $http.get('http://someurl');
}
}
})
Controller
app.controller('myController', function(myService){
$scope.filteredArray = [];
myService.getRequests()
.then( function(data){
$scope.filteredArray = $scope.filterFunction(data);
})
$scope.filterFunction = function(array){
//function logic
//...
}
})
Notes:
The trick here is done by calling the filtering function inside the then() function. This way you are sure the data from the API is been already fetched and after that you can filter it.
I am not sure how your $scope.recievedRequests object look, can you try the below... why don't you send the State.pending value with an associated key for your array.
$scope.filterByStatus = function (statuses) {
return $filter('filterByStatus')($scope.receivedRequests, statuses);
};
$scope.pendingRequests = $scope.filterByStatus({key: State.PENDING})[0];