fiddle: https://jsfiddle.net/ahmadabdul3/L3eeeh6n/
I'm building an app. This app starts with some static data that I can add to, remove from. The problem I'm having (circular dependency) comes from this initial static data. Here's the scenario:
I have 2 services (fruit, basket). Each fruit can belong to only 1 basket, but each basket can hold many fruits. These two services (below) depend on each other:
function fruitService() {}
function basketService() {}
the reason they depend on each other is because of the initial static data that I have:
function fruitService(basketService) {
var data = [
{
id: 0,
name: 'apple',
basket: function() { return basketService.getById(this.refs.basket); },
refs: {
basket: 0
}
}
];
}
as you can see, I have a function basket that returns a basket item, so that I can dynamically retrieve basket objects from fruit objects.
the basket service is similar:
function basketService(fruitService) {
var data = [
{
id: 0,
name: 'basket1',
fruits: function() { return fruitService.getByIdList(this.refs.fruits); },
refs: {
fruits: [0, ...]
}
}
];
}
same as the fruitService, I have a fruits function that can give me fruit objects when I ask for them. I've tried different combinations of ways to try to break apart the actual static data and the service to overcome this circular dependency, but its not happening.
how can I architect this without having this circular dependency
Have one of the services use $injector and do a lookup for the service at runtime
var injectedService = $injector.get('servicenamehere');
You will need to add $injector to your service parameters for this to work (just in case there was any question)
You don't need 2 services. I think that one is enough.
function basketService() {
var data = [
{
id: 0,
name: 'apple',
basketName: 'basket1'
},
];
var service = {
getFruitById: getFruitById,
getBasketById: getBasketById
};
function getFruitById(fruitId){
// return one fruit here
}
function getBasketById(basketId){
// return your basket with a list of fruits
}
}
Another option would be use a constant to store your data.
angular.module('yourApp').Constant("fruitAndBasketConstant", {
baskets : [{
id : 0,
fruits : [
{
// some info
},
{
// some info
}
]
}]
});
And create a service that will query your constant.
angular.module("yourApp").factory("FruitService"["fruitAndBasketConstant", function(fruitAndBasketConstant){
var service = {
getFruitById: getFruitById
};
return service;
function getFruitById(fruitId){
// loop through fruitAndBasketConstant.baskets
// and return the fruit you want
}
}]);
And do the same for your basketService.
Related
and thanks for your attention and help,
I have a Collection in my livewire controller. This collection contains a list of players, with some properties : here we will just focus on id and name.
So we can imagine that we have 3 players in the collection :
Players[0] : 'id'=>1, 'name'=>'Yann';
Players[1] : 'id'=>2, 'name'=>'Robert';
Players[2] : 'id'=>3, 'name'=>'Jessica';
I need to get these players in my alpine data.
I can easily get these players in Alpine with the #js method :
window.addEventListener('alpine:init', () => {
Alpine.data('data', () => ({
players: #js($players),
}))
})
So, now I have in my alpine.data :
players: [
{ id: 1, name: 'Yann' },
{ id: 2, name: 'Robert' },
{ id: 3, name: 'Jessica' },
]
Now I can easily display the players in my html with a template x-for :
<template x-for="player in players">
<p x-text="player.name"></p>
</template>
But I want to add some additionnal properties in each player object. Those properties will be updated in front depending user's actions.
I would like to get something like this :
players: [
{ id: 1, name: 'Yann', touched20: 0, touched15: 0 },
{ id: 2, name: 'Robert', touched20: 0, touched15: 0 },
{ id: 3, name: 'Jessica', touched20: 0, touched15: 0 },
]
All additionnal properties are the same for each object in the array, so I imagine i could use a foreach loop to put them in the objects.
But I can't see and don't understand how i can include a loop in my alpine.data script to do this.
Anyone could help me ?
I edit my question because I found a solution :
I just make a loopPlayers function outside of my alpine data and call this function in the alpine data :
function loopPlayers() {
let players = [];
const livewirePlayers = #js($players);
livewirePlayers.forEach(element => {
players.push(element);
});
players.forEach(function(element) {
element.touched15 = 0;
})
return players;
}
And in alpine.data :
players: loopPlayers(),
Now I have my collection of players from my livewire controller & I have new properties for each element of the collection in the js data
That was easy, as usual I guess :)
In my state I have an object called foodLog which holds all entries a user enters with one of the keys being foodSelectedKey and I'm trying to return all entries that have a matching value from that key with a different array called foodFilter.
However, this doesn't work and errors out saying foodLog.filter() isn't a function - I've looked this up and it's because it's an Object (I think). Any help would be greatly appreciated!
state = {
// log food is for the logged entries
foodLog: {},
// used for when filtering food entries
foodFilter: [],
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.filter((item) => {
return foodLog.foodsSelectedKey.map((food) => {
return foodFilter.includes(food);
});
});
};
I guess the reason behind the error Is not a function is that the object can not be looped. By that it means you can not iterate an object with differend variables inside, if it has no index to be iterated like an array. The same goes for map(), find() and similar functions which MUST be run with arrays - not objects.
As far as I understand you have an object named foodLog which has an array named foodsSelectedKey. We need to find intersected elements out of foodFilter with the array. This is what I came up with:
state = {
// log food is for the logged entries
foodLog: {
foodsSelectedKey: [
{ id: 1, name: "chicken" },
{ id: 2, name: "mashroom" }
]
},
// used for when filtering food entries
foodFilter: [
{ id: 1, name: "chicken" },
{ id: 2, name: "orange" }
]
};
findMatches = () => {
let foodLog = this.state.foodLog;
let foodFilter = this.state.foodFilter;
let matched = foodLog.foodsSelectedKey.filter((key) =>
{
for (let i=0; i<foodFilter.length;i++){
if(foodFilter[i].name===key.name)
return true
}
return false;
}
);
return matched;
};
The Output is filtered array, in this case, of one element only:
[{
id: 1
name: "chicken"
}]
In order to check the output - run console.log(findMatches()). Here is the CodeSandbox of the solution. (check console at right bottom)
I have the following JSON definition:
export class Company {
name: string;
trips : Trip[] = [];
}
I am able to see the trips in the console using:
console.log(this.company);
But I am not able to access the array elements (trip), I've tried
the following:
for(let i = 0; i < this.company.trips.length; i++){
console.log(this.company.trips[i]);
}
When I display the company to the log I'm getting the trip as you can
see below:
{id: 1}
name: "Sample"
trips: {id: 1, r: "ABC"}
id: 1
Any idea how to access array elements in Angular thanks?
Using a combination of Object.keys() and forEach() will let you iterate through the the object in a similar fashion to an array.
explination
const x = {hello: "hi"};
console.log(Object.keys(x)); // will return array looking like
// [hello] which you can run a for each on.
Object.keys(x).forEach(data => {
console.log(x[data]); // will return `hi`
});
Solution
const trips = {id: 1, r: "ABC"}; // const trips = this.company.trips
if (trips) // check the value exists here
{
Object.keys(trips).forEach((data) => {
console.log(trips[data]);
});
}
if(this.company)
this.Company.trips.forEach(x => {
console.log(x);
});
I am trying to display exactly how many of an array variable I have, not just the items in the array, but one of the 'tags' assigned to it.
$scope.products = [
{
too: 'xxx-xxx-xxx',
foo: 'xxx',
bar: 'xxx-xxx',
}
}
];
When I try to list the length of one of the 'tags' for example "Foo".
Using {{foo.length}} will not work.
I would want my HTML to look something like this:
Menu 1 (1) <--- With this being the "foo" value.
*The foo value will change based on the amount of 'items' i have in my array, so if I had an array like this:
$scope.products = [
{
too: 'xxx-xxx-xxx',
foo: 'xxx',
bar: 'xxx-xxx',
}
{
too: 'yyy-yyy-yyy',
foo: 'yyy',
bar: 'yyy-yyy',
}
}
];
the HTML would reflect that I now have '2' values of 'foo', so it would say
Menu 1 (2), and so on and so forth.. How can I accomplish this?
You can write a javascript function and just call it. (updated to only count unique foos)
$scope.countUniqueFoos = function () {
var uniqueFoos = []; //you could also keep this as a $scope variable if needed
$scope.products.forEach( function (product) {
if (product.hasOwnProperty('foo') && uniqueFoos.indexOf(product.foo) == -1) {
uniqueFoos.push(product.foo);
}
});
return uniqueFoos.length;
};
And in the HTML:
<div>NumFoos: {{countUniqueFoos()}}</div>
I'm working in a collection that contains a model with collections of "itself". For example:
[{
id: 1
name: "John",
children: [
{
id: 32
name: "Peter",
children: []
},
{
id: 54
name: "Mary",
children: [
{
id:12,
name: "Kevin"
}
]
},
]
}]
Let say that I want to get the Kevin "user" by its Id. But all that I have is the "first collection". How can I do that?? And about setting a user within a collection? Another thing: Its possible to get all the Kevin "parents" from him? Like Mary and John?
Does anyone has come to a issue like that?
Thanks a LOT
Well I've made a recursive function on the User's Collection that seems to solved the problem for now ( the best of this is that I can use for retrieve a "deep" model and change it.). Something like that ( if someone has any suggestions, be free to give it a opinion ):
findUserById: function(id) {
var self = new Backbone.Collection(this.toJSON());
return thisCollection(id, this);
function thisCollection(id, collection, array) {
var innerSelf = collection || this;
var thisArray = array || [];
for(var i = innerSelf.models.length; i--;) {
if(innerSelf.models[i].get('id') == id) {
return [innerSelf.models[i]].concat([thisArray]);
}else {
if(innerSelf.models[i].get('children').length > 0) {
thisArray.push(innerSelf.models[i]);
return thisCollection(id, innerSelf.models[i].get('children'), thisArray);
}else {
innerSelf.remove(innerSelf.models[i]);
return thisCollection(id, self, []);
}
}
}
}
}
Basically I return an array with 2 items. The first is the record that I'm looking for and the second is an array with the parents of this user.
Underscore (which is a Backbone dependency, so you already have it) is great for this sort of thing; if you use its "map" function (which Backbone provides as a method on Collection) with its find function, you can do the following:
findPersonInPeopleCollection: function(nameWeAreLookingFor) {
function findChildren(person) {
if (!person.children) return [person];
var children = _.map(person.children, function(child) {
foundPeople.push(findChildren(child);
})
return _.flatten(children);
}
var allPeople = _.flatten(myCollectionOfPeople.map(findChildren));
return _(allPeople).find(function(person) {
return person.get('name') == nameWeAreLookingFor;
}
}
If you wanted to store the parents initially you could either add logic to your "Person" model class's initialize function, eg.
var Person = Backbone.Model.extend({
initialize: function() {
_.each(this.get('children'), function(child) {
child.parent = this;
}, this);
}
});
You could also do something similar by overriding your collection's add method, or adding an event handler to it that triggers after people get added.