Javascript push function - javascript

I want to create simple social media app.I'am working now on part with groups.But I cant filter only groups where some user is member.The code is following
import { Component, OnInit, OnChanges } from '#angular/core';
import { AngularFireDatabase } from '#angular/fire/database';
import { GroupsService } from '../groups.service';
#Component({
selector: 'app-groups',
templateUrl: './groups.component.html',
styleUrls: ['./groups.component.scss']
})
export class GroupsComponent implements OnInit {
uid = localStorage.getItem('uid')
groups: Array<any>;
mygroups: Array<any>;
sgroups;
constructor(private db: AngularFireDatabase, private _groups: GroupsService) {
}
ngOnInit() {
this._groups.getGroups().subscribe((data) => {
this.groups = data;
})
this.loadGroups()
}
search(e) {
this.sgroups = this.groups.find(gr => gr.name.toLowerCase().indexOf(e.target.value.toLowerCase()) > -1)
}
loadGroups() {
this.groups.map(gr => {
this._groups.getGroupMembers(gr.id).subscribe((data: any) => {
data.map(mem => {
if(mem.uid == this.uid) {
this.mygroups.push(gr); //here is the problem
}
})
})
})
}
scrollnav() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
}
Every help is welcomed.
Thanks a lot!

Problem is in initialization. intialize mygroups like
mygroups: any[] = [];
instead of
mygroups: Array<any>;

You can use forkJoin to fire all of the calls at once and get an array with all the results:
import {forkJoin} from 'rxjs';
...
const requests = this.groups.map(gr => this._groups.getGroupMembers(gr.id));
forkJoin(requests).subscribe((res) => this.mygroups = res);

Related

Changes in one Array is affecting other Array in Angular project

I am creating a angular project where i am getting API data which i am displaying to user. It has two components "Navigation" and "NewsBlock" and a service called "newsFetchService". API data is fetched by newsfetch service and used by both components.
NewsFetchService
import { Injectable } from '#angular/core';
import { Observable, of } from 'rxjs';
import { NewsData } from './NewsData';
import { HttpClient,HttpHeaders } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class NewsfetchserviceService {
//Global variable storing All fetch data
newsBlockData : NewsData[] = [];
allNews : NewsData[] = [];
constructor(
private http:HttpClient
) { }
private newsFetchURL = 'api/v1/topics';
getNews() {
return new Promise((resolve, reject) => {
this.http.get<NewsData[]>(this.newsFetchURL).subscribe(res => {
this.allNews = res;
this.newsBlockData = res;
resolve(true);
})
})
}
updateNewsBlock(selectedNews : NewsData){
this.newsBlockData.length = 0;
this.newsBlockData.push(selectedNews);
}
}
navigation.component.ts
import { Component, OnInit } from '#angular/core';
import { BreakpointObserver, Breakpoints } from '#angular/cdk/layout';
import { Observable } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { NewsfetchserviceService } from '../newsfetchservice.service';
import { NewsData } from '../NewsData';
#Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit{
sourcesList : NewsData[] = [];
ngOnInit(): void {
this.showAllSources();
}
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches),
shareReplay()
);
constructor(private breakpointObserver: BreakpointObserver,private newsfetch: NewsfetchserviceService) {}
showAllSources():void {
this.sourcesList = this.newsfetch.allNews;
/* this.newsfetch.getNews().subscribe(news => this.news = news); */
}
updateNewsList(source : NewsData):void{
console.log('option selected');
console.log(source);
this.newsfetch.updateNewsBlock(source);
}
}
newsBlock.component.ts
import { Component, OnInit } from '#angular/core';
import { NewsData } from '../NewsData';
import { NewsfetchserviceService } from '../newsfetchservice.service';
#Component({
selector: 'app-newsblock',
templateUrl: './newsblock.component.html',
styleUrls: ['./newsblock.component.css']
})
export class NewsblockComponent implements OnInit {
constructor(private newsfetch: NewsfetchserviceService) { }
newsBlockData : NewsData[] = [];
ngOnInit(): void {
this.getNews();
}
getNews():void {
this.newsBlockData = this.newsfetch.newsBlockData;
/* this.newsfetch.getNews().subscribe(news => this.news = news); */
}
}
Now, when user click a field in Navigation component it updates the newsBlockData array in Newsfetchservice. This newsBlockData is used by "newsBlock" component which is correctly updating data based on data changed.
Issue i am facing is that updating data within newsBlockData array is also affecting allnews array Data. Any data added or removed from newsBlockData array is also reflected in allnews array even though they are two separate arrays.
I have tried changing approach like trying to use subscriber and promise but getting the same issue. Also tried deep copying and shallow copying but getting the same result
The problem is in the getNews() method. Even though you initialize both properties to point to a separate array:
newsBlockData : NewsData[] = [];
allNews : NewsData[] = [];
In your getNews() method, you point them to the same array:
getNews() {
return new Promise((resolve, reject) => {
this.http.get<NewsData[]>(this.newsFetchURL).subscribe(res => {
this.allNews = res;
this.newsBlockData = res;
resolve(true);
})
})
}
A shallow copy in this case should be sufficient:
getNews() {
return new Promise((resolve, reject) => {
this.http.get<NewsData[]>(this.newsFetchURL).subscribe(res => {
this.allNews = [...res];
this.newsBlockData = [...res];
resolve(true);
})
})
}

Retrieving the next item in an array by index in Angular 11

I have a list of employees in a JSON object in my service.ts file. It will be moved to a DB eventually. I know this is not the place for it. It's just for dev purposes. I am able to retrieve the employees by clicking on it and displaying them in a dynamic view component.
However, I want to be able to see the next employee on a button click without having to always go back to the main list. I am able to retrieve the index and increment the index on click. I just can't get the route to display the data for that index.
The HTML is pretty basic a simple button with a click method. The Service file looks like this:
import {HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Employee } from '../models/employee.models';
#Injectable({
providedIn: 'root'
})
export class EmployeesService {
private listEmployees: Employee[] = [...regular json object...];
constructor() { }
getEmployees(): Employee[] {
return this.listEmployees;
}
getEmployee(id: number): Employee {
return this.listEmployees.find(e => e.id === id);
}
}
And my employee.component.ts file
import { ActivatedRoute } from '#angular/router';
import { EmployeesService } from './../../../services/employees.service';
import { Component, OnInit } from '#angular/core';
import { Employee } from 'src/app/models/employee.models';
import { Router } from '#angular/router';
#Component({
selector: 'employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.scss']
})
export class EmployeeComponent implements OnInit {
employee: Employee;
employees: Employee[];
index: number;
private _id: number;
constructor(
private _route: ActivatedRoute,
private _router: Router,
private _employeeService: EmployeesService
) { }
ngOnInit() {
this.employees = this._employeeService.getEmployees();
for ( this.index = 1; this.index < this.employees.length + 1; this.index++ ) {
this._route.paramMap.subscribe(params => {
this._id = +params.get('id')
console.log('this index:', this.index);
this.employee = this._employeeService.getEmployee(this._id);
});
}
}
viewNextEmployee() {
if( this.index < this.employees.length ){
this.index = this.index++;
console.log('this index:', this.index);
this._router.navigate(['/team', this._id])
} else {
this.index = 1;
}
}
}
I'm sorry, I can't really reproduce a working code. So many dependencies.

Cannot read property 'toLowerCase' of undefined in angular when filtering products

I am having this problem in component Type { data: Product[]; id: string; }[]' is not assignable to type 'Product[].
Type { data: Product[]; id: string; } is missing the following properties from type 'Product': title, price, category, imageUrl. When i am trying to assign the values returned from database to
this.filterdProduct = this.Product = products;
In Constructor...
Here is my code: Interface
export interface Product{
title:string;
price:number;
category:string;
imageUrl:string;
}
Service.Ts:
import { Product } from './../../new-products';
import { Observable } from 'rxjs';
import { AngularFireDatabase } from '#angular/fire/database';
import { Injectable } from '#angular/core';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private db: AngularFireDatabase){}
create(product){
return this.db.list('/products').push(product);
}
getAll() {
return this.db.list<Product[]>('/products').snapshotChanges()
.pipe(
map(a =>
a.map(
ac => {
const data= ac.payload.val();
const id = ac.key;
// console.log(data);
// console.log(id)
return {data,id}
} )
)
);
}
Component.ts:
import { Product } from './../../new-products';
import { ProductService } from './../Services/product.service';
import { AngularFireDatabase } from '#angular/fire/database';
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-admin-products',
templateUrl: './admin-products.component.html',
styleUrls: ['./admin-products.component.css']
})
export class AdminProductsComponent implements OnInit, OnDestroy {
Product:Product []= [];
filterdProduct:any = [];
subscription: Subscription;
constructor(private pd:ProductService){
this.subscription = this.pd.getAll().subscribe(products => {
this.filterdProduct = this.Product = products;
})
}
filter(query:string){
this.filterdProduct = (query) ?
this.Product.filter(p => p.title.toLowerCase().includes(query.toLowerCase())) : this.Product;
console.log(query)
}
ngOnInit(){
}
ngOnDestroy(){
this.subscription.unsubscribe();
}
}
The problem is in your getAll function. You are returning an object of type {data, id}. You should return data as your error signifies that you are expected to return an array of Product.
getAll service function return a object, it's not a Product[], but { Product[], id}
ac => {
const data= ac.payload.val();
const id = ac.key;
// console.log(data);
// console.log(id)
return {data,id} <-- Here
} )
It has to return a Product[].
Product:Product []= [];
in AdminProductsComponent field should be renamed to product and not Product as it clashes with interface name so it should be like this -
product: Array<Product> = [];
What is preventing you from using following code?
const data = ac.payload.val();
data.id = ac.key;
return data;

Angular4 routing tests

I works on an Angular project (v4), and I want test a ngOnInit of component. But, I have no idea to test a component with routing. In fact, I want found a label contains into 2 array of objects, due to the URL params. Thank you for your answers.
import { Component, OnInit } from '#angular/core';
import { MenuNavComponent } from '../../shared/folder/cpt.component';
import { Router } from '#angular/router';
#Component({
selector: 'cpt-small',
templateUrl: './cpt-small.component.html',
styleUrls: [
'./cpt.scss'
]
})
export class Cpt extends ParentCpt implements OnInit {
constructor( private router: Router ) {
super();
}
ngOnInit() {
this.router.events.subscribe(() => {
if (!this.router.url.startsWith('/home')) {
let routeURL = this.router.url.split('/')[1];
routeURL = `/${routeURL}`;
this.menu.forEach((menuCategory, index) => {
menuCategory.items.forEach((item) => {
if (routeURL === item.link) {
this.onDropDownClicked(index);
}
});
});
}
});
}
onDropDownClicked(index) {
this.menu.forEach((dropDown, i) => {
if (i !== index) {
dropDown['open'] = false;
} else {
dropDown['open'] = true;
}
});
}
}

Where to put service providers in angular 2 hierarchy so that components can talk to each other using the same instance of service?

Related question:
Observable do not receive the next value in angular2
No provider for service error in angular2, why do I need to inject it in it's parent component?
Using observable talk to other component in angular2, not receiving coming value
I have a PagesService that has a setCurrentPlaylists function, this function will be triggered from other component, it will receive an value of Playlists type, and will console log this value, using the next function pass to other component( I intent to).
My entire code for pages service is:
import { Injectable } from '#angular/core';
import { ApiService } from '../../apiService/api.service';
import { Platform } from '../../platforms/shared/platform.model';
import { Page } from './page.model';
import { Playlists } from '../shared/playlists.model';
import { Subject, BehaviorSubject } from 'rxjs/Rx';
#Injectable()
export class PagesService {
private currentPlaylists: Subject<Playlists> = new BehaviorSubject<Playlists>(new Playlists());
constructor(private service: ApiService) {
this.currentPlaylists.subscribe((v) => console.log(v, 'subscriber from pages service is printing out the incoming value'));
}
getPages(platform: Platform) {
return this.service.getPages(platform.value);
}
setCurrentPage(page: Page) {
this.service.setCurrentPage(page.pageId);
}
getCurrentPage():string {
return this.service.getCurrentPage();
}
getCurrentPlaylists() {
return this.currentPlaylists;
}
setCurrentPlaylists(playlists: Playlists) {
console.log("Pages Service receive an value of playlists:", playlists);
this.currentPlaylists.next(playlists);
}
}
My code for page component is:
import { Component, OnInit, Input, Output, OnChanges, EventEmitter, Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Platform } from '../platforms/shared/platform.model';
import { Page } from './shared/page.model';
import { Playlists } from './shared/playlists.model';
import { PagesService } from './shared/pages.service';
import { PlaylistService } from '../playlist/shared/playlist.service';
import { Subject,BehaviorSubject } from 'rxjs/Rx';
#Component({
selector: 'pages',
styleUrls: ['app/pages/pages.css'],
templateUrl: 'app/pages/pages.html',
providers: [PagesService, PlaylistService]
})
export class PagesComponent {
#Input() platform: Platform;
#Output() onPlaylistsChange: EventEmitter<Playlists>;
currentPageName: string;
currentPage: Page;
pages: Array<Page>;
playlists: Playlists;
constructor(private pageServer: PagesService, private playlistService: PlaylistService) {
this.pages = [];
this.currentPage = new Page();
this.pageServer.setCurrentPage(this.currentPage);
this.playlists = new Playlists();
this.onPlaylistsChange = new EventEmitter<Playlists>();
}
ngOnInit() {
this.pageServer.getCurrentPlaylists().subscribe((playlists) => {
console.log('subscriber in pages component is printing out the incoming value', playlists);
this.playlists = playlists;
}, error => {
console.log(error);
});
}
getPages(platform: Platform): void {
this.pageServer.getPages(platform)
.subscribe(
res => {
if (res.pages.length > 0) {
this.pages = [];
for (let page of res.pages) {
if (page.pageName !== "Shows" && page.pageName !== "All Shows" && page.pageName !== "Moives" && page.pageName !== "All Movies") {
this.pages.push(page);
}
}
this.currentPage = this.pages[0];
this.pageServer.setCurrentPage(this.currentPage);
this.currentPageName = this.pages[0].pageName;
this.getPlaylist(this.currentPage, this.platform);
} else {
this.pages = [];
this.currentPage = new Page();
this.pageServer.setCurrentPage(this.currentPage);
this.playlists = new Playlists();
this.onPlaylistsChange.emit(this.playlists);
}
},
error => console.log(error)
);
}
getPlaylist(page: Page, platform: Platform): void {
this.currentPage = page;
this.pageServer.setCurrentPage(this.currentPage);
this.playlistService.getPlaylist(page, platform)
.subscribe(
res => {
if (res.hasOwnProperty('pages') && res.pages.length > 0) {
if (res.pages[0].hasOwnProperty('bodyPlaylists') && res.pages[0].hasOwnProperty('headerPlaylists')) {
this.playlists.bodyPlaylists = res.pages[0].bodyPlaylists || [];
this.playlists.headerPlaylists = res.pages[0].headerPlaylists || [];
} else {
this.playlists.bodyPlaylists = [];
this.playlists.headerPlaylists = [];
this.playlists.wholePlaylists = res.pages[0].playlists || [];
}
this.onPlaylistsChange.emit(this.playlists);
} else {
this.playlists = new Playlists();
this.onPlaylistsChange.emit(this.playlists);
}
},
error => console.error(error)
);
}
ngOnChanges() {
// Get all Pages when the platform is set actual value;
if (this.platform.hasOwnProperty('value')) {
this.getPages(this.platform);
}
}
}
When I trigger the setCurrentPlaylists function, the playlists didn't passed to pages component. I need to use that passed value to update pages component's playlists.
This is the console output after I trigger the setCurrentPlaylsts function. No message from pages components.
Any suggestions are appreciated!
I call setCurrentPlaylists function from this component
/// <reference path="../../../typings/moment/moment.d.ts" />
import moment from 'moment';
import { Component, ViewChild, ElementRef, Input, Output, EventEmitter } from '#angular/core';
import { CORE_DIRECTIVES } from '#angular/common';
import { Http, Response } from '#angular/http';
import { MODAL_DIRECTVES, BS_VIEW_PROVIDERS } from 'ng2-bootstrap/ng2-bootstrap';
import {
FORM_DIRECTIVES,
REACTIVE_FORM_DIRECTIVES,
FormBuilder,
FormGroup,
FormControl,
Validators
} from '#angular/forms';
import { PagesService } from '../../pages/shared/pages.service';
import { ApiService } from '../../apiService/api.service';
#Component({
selector: 'assign-playlist-modal',
providers: [PagesService],
exportAs: 'assignModal',
directives: [MODAL_DIRECTVES, CORE_DIRECTIVES, FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES ],
viewProviders: [BS_VIEW_PROVIDERS],
styleUrls: ['app/channel/shared/assignPlaylist.css'],
templateUrl: 'app/channel/modals/assignPlaylistModal.html'
})
export class AssignPlaylistModalComponent {
#ViewChild('assignPlaylistModal') modal: any;
private addPlaylistForm: FormGroup;
private playlistType: string;
private currentPage: string;
private editDate: string;
constructor(private apiService: ApiService, private pagesService: PagesService, fb: FormBuilder) {
this.currentPage = '';
this.editDate = this.apiService.getDate();
this.addPlaylistForm = fb.group({
'longPlaylistName': ['', Validators.required],
'shortPlaylistName': ['', Validators.required],
'startOn': ['', Validators.compose([
Validators.required, this.validTimeFormat
])],
'expireOn': ['', Validators.compose([
Validators.required, this.validTimeFormat
])],
'isExpire': ['']
});
this.addPlaylistForm.controls['startOn'].valueChanges.subscribe((value: string) => {
if (moment(value, 'YYYY-MM-DDThh:mm').isValid()) {
if (this.playlistType == 'dynamic') {
this.apiService.setGlobalStartTime(moment(value).format("YYYYMMDDHHmm"));
}
}
});
this.addPlaylistForm.controls['expireOn'].valueChanges.subscribe((value: string) => {
if (moment(value, 'YYYY-MM-DDThh:mm').isValid()) {
if (this.playlistType == 'dynamic') {
this.apiService.setGlobalEndTime(moment(value).format("YYYYMMDDHHmm"));
}
}
});
}
showModal(type: string) {
this.playlistType = type;
this.currentPage = this.apiService.getCurrentPage();
this.modal.show();
}
validTimeFormat(control: FormControl): { [s: string]: boolean} {
if (!moment(control.value, 'YYYY-MM-DDThh:mm').isValid()) {
return { invalidTime: true};
}
}
setCloseStyle() {
let styles = {
'color': 'white',
'opacity': 1
}
return styles;
}
createNewPlaylist(stDate: string, etDate: string, playlistTitle: string, shortTitle: string, callback?: any):any {
this.apiService.createNewPlaylist(stDate, etDate, playlistTitle, shortTitle)
.subscribe(
data => {
let playlistId = data[0].id;
this.apiService.addPlaylistToPage(playlistId, stDate, etDate, this.apiService.getGlobalRegion(), callback)
.subscribe(
data => {
if (this.apiService.g_platform == 'DESKTOP') {
this.apiService.getPlaylist(this.apiService.getCurrentPage(), 'true' )
.subscribe(
res => {
if (res.hasOwnProperty('pages') && res.pages.length > 0) {
if (res.pages[0].hasOwnProperty('bodyPlaylists') && res.pages[0].hasOwnProperty('headerPlaylists')) {
this.apiService.getCurrentPlaylists().bodyPlaylists = res.pages[0].bodyPlaylists || [];
this.apiService.getCurrentPlaylists().headerPlaylists = res.pages[0].headerPlaylists || [];
console.log('assign playlist component is calling the pages service setCurrentPlaylists function.');
this.pagesService.setCurrentPlaylists(this.apiService.getCurrentPlaylists());
} else {
this.apiService.getCurrentPlaylists().bodyPlaylists = [];
this.apiService.getCurrentPlaylists().headerPlaylists = [];
this.apiService.getCurrentPlaylists().wholePlaylists = res.pages[0].playlists || [];
console.log('assign playlist component is calling the pages service setCurrentPlaylists function.');
this.pagesService.setCurrentPlaylists(this.apiService.getCurrentPlaylists());
}
}
}
);
} else {
this.apiService.getPlaylist(this.apiService.getCurrentPage(), 'false' )
.subscribe(
res => {
if (res.hasOwnProperty('pages') && res.pages.length > 0) {
this.apiService.getCurrentPlaylists().bodyPlaylists = [];
this.apiService.getCurrentPlaylists().headerPlaylists = [];
this.apiService.getCurrentPlaylists().wholePlaylists = res.pages[0].playlists || [];
console.log('assign playlist component is calling the pages service setCurrentPlaylists function.');
this.pagesService.setCurrentPlaylists(this.apiService.getCurrentPlaylists());
}
}
);
}
}
);
},
error => console.log(error)
);
}
onSubmit(form: FormGroup) {
// get start time, the format from input will be like 2016-06-07T00:05
let startTime = moment(form.value.startOn).format("YYYYMMDDHHmm");
let expireTime = moment(form.value.expireOn).format("YYYYMMDDHHmm");
let playlistTitle = form.value.longPlaylistName;
let shortTitle = form.value.shortPlaylistName;
if (this.playlistType == 'smart' || this.playlistType == 'new') {
this.createNewPlaylist(startTime, expireTime, playlistTitle, shortTitle);
}
}
}
This is my component tree:
I am assuming your components tree is as follow:
AssignPlaylistModalComponent (Parent or higher level than PagesComponent in the tree)
PagesComponent (lowest level child as it does not import any directive)
Issue
You should only put your service in the top level (parent) components provider. Though all components still need to do the import and constructor.
Putting the service in a component's provider will create a new copy of the service and share along the component tree downward, not upward.
In the code in question, PagesComponent, as the lowest level child in the tree, with its own provider line, is actually initiating its own copy of PagesService, PlaylistService. So each instance of PagesComponent is basically listening to itself only. It won't receive any messages from others.
Fix
#Component({
selector: 'pages',
styleUrls: ['app/pages/pages.css'],
templateUrl: 'app/pages/pages.html',
providers: [PagesService, PlaylistService] // <--- Delete this line
})
export class PagesComponent {
#Input() platform: Platform;
#Output() onPlaylistsChange: EventEmitter<Playlists>;
Where to put providers
Assume following component tree:
Component A1 (root component)
Component B1
Component C1
Component C2
Component B2
Component C3
Component C4
The easiest way is to put it in A1 providers, all components will be sharing the same service instance, and able to message each other.
If you put it in B1 providers, then only B1, C1 and C2 can talk to each other.
Base on lastest update, the root component of the project is AppComponent.ts. providers should be added in it.
From the code you provided, I cannot see when this method
setCurrentPlaylists(playlists: Playlists) {
console.log(playlists, 'i am here');
this.currentPlaylists.next(playlists);
}
is called. Therefore, your list is empty.
Doing this
this.pageServer.getCurrentPlaylists().subscribe((playlists) => {
console.log(playlists, 'new playlists coming');
this.playlists = playlists;
}, error => {
console.log(error);
});
only creates a subscription to the observable. You need to publish data from somewhere.
In addition, it'd better to move this code
this.pageServer.getCurrentPlaylists().subscribe((playlists) => {
console.log(playlists, 'new playlists coming');
this.playlists = playlists;
}, error => {
console.log(error);
});
to ngOnInit()

Categories

Resources