I put the collected data from services into the array. I put this array through #Input into the second component but in it the array instead of length 18 has 0;
TS:
arr: Datas[] = [];
constructor(private dataService: DataService) {
}
ngOnInit() {
console.log("ng init");
this.getArraysFromData();
}
getArraysFromData() {
this.DataService.getDatas().subscribe((data: Datas[]) => {
for (let item of data) {
this.arr.push(item);
}
console.log("smartlamps from Map ", this.arr);
});
}
}
HTML :
<app-osm-generator [dataInput]="arr"></app-osm-generator>
COMPONENT WHERE I INPUT
#Input() dataInput: Datas[];
ngOnInit(): void {
this.takeDataFromInput();
}
takeDataFromInput() {
console.log(this.dataInput.length); <-- is 0 must be 18
for(let item of dataInput) {
console.log(item);
}
}
You are getting console.log(dataInput.length); coz its is being called before data is assigned
There are 2 ways you can solve the issue :
1) Include app-osm-generator only when data is available
<app-osm-generator *ngIf="arr.length > 0" [dataInput]="arr"></app-osm-generator>
2) implements OnChanges
ngOnChanges(changes: SimpleChanges) {
let data = changes.dataInput;
console.log('prev value: ', data.previousValue);
console.log('got name: ', data.currentValue);
console.log(data.length);
}
Checking console will clear all your doubts regarding the flow
For more details on 2nd method : READ
Suggestion :
this.DataService.getDatas().subscribe((data: Datas[]) => {
this.arr = [ ...this.arr , ...data]; // instead of looping try out ES6's feature
console.log("smartlamps from Map ", this.arr);
});
I don't know if is a mistake in the question but not this.DataService because DataService is the Service declaration and dataService is the instance injected..
this.DataService.getDatas().subscribe((data: Datas[]) => {
for (let item of data) {
this.arr.push(item);
}
console.log("smartlamps from Map ", this.item);
Good:
this.dataService.getDatas().subscribe((data: Datas[]) => { // good!
for (let item of data) {
this.arr.push(item);
}
console.log("smartlamps from Map ", this.item);
add a ngIf
<app-osm-generator *ngIf="arr" [dataInput]="arr"></app-osm-generator>
Related
I really need help, because I don't know why this is happening.
I have an Angular webapp with a Firebase backend. My webapp shows 7 random products from a products collection in Firebase. This 7 random products should be stored so that, the users can see it on different devices.
But if i call my savemethod, something weird is happening. The save method, calls a the method, who generates the 7 random products and i don't know why, because i don't call it expecially.
Heres my code:
export class ProductComponent implements OnInit {
schedulenumbers: ISchedule = { numbers: [] }; //Just an Object with an array of numbers in it
length: number = 0; //lenght is needed to show or hite the list in the template
filteredproducts: IProduct[] = [];
numbers: number[] = []; // the numbers to filter the products
constructor(private productservice: ProductService) {
console.log("constructor wurde aufgerufen");
}
ngOnInit(): void {
this.filteredproducts = [];
console.log("ngOnInit was called")
//get the numbers from the database
this.productservice.getnumbers().subscribe(
numbers => {
this.numbers = numbers.numbers;
}
);
//get all products from the database collection
this.productservice.getallproducts().pipe(
map((products) => {
this.length = products.length;
for (let i = 0; i < 7; i++) {
this.filteredproducts.push(product[this.numbers[i]]);
}
return this.filteredproducts;
})
).subscribe();
}
getRandomproducts(): void {
this.filteredproducts = [];
this.numbers = [];
console.log("getRandomproducts was called");
this.productservice.getallproducts().pipe(
map(products => {
for (let i = 0; i < 7; i++) {
this.numbers[i] = Math.floor(Math.random() * products.length + 1)
if (products[this.numbers[i]] == undefined) {
i -= 1;
}
else {
this.filteredproducts.push(products[this.numbers[i]]);
}
}
console.log(this.numbers);
return this.filteredproducts;
})
).subscribe();
}
saveNumbers(): void {
console.log("savenumbers was called")
console.log(this.numbers);
this.schedulenumbers.numbers = this.numbers;
console.log(this.filteredproducts.length);
this.productservice.updatenumbers(this.schedulenumbers);
}
}
Here is the code for the productservice:
#Injectable({
providedIn: 'root'
})
export class ProductService {
Productcollection: AngularFirestoreCollection<IProduct>;
schedulenumbers: AngularFirestoreDocument<ISchedule>;
numbers: Observable<ISchedule>
Products$: Observable<IProduct[]>;
constructor(private firestore: AngularFirestore, private auth: AngularFireAuth) {
this.auth.currentUser.then(user => {
this.productcollection = this.firestore.collection(`${user.uid}`);
this.schedulenumbers = this.firestore.collection(`${user.uid}`).doc("numbers")
})
}
getallproducts() {
return this.Products$ = this.Productcollection.valueChanges({ idField: 'ProductId' }).pipe(
map(products =>
products.filter(product => product.ProductId != 'numbers')
)
);
}
getnumbers() {
return this.numbers = this.schedulenumbers.valueChanges();
}
updatenumbers(numbers: ISchedule) {
this.schedulenumbers.update(numbers)
}
addrecipe(product: IProduct) {
this.Productcollection.add(product);
}
}
I have attached a picture from the Firefox console that shows that if I press the button for the getRandomproducts method twice and then I press the button for the saveNumbers method, strangely white from the saveNumbers method, the for loop of getrandomproduct number method is called and in the filteredproducts array are not longer only 7, but much more products. But why?
Firefox console
My problem is that the iterableDiffer detect a change when I only what to inialize the data from the database. I dont want to get this first change detection. I only want to get a change detection when the user edits the array.
I tryed to put the database request in the constructor. Didnt change anything.
constructor(
private route: ActivatedRoute,
private StoreService: StoreService,
private iterableDiffers: IterableDiffers
) {
this.iterableDiffer =
this.iterableDiffers.find([]).create(null);
}
myArray: MyClass[] = [];
iterableDiffer: IterableDiffer<unknown>;
ngOnInit() {
const id = this.route.snapshot.params.id;
this.StoreService.getData(id)
.subscribe(data => this.myArray = data);
// on this subscribe, the Differ falsely gets triggered
}
ngAfterViewChecked() {
const changes = this.iterableDiffer.diff(this.myArray);
if (changes) {
changes.forEachAddedItem((record: IterableChangeRecord<MyClass>) => {
console.log('Added Changes detected');
this.StoreService.addToDatabase(record.item);
});
}
}
// gets called by User Click, here i want to have the Differ called
// This works fine
addElmt(elmt: MyClass) {
this.myArray.push(elmt);
}
The code snippet is a simplified version of the real code. I can not call the StoreService.addToDatabase() in the function addElmt(). This is just for better explanation.
Thank you for your help!
Always can has a variable 'myObserver' and do
myObserver:any=null
ngAfterViewChecked() {
const changes = this.iterableDiffer.diff(this.myArray);
if (changes) {
changes.forEachAddedItem((record: IterableChangeRecord<MyClass>) => {
console.log('Added Changes detected');
this.StoreService.addToDatabase(record.item);
});
}
if (!myObserver)
{
const id = this.route.snapshot.params.id;
myObserver=this.StoreService.getData(id)
.subscribe(data => this.myArray = data);
}
}
Objective : i have a button named "feed data" so when ever i click it the data will be loaded i mean the tree with checkboxes here my requirement is when ever i click it along with data all the check boxes have to be checked on init i tried using
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
but it is not working below is my code
click(tree: TreeModel) {
this.arrayData = [];
let result: any = {};
let rs = [];
console.log(tree.selectedLeafNodeIds);
Object.keys(tree.selectedLeafNodeIds).forEach(x => {
let node: TreeNode = tree.getNodeById(x);
// console.log(node);
if (node.isSelected) {
if (node.parent.data.name) //if the node has parent
{
rs.push(node.parent.data.name + '.' + node.data.name);
if (!result[node.parent.data.name]) //If the parent is not in the object
result[node.parent.data.name] = {} //create
result[node.parent.data.name][node.data.name] = true;
}
else {
if (!result[node.data.name]) //If the node is not in the object
result[node.data.name] = {} //create
rs.push(node.data.name);
}
}
})
this.arrayData = rs;
tree.selectedLeafNodeIds = {};
}
selectAllNodes() {
this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true));
// firstNode.setIsSelected(true);
}
onTreeLoad(){
console.log('tree');
}
feedData() {
const results = Object.keys(this.data.info).map(k => ({
name: k,
children: this.data.info[k].properties
? Object.keys(this.data.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
feedAnother() {
const results = Object.keys(this.dataa.info).map(k => ({
name: k,
children: this.dataa.info[k].properties
? Object.keys(this.dataa.info[k].properties).map(kk => ({ name: kk }))
: []
}));
this.nodes = results;
}
onActivate(event) {
this.selectedDataList.push(event.node.data);
console.log(this.selectedDataList)
}
onDeactivate(event) {
const index = this.selectedDataList.indexOf(event.node.data);
this.selectedDataList.splice(index, 1);
console.log(this.selectedDataList)
}
below is my stackblitz https://stackblitz.com/edit/angular-hrbppy
Use updatedata and initialized event to update the tree view to check all checkboxes.
app.component.html
<tree-root #tree *ngIf ="nodes" [nodes]="nodes" [options]="options" [focused]="true"
(initialized)="onTreeLoad()"
(updateData)="updateData()"
(select)="onActivate($event)"
(deselect)="onDeactivate($event)">
</tree-root>
It'll initiate tree-root component only if nodes variable is available,
then in the initialized and updateData event call selectAllNodes method to select all checkboxes.
app.component.ts
updateData() {
this.selectAllNodes();
}
onTreeLoad(){
this.selectAllNodes();
}
Refer to this slackblitz for working example.
just, in your function feed data call to your function this.selectAllNodes() enclosed in a setTimeout. You can see your forked stackblitz
setTimeout(()=>{
this.selectAllNodes()
})
NOTE: I see in your code you try to control in diferents ways the items selected. I simplified using a recursive function.
In this.treeComp.treeModel.selectedLeafNodeIds we have the items that are changed, so
getAllChecked()
{
const itemsChecked=this.getData(
this.treeComp.treeModel.selectedLeafNodeIds,null)
console.log(itemsChecked);
}
getData(nodesChanged,nodes) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
//in nodesChanged we has object like {1200002:true,123132321:false...}
if (nodesChanged[node.id]) //can be not changed, and then it's null because
//it's not in object or can be changed to false
data.push({id:node.id,name:node.name})
//or data.push(node.name); //if only need the "name"
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children)]
}
);
return data
}
Updated I updated the function getData to include the "parent" of the node, but looking the code of #Raghul selvam, his function like me more than mine.
getData(nodesChanged,nodes,prefix) {
nodes=nodes||this.treeComp.treeModel.nodes
let data: any[] = []
nodes.forEach((node: any) => {
if (nodesChanged[node.id])
data.push(prefix?prefix+"."+node.name:node.name)
if (node.children)
data=[...data,...this.getData(nodesChanged,node.children,prefix?prefix+"."+node.name:node.name)]
}
);
return data
}
And call it as
this.getData(this.treeComp.treeModel.selectedLeafNodeIds,null,"")
You could add this in your onTreeLoad function. You could add a boolean flag(treeLoaded) for tracking if the tree has loaded or not.
onTreeLoad(tree){
this.selectAllNodes();
this.treeLoaded = true;
}
I am working in Angular and I have a following situation:
my.service.ts has this class:
export class MyClass {
MyList: string[] = [];
MyString: string = '';
createString(): void {
this.MyList.forEach(s => {
this.MyString += s + ', ';
});
}
}
And my.component.ts calls it like this:
myData: MyClass[] = [];
this.myService.getMyData().subscribe(res => {
myData = res;
if (myData.length > 0) {
this.myData.forEach(x => x.createString());
}
});
VS Code recognizes the createString function as a metod of MyClass, but I still get an error:
ERROR TypeError: x.createString is not a function
Any explanations?
EDIT: The data comes from back end, and the back end model doesn't have this method. Maybe that is the issue?
The object coming from the server will be just a simple object not an instance of the class MyClass. You can create instances of MyClass and assign the values from the server object to the instance of the class:
this.myService.getMyData().subscribe(res => {
myData = res.map(o => Object.assign(new MyClass(), o));
if (myData.length > 0) {
this.myData.forEach(x => x.createString());
}
});
Accepted soulution didn't help me, so I propose mine. It does not require .map().
My http service:
getOffers() {
return this.http.get('https://ohipo.pl/assets/oferty.json');
}
Consuming service in component:
offers: Offer[] = [];
this.offersService.getOffers().subscribe((response: Offer[]) => {
for (let i in response) {
this.offers[i] = Object.assign(new Offer(), response[i]);
}
import { Component, Input, Output, OnInit, OnChanges } from '#angular/core';
import { ViewComponent } from '../view/view.component';
import { HitoService } from '../../services/hito.service';
#Component({
selector: 'app-time-line',
templateUrl: './time-line.component.html',
styleUrls: ['./time-line.component.css'],
providers: [HitoService]
})
export class TimeLineComponent implements OnChanges, OnInit {
#Input() calbuscador: String;
#Input() idcalbuscador: String;
public pepe: String
nom_cal1: any;
hito1: any = {};
hito2: any = {};
constructor(public _hitoService: HitoService) {
}
ngOnInit() {
}
///we retrieve the value sento to this component in OnChanges
ngOnChanges() {
this.nom_cal1 = this.calbuscador;
this.drawtimeline(this.nom_cal1, this._hitoService, this.idcalbuscador);
}
result: any[] = [];
drawtimeline(nom_cal, _hitoService, idcalbuscador) {
var container = document.getElementById('timeLine');
//alert("id cal sele es :" + idcalbuscador);
var k = 0;
var j = 0;
var master = new vis.DataSet();
var items = new vis.DataSet();
this.result.push({ "_id": this.idcalbuscador,
"title": nom_cal });
this.result.forEach(function (ev) {
master.add([{ id: ev._id, content: ev.title, cal_id: ev._id }]);
var g = ev._id;
for (var i= 0; i<this.result.length; i++){
console.log("hola");
console.log(this.result[i]._id);
this._hitoService.getHitos(this.result[i]._id)
.subscribe(hito2 => {
this.hito2 = hito2
var items: any;
items = hito2;
items.forEach(function (item) {
items.add([{ id: k, group: g, start: item.start_datetime, end: item.end_datetime, style: itemStyle(item.design), className: "pepe" }]);
k++;
});
j++;
});
}
});
I am trying to implement a timeline using the vis.js, I retrieve the name and id of the timeline want in this class component then in the ngOnChanges I call the function to draw the timeline passing to it the name of the timeline, it's id and the services in other to get the observables item of the this specific timeline. I have an array that will store the timelines (result) I want to view and then an observable I subscribed to, to add the items of the timelines. The first foreach() will remove the first element in the result array, get the observables of items for that result and the second foreach() will go through the observables items and print the items, then it start over and move to the next. But all I get in the browsers console is : TypeError: this is undefined. Probably not making use of the service in the forach()
You can use an arrow function inside your forEach to keep using the enclosing scope.
this.result.forEach((ev) => {
// your code here
}
Assign current this to another variable (Say, that) then, use that into your callback.
Note: Inside your callback function this is changed to the JavaScript context by which the function is called.
const that = this; // save the current 'this' to 'that' variable
this.result.forEach(function (ev) {
master.add([{ id: ev._id, content: ev.title, cal_id: ev._id }]);
var g = ev._id;
for (var i= 0; i< that.result.length; i++){
console.log("hola");
console.log(that.result[i]._id);
that._hitoService.getHitos(that.result[i]._id)
.subscribe(hito2 => {
that.hito2 = hito2
var items: any;
items = hito2;
....
....
....