Cannot read property 'id' of undefined while looping through two arrays : Angular - arrays

I think that I might be missing something small or I have not properly implemented my angular code. Nevertheless , the error I am currently receiving is:
TypeError: Cannot read property 'id' of undefined
I am attempting to call a function within my newsfeed.component.html (template) which calls a function that takes an id and loops thrugh two arrays, defined at the top of my newsfeed.component.ts (component)(See Below)
newsfeed.component.ts:
import {
Component,
OnInit,
Input,
OnChanges,
SimpleChanges
} from "#angular/core";
//import { FeedService } from "src/app/core/news-feed";
import { FeedService } from "../../../core/news-feed/_services/feed.service.fake";
import { CustomersService } from "../../../core/e-commerce";
import { MockFeedTable } from "../../../core/news-feed/_server/feed.mock-table";
import moment from "moment";
import { Observable } from "rxjs";
import { SearchPipe } from "../../partials/_pipes/search.pipe";
import { FeedTagsService } from "../../../../../src/app/core/news-feed/_services/feed-tags.service";
import { ActivatedRoute } from "#angular/router";
export interface MOCK_UserData {
id: number;
userName: string;
profileImagePath: string;
}
export interface MOCK_NewsPost {
id: number;
bookmarkCount: number;
commentCount: number;
timeLastUpdated: string;
}
#Component({
selector: "kt-news-feed",
templateUrl: "./news-feed.component.html",
styleUrls: ["./news-feed.component.scss"]
})
export class NewsFeedComponent implements OnInit {
filterQuery: any;
componentDataLoaded: boolean;
constructor(
private feedService: FeedService,
private feedTagsService: FeedTagsService,
private route: ActivatedRoute
) {}
NEWS_DATA: any;
NEWS_TAG_DATA: any;
currentNewsQuery: any;
ngOnInit() {
this.componentDataLoaded = false;
this.route.data.subscribe(res => {
this.NEWS_DATA = res.newsfeed;
console.log(res.newsfeed);
this.NEWS_TAG_DATA = res.newsfeedtags;
console.log(res.newsfeedtags);
});
}
waitForComponentData() {
console.log("Entered waitForComponentData");
console.log(" componentDataLoaded is currently : ");
console.log(this.componentDataLoaded);
this.componentDataLoaded = true;
console.log(" componentDataLoaded is now : ");
console.log(this.componentDataLoaded);
}
filterNewsPosts() {
return this.feedService.retrieveSearchBarQuery();
}
getUserProfilePictureFromUserId(userId) {
for (var i = 0, len = this.MOCK_UserDataTable.length; i < len; i++) {
if (this.MOCK_UserDataTable[i].id == userId) {
return this.MOCK_UserDataTable[i].profileImagePath;
}
}
}
getUserNameFromUserId(userId) {
for (var i = 0, len = this.MOCK_UserDataTable.length; i < len; i++) {
if (this.MOCK_UserDataTable[i].id == userId) {
return this.MOCK_UserDataTable[i].userName;
}
}
}
getNewsPostElapsedTime(startDate) {
return moment(startDate).fromNow();
}
getFilterTagIdsFromNewsPost(postId: any) {
console.log(
"getFilterTagIdsFromNewsPost : postId from parameter is: " + postId
);
for (var i = 0; i <= this.NEWS_DATA.length; i++) {
if (postId == this.NEWS_DATA[i].id) {
console.log(
"getFilterTagIdsFromNewsPost : returning the following tags: " +
this.NEWS_DATA[i].postTags
);
return this.NEWS_DATA[i].postTags;
}
}
}
getFilterTagsFromNewsPost(postId: any) {
var tagIdArray = this.getFilterTagIdsFromNewsPost(postId);
let newsDataArray = this.NEWS_DATA;
let newsTagDataArray = this.NEWS_TAG_DATA;
var resultTagArray = [];
console.log("tagIdArray for Post with id of " + postId + " is :");
console.log(tagIdArray);
console.log("Looping through first loop...");
for (var i = 0; i <= tagIdArray.length; i++) {
console.log("Looping through second loop...");
for (var j = 0; j <= newsTagDataArray.length; j++) {
if (tagIdArray[i] == newsTagDataArray[j].id) {
resultTagArray.push(newsTagDataArray[j]);
}
}
}
console.log("Final Array for this post is: ");
console.log(resultTagArray);
return resultTagArray;
}
}
newsfeed.component.html:
<!-- Left col -->
<!-- <div class="row" *ngIf="!componentDataLoaded">
<div class="col-sm">
<div
class="kt-spinner kt-spinner--v2 kt-spinner--lg kt-spinner--info"
></div>
</div>
</div> -->
<div class="row">
<div class="col-lg-6">
<ng-container
*ngFor="
let newsData of NEWS_DATA | NewsStringFilter: filterNewsPosts()
"
>
<!-- CARD START -->
<div *ngIf="!newsData.oddNewsPostInDatabase" class="kt-portlet">
<div class="kt-portlet__head" style="margin-top: 15px;">
<div class="kt-portlet__head-label">
<span class="kt-portlet__head-icon">
<a
href="#"
class="kt-media kt-media--lg kt-media--circle"
>
<img
src="{{
getUserProfilePictureFromUserId(
newsData._userId
)
}}"
alt="profile-image"
/>
</a>
</span>
<h3 class="kt-portlet__head-title">
{{ getUserNameFromUserId(newsData._userId) }}
</h3>
</div>
<div class="kt-portlet__head-toolbar">
{{ getNewsPostElapsedTime(newsData._createdDate) }}
</div>
</div>
<div class="kt-portlet__body">
<h4>
{{ newsData.postHeader }}
</h4>
{{ newsData.postBody }}
</div>
<div class="kt-portlet__foot">
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-actions">
<ng-container
*ngFor="
let newsTag of getFilterTagsFromNewsPost(
newsData.id
)
"
>
<td class="card-tag-container">
<span
class="kt-badge kt-badge--inline cat-tab"
[style.background-color]="
newsTag.tagColour
"
style="color: #ffffff"
>{{ newsTag.tagTitle }}</span
>
</td>
</ng-container>
<a
href="#"
class="btn btn-outline-brand btn-sm btn-icon btn-icon-md"
>
<i
class="flaticon2-tag
"
></i>
</a>
<a
href="#"
class="btn btn-outline-brand btn-sm btn-icon btn-icon-md"
>
<i class="flaticon-comment"></i>
</a>
</div>
</div>
</div>
</div>
<!-- CARD END -->
<!-- CARD END -->
</ng-container>
</div>
<!-- Right col -->
<div class="col-lg-6">
<ng-container
*ngFor="
let newsData of NEWS_DATA | NewsStringFilter: filterNewsPosts()
"
>
<!-- CARD START -->
<div *ngIf="newsData.oddNewsPostInDatabase" class="kt-portlet">
<div class="kt-portlet__head" style="margin-top: 15px;">
<div class="kt-portlet__head-label">
<span class="kt-portlet__head-icon">
<a
href="#"
class="kt-media kt-media--lg kt-media--circle"
>
<img
src="{{
getUserProfilePictureFromUserId(
newsData._userId
)
}}"
alt="profile-image"
/>
</a>
</span>
<h3 class="kt-portlet__head-title">
{{ getUserNameFromUserId(newsData._userId) }}
</h3>
</div>
<div class="kt-portlet__head-toolbar">
{{ getNewsPostElapsedTime(newsData._createdDate) }}
</div>
</div>
<div class="kt-portlet__body">
<h4>
{{ newsData.postHeader }}
</h4>
{{ newsData.postBody }}
</div>
<div class="kt-portlet__foot">
<div class="kt-portlet__head-toolbar card-footer-toolbar">
<div class="kt-portlet__head-actions">
<ng-container
*ngFor="
let newsTag of getFilterTagsFromNewsPost(
newsData.id
)
"
>
<td class="card-tag-container">
<span
class="kt-badge kt-badge--inline cat-tab"
[style.background-color]="
newsTag.tagColour
"
style="color: #ffffff"
>{{ newsTag.tagTitle }}</span
>
</td>
</ng-container>
<a
href="#"
class="btn btn-outline-brand btn-sm btn-icon btn-icon-md"
>
<i
class="flaticon2-tag
"
></i>
</a>
<a
href="#"
class="btn btn-outline-brand btn-sm btn-icon btn-icon-md"
>
<i class="flaticon-comment"></i>
</a>
</div>
</div>
</div>
</div>
</ng-container>
<!-- CARD END -->
</div>
</div>
Originally I thought that the view executed the function in question:
(getFilterTagsFromNewsPost(newsData.id))
before the data was loaded by the observable populated variables (NEWS_DATA and NEWS_TAG_DATA) into the component and therefore throwing 'undefined'.
So I tried implementing resolvers into the mix in hope that this would be the issue , turns out I still have not got it right. Code for Resolvers and the Services are below:
feed.service.fake.ts
// Angular
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
// RxJS
import { Observable, forkJoin, of } from "rxjs";
import { mergeMap, delay } from "rxjs/operators";
// Lodash
import { each } from "lodash";
// CRUD
import {
HttpUtilsService,
QueryParamsModel,
QueryResultsModel
} from "../../_base/crud";
// Models
import { NewsPostModel } from "../_models/news-post.model";
const API_NEWS_POST_URL = "api/newsfeed";
// Fake REST API (Mock)
// This code emulates server calls
#Injectable()
export class FeedService {
NEWS_FEED_ITEMS_STORAGE: any;
public currentSearchBarQuery: any;
constructor(
private http: HttpClient,
private httpUtils: HttpUtilsService
) {}
// CREATE => POST: add a new newsPost to the server
createNewsPost(newsPost: NewsPostModel): Observable<NewsPostModel> {
// Note: Add headers if needed (tokens/bearer)
const httpHeaders = this.httpUtils.getHTTPHeaders();
return this.http.post<NewsPostModel>(API_NEWS_POST_URL, newsPost, {
headers: httpHeaders
});
}
// READ
getAllNewsPosts(): Observable<NewsPostModel[]> {
return this.http.get<NewsPostModel[]>(API_NEWS_POST_URL);
}
getNewsPostById(newsPostId: number): Observable<NewsPostModel> {
return this.http.get<NewsPostModel>(
API_NEWS_POST_URL + `/${newsPostId}`
);
}
// Method from server should return QueryResultsModel(items: any[], totalsCount: number)
// items => filtered/sorted result
findNewsPosts(
queryParams: QueryParamsModel
): Observable<QueryResultsModel> {
// This code imitates server calls
const url = API_NEWS_POST_URL;
return this.http.get<NewsPostModel[]>(API_NEWS_POST_URL).pipe(
mergeMap(res => {
const result = this.httpUtils.baseFilter(res, queryParams, [
"status",
"type"
]);
return of(result);
})
);
}
// UPDATE => PUT: update the newsPost on the server
updateNewsPost(newsPost: NewsPostModel): Observable<any> {
const httpHeader = this.httpUtils.getHTTPHeaders();
return this.http.put(API_NEWS_POST_URL, newsPost, {
headers: httpHeader
});
}
// UPDATE Status
updateStatusForNewsPost(
newsPosts: NewsPostModel[],
status: number
): Observable<any> {
const tasks$ = [];
each(newsPosts, element => {
const _newsPost = Object.assign({}, element);
_newsPost.status = status;
tasks$.push(this.updateNewsPost(_newsPost));
});
return forkJoin(tasks$);
}
// DELETE => delete the newsPost from the server
deleteNewsPost(newsPostId: number): Observable<any> {
const url = `${API_NEWS_POST_URL}/${newsPostId}`;
return this.http.delete<NewsPostModel>(url);
}
deleteNewsPosts(ids: number[] = []): Observable<any> {
const tasks$ = [];
const length = ids.length;
// tslint:disable-next-line:prefer-const
for (let i = 0; i < length; i++) {
tasks$.push(this.deleteNewsPost(ids[i]));
}
return forkJoin(tasks$);
}
fetchSearchBarQuery(value: any): any {
this.currentSearchBarQuery = value;
}
retrieveSearchBarQuery(): any {
return this.currentSearchBarQuery;
}
}
feed-tags.service.ts
// Angular
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
// RxJS
import { Observable } from "rxjs";
// CRUD
import {
HttpUtilsService,
QueryParamsModel,
QueryResultsModel
} from "../../_base/crud";
// Models
import { NewsPostTagModel } from "../_models/news-post-tag.model";
const API_NEWS_POST_TAG_URL = "api/newsfeedtags";
#Injectable()
export class FeedTagsService {
NEWS_TAG_STORAGE: any;
SELECTED_NEWS_TAG_STORAGE: any;
constructor(
private http: HttpClient,
private httpUtils: HttpUtilsService
) {}
// CREATE => POST: add a new NewsPost to the server
createNewsPostTag(
newsPostTag: NewsPostTagModel
): Observable<NewsPostTagModel> {
// Note: Add headers if needed (tokens/bearer)
const httpHeaders = this.httpUtils.getHTTPHeaders();
return this.http.post<NewsPostTagModel>(
API_NEWS_POST_TAG_URL,
newsPostTag,
{
headers: httpHeaders
}
);
}
// READ
getAllNewsPostsTags(): Observable<NewsPostTagModel[]> {
return this.http.get<NewsPostTagModel[]>(API_NEWS_POST_TAG_URL);
}
getNewsPostTagsById(newsPostTagId: number): Observable<NewsPostTagModel> {
return this.http.get<NewsPostTagModel>(
API_NEWS_POST_TAG_URL + `/${newsPostTagId}`
);
}
// Method from server should return QueryResultsModel(items: any[], totalsCount: number)
// items => filtered/sorted result
// Server should return filtered/sorted result
findNewsPostsTags(
queryParams: QueryParamsModel
): Observable<QueryResultsModel> {
// Note: Add headers if needed (tokens/bearer)
const httpHeaders = this.httpUtils.getHTTPHeaders();
const httpParams = this.httpUtils.getFindHTTPParams(queryParams);
const url = API_NEWS_POST_TAG_URL + "/find";
return this.http.get<QueryResultsModel>(url, {
headers: httpHeaders,
params: httpParams
});
}
// UPDATE => PUT: update the NewsPost on the server
updateNewsPostTag(newsPostTag: NewsPostTagModel): Observable<any> {
const httpHeader = this.httpUtils.getHTTPHeaders();
return this.http.put(API_NEWS_POST_TAG_URL, newsPostTag, {
headers: httpHeader
});
}
// UPDATE Status
updateStatusForNewsPostTagModel(
newsPostsTags: NewsPostTagModel[],
status: number
): Observable<any> {
const httpHeaders = this.httpUtils.getHTTPHeaders();
const body = {
newsPostsForUpdate: newsPostsTags,
newStatus: status
};
const url = API_NEWS_POST_TAG_URL + "/updateStatus";
return this.http.put(url, body, { headers: httpHeaders });
}
// DELETE => delete the newsPost from the server
deleteNewsPostTag(newsPostTagId: number): Observable<NewsPostTagModel> {
const url = `${API_NEWS_POST_TAG_URL}/${newsPostTagId}`;
return this.http.delete<NewsPostTagModel>(url);
}
deleteNewsPostsTags(ids: number[] = []): Observable<any> {
const url = API_NEWS_POST_TAG_URL + "/deleteNewsPostsTags";
const httpHeaders = this.httpUtils.getHTTPHeaders();
const body = { newsPostIdsForDelete: ids };
return this.http.put<QueryResultsModel>(url, body, {
headers: httpHeaders
});
}
}
news-feed-tags.resolve.ts :
News Feed Tags resolve Image
news-feed.resolve.ts :
News feed resolve image
Here is the console with the various logs and errors:
Logs and Errors Image
As you can see , data does seem to be passing through with the individual values being outputted in the log ( the id value specifically) yet it seems to be throwing an error on a hidden element in the array perhaps? No idea at the moment.

for (var i = 0; i <= tagIdArray.length; i++) {
console.log("Looping through second loop...");
for (var j = 0; j <= newsTagDataArray.length; j++) {
if (tagIdArray[i] == newsTagDataArray[j].id) {
resultTagArray.push(newsTagDataArray[j]);
}
}
}
The above code looks wrong and requires the following changes
i <= tagIdArray.length → i < tagIdArray.length
j <= newsTagDataArray.length → j < newsTagDataArray.length
Notice how the following prints undefined because x[x.length] is always undefined .
x = [10, 11, 12];
for (var i = 0; i <= x.length; i++) {
console.log(x[i]);
}

Related

How do I remove commas in an array in Angular project

I am trying to call a list of actors from movies; in the DB I made, they all have commas at the end of each string. When the array is called, the content displays with 2 commas after each other and I am wondering how I can get rid of that. I have tried to use .join but I don't know how to implement it into the HTML (I am new at Angular).
Here is the HTML and .ts files:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { FetchApiDataService } from '../fetch-api-data.service'
import { MatDialog } from '#angular/material/dialog';
import { GenreComponent } from '../genre/genre.component';
import { DirectorComponent } from '../director/director.component';
#Component({
selector: 'app-movie-card',
templateUrl: './movie-card.component.html',
styleUrls: ['./movie-card.component.css']
})
export class MovieCardComponent implements OnInit {
movies: any[] = [];
actors: any[] = [];
constructor(
public dialog: MatDialog,
public fetchApiData: FetchApiDataService,
public router:Router,
) { }
ngOnInit(): void {
this.getMovies();
}
removeCommas(): void {
this.actors.join(' ');
}
getMovies(): void {
this.fetchApiData.getAllMovies().subscribe((response: any) => {
this.movies = response;
console.log(this.movies);
return this.movies;
});
}
openGenreDialog(genreName: string): void {
this.dialog.open(GenreComponent, {
width: '280px',
data: {
genreName: genreName
}
});
}
openDirectorDialog(directorName: string): void {
this.dialog.open(DirectorComponent, {
width: '280px',
data: {
directorName: directorName
}
});
}
}
<div style="display: flex;">
<mat-card *ngFor="let movie of movies;" style="flex: 1 1 auto;">
<mat-card-header>
<mat-card-title>{{movie.Title}}</mat-card-title>
<mat-card-subtitle>Starring: {{movie.Actors}}</mat-card-subtitle>
</mat-card-header>
<img src={{movie.ImagePath}} alt= {{movie.Title}} />
<mat-card-actions>
<button
mat-button
color="primary"
(click)="openGenreDialog(movie.Genre.Name)"
>
Genre
</button>
<button
mat-button
color="primary"
(click)="openDirectorDialog(movie.Director.Name)"
>
Director
</button>
<button
mat-button
color="primary"
>
Synopsis
</button>
<mat-icon>favorite_border</mat-icon>
</mat-card-actions>
</mat-card>
</div>
You can run the map pipe and replace method in your array.
getMovies(): void {
this.fetchApiData.getAllMovies().pipe(
map((actor) => actor.replace(',', ''))).
subscribe((response: any) => {
this.movies = response;
console.log(this.movies);
return this.movies;
});
}
First of all, I will advice you to not use 'any' everywhere. It removes type checking and that can lead to issues and bugs in future.
As the returned object will be an Observable of type any[] (or Movies[] if you create a movie object with a string property named actor), you can do something like this. It will return an array of actors. For replace function, you will have to use a regexp expression to select all the commas in the value -
getMovies() {
this.fetchApiData
.getAllMovies()
.subscribe((res: any[]) => {
this.movies = res;
this.actors = res.map((movie: any) => movie.actor.replace(/,/g, ''));
console.log(this.actors);
});
}

Ionic5 FavoriteService that doesn’t duplicate

I’ve been following tutorials to create an Ionic5 StarWars app and although it’s almost completed, have ran into trouble understanding how I can get ‘favorite’ star button on all tabs (ie) Films, People, Planets. I'm not very familiar with Ionic5 and trying to figure out how to add favorite buttons across all tabs. Have done a lot of research and spent time trying to get this to work.
So far there is only a function to ‘favorite’ films and not people or planets. When I try to replicate the code for Films to extend to people and planets, I can’t and get errors that duplication is not allowed.
Would really appreciate any help with this, as I want to get all - Films, People and Planets to be starred as favorites. Thanks for any help with this.
The code in favorite.service.ts is as follows:-
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
const STORAGE_KEY = 'favoriteFilms';
#Injectable({
providedIn: 'root'
})
export class FavoriteService {
constructor(private storage: Storage) {
}
getAllFavoriteFilms() {
return this.storage.get(STORAGE_KEY);
}
isFavorite(filmId) {
return this.getAllFavoriteFilms().then(result => {
return result && result.indexOf(filmId) !== -1;
});
}
favoriteFilm(filmId) {
return this.getAllFavoriteFilms().then(result => {
result = result || [];
result.push(filmId);
return this.storage.set(STORAGE_KEY, result);
});
}
unfavoriteFilm(filmId) {
return this.getAllFavoriteFilms().then(result => {
if (result) {
var index = result.indexOf(filmId);
result.splice(index, 1);
return this.storage.set(STORAGE_KEY, result);
}
})
}
}
This is exactly how I tried to import the service into the components:-
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
const STORAGE_KEY = 'favoriteFilms';
const STORAGE_KEY1 = 'favoritePlanets';
#Injectable({
providedIn: 'root'
})
export class FavoriteService {
constructor(private storage: Storage) {
}
getAllFavoriteFilms() {
return this.storage.get(STORAGE_KEY);
}
getAllFavoritePlanets() {
return this.storage.get(STORAGE_KEY1);
}
isFavorite(filmId) {
return this.getAllFavoriteFilms().then(result => {
return result && result.indexOf(filmId) !== -1;
});
}
isFavorite(planetId) {
return this.getAllFavoritePlanets().then(result => {
return result && result.indexOf(planetId) !== -1;
});
}
favoriteFilm(filmId) {
return this.getAllFavoriteFilms().then(result => {
result = result || [];
result.push(filmId);
return this.storage.set(STORAGE_KEY, result);
});
}
favoritePlanet(planetId) {
return this.getAllFavoriteFilms().then(result => {
result = result || [];
result.push(planetId);
return this.storage.set(STORAGE_KEY1, result);
});
}
unfavoriteFilm(filmId) {
return this.getAllFavoriteFilms().then(result => {
if (result) {
var index = result.indexOf(filmId);
result.splice(index, 1);
return this.storage.set(STORAGE_KEY, result);
}
})
}
unfavoriteFilm(planetId) {
return this.getAllFavoritePlanets().then(result => {
if (result) {
var index = result.indexOf(planetId);
result.splice(index, 1);
return this.storage.set(STORAGE_KEY1, result);
}
})
}
}
This is the error message I am getting (x4 times) for each duplication:-
Duplicate function implementation. ts(2393)
The components page (planet-details.page.ts) is as follows:-
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ApiService } from 'src/app/services/api.service';
import { EmailComposer } from '#ionic-native/email-composer/ngx';
import { FavoriteService } from 'src/app/services/favorite.service';
#Component({
selector: 'app-planet-details',
templateUrl: './planet-details.page.html',
styleUrls: ['./planet-details.page.scss'],
})
export class PlanetDetailsPage implements OnInit {
planet: any;
isFavorite = false;
planetId = null;
constructor(private activatedRoute: ActivatedRoute, private api: ApiService,
private emailComposer: EmailComposer, private favoriteService: FavoriteService) { }
ngOnInit() {
let id = this.activatedRoute.snapshot.paramMap.get('id');
this.api.getPlanet(id).subscribe(res => {
this.planet = res;
console.log(res);
});
}
favoritePlanet() {
this.favoriteService.favoritePlanet(this.planetId).then(() => {
this.isFavorite = true;
});
}
unfavoritePlanet() {
this.favoriteService.unfavoritePlanet(this.planetId).then(() => {
this.isFavorite = false;
});
}
sharePlanet() {
let email = {
to: "",
subject: `I love this planet: ${this.planet.name}`,
body: `Do you like it?<br><br>"${this.planet.opening_crawl}"`,
isHtml: true
};
this.emailComposer.open(email);
}
}
The planet-details.page.html is as follows:-
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="/tabs/planets"></ion-back-button>
</ion-buttons>
<ion-title>{{ planet?.title }}</ion-title>
<ion-buttons slot="end">
<ion-button (click)="unfavoritePlanet()" *ngIf="isFavorite">
<ion-icon name="star" slot="icon-only" color="secondary"></ion-icon>
</ion-button>
<ion-button (click)="favoritePlanet()" *ngIf="!isFavorite">
<ion-icon name="star-outline" slot="icon-only"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-card *ngIf="planet" class="planet-card">
<ion-item class="planet-card" lines="none">
<ion-icon name="cloudy-night-outline" slot="start"></ion-icon>
Climate for {{ planet.name }}: {{ planet.climate }}
</ion-item>
<ion-item class="planet-info" lines="none">
<ion-icon name="planet" slot="start"></ion-icon>
Rotation Period: {{ planet.rotation_period }}
</ion-item>
<ion-item class="planet-info1" lines="none">
<ion-icon name="people-outline" slot="start"></ion-icon>
Population: {{ planet.population }}
</ion-item>
</ion-card>
<ion-button expand="full" (click)="sharePlanet()">Share by Email</ion-button>
</ion-content>
The two errors I am getting are same two errors as outlined above (Duplicate Function Implementation) ts.2393 in favorite.service.ts, but now only get 2 errors, instead of 4. Both errors are due to repetition of 'isFavorite(filmId)' and 'isFavorite(planetId).
ERROR in src/app/services/favorite.service.ts:24:3 - error TS2393:
Duplicate function implementation. [ng] [ng] 24
isFavorite(filmId) { [ng] ~~~~~~~~~~ [ng]
src/app/services/favorite.service.ts:30:3 - error TS2393: Duplicate
function implementation. [ng] [ng] 30 isFavorite(planetId)
{ [ng] ~~~~~~~~~~

State changes without setState on key

I'm (very) new to React and am building a small web page as part of my learning process. My web page is a table of currencies (given using an API) and I should be able to delete and add currencies and revert to the table's original state.
Deleting as many as I want and then reverting a first time works fine. When I delete and then revert a second time - nothing happens.
My App's state has a 'shownData' attribute, holding the data currently presented in the table, and an 'initialData' attribute, holding the original data I should present when clicking 'revert'.
From my debugging process, it seems that clicking the 'revert' button ties 'initialData' to 'shownData', so that when reverting a second time nothing happens.
Here's the code for my main App component:
import React, { Component } from 'react';
import CurrencyTable from './currencyTable';
import SearchBar from './searchBar';
// import _ from 'lodash';
import RevertButton from './revertButton';
import logo from './logo.svg';
import './App.css';
import './bootstrap.min.css';
const API_KEY = '7312269b011ac267b5d9663c4a479c24';
const endpoint = 'latest';
class App extends Component {
constructor(props){
super(props);
this.state={
shownCountries: [],
initialCountries: [],
allData: [],
shownData:[],
initialData:[],
value:'',
}
}
componentDidMount(){
this.setState({shownCountries: ['EUR', 'ILS', 'USD', 'GBP', 'AUD']});
this.setState({initialCountries: ['EUR', 'ILS', 'USD', 'GBP', 'AUD']});
// localStorage.setItem('shownC', JSON.stringify(this.shownCountries));
const cachedCountries = localStorage.getItem('shownC');
if (!cachedCountries)
localStorage.setItem('shownC', JSON.stringify(this.state.shownCountries));
else
this.setState({shownCountries: JSON.parse(cachedCountries)});
fetch(`http://data.fixer.io/api/${endpoint}?access_key=${API_KEY}`)
.then(results => {
return results.json();
})
.then(result => {
let rates = result.rates;
let shownData = [];
let initialData = [];
let allData = [];
let i = 0;
let j = 0;
let k = 0;
//check for saved data in local storage:
Object.keys(rates).forEach(key => {
if (this.state.shownCountries.indexOf(key) > -1){
shownData[j] = {rate: key, value: rates[key], button: "btn btn-danger red"};
j++;
}
if (this.state.initialCountries.indexOf(key) > -1){
initialData[k] = {rate: key, value: rates[key], button: "btn btn-danger red"};
k++;
}
allData[i] = {rate: key, value: rates[key], button: "btn btn-danger red"};
i++;
})
this.setState({allData: allData, initialData: initialData, shownData: shownData});
})
}
findValue(rate){
for (let i=0; i<this.state.allData.length; i++){
if (this.state.allData[i].rate === rate)
return this.state.allData[i].value;
}
}
handleChangeValue = value => {
let newRateObj = {rate: value, value: this.findValue(value), button: "btn btn-danger red"};
this.setState({
shownData: this.state.shownData.concat([newRateObj])
});
this.state.shownCountries.concat([value]);
localStorage.setItem('shownC', JSON.stringify(this.state.shownCountries));
}
handleRevertClick = () => {
console.log(this.state.initialData);
localStorage.setItem('shownC', JSON.stringify(this.state.initialCountries));
this.setState({shownData: this.state.initialData});
console.log(this.state.initialData);
};
saveToLocalStorage(data) {
localStorage.setItem('shownC', JSON.stringify(this.getShownCList(data)));
}
getShownCList(data){
let shownC = [];
for (let i=0;i<data.length;i++){
shownC.push(data[i].rate);
}
// console.log(shownC);
return shownC;
}
render() {
console.log("initialData is:");
console.log(this.state.initialData);
if (this.state.initialData.length===0)
return 'loading...';
return (
<div className="headDiv">
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Niki's React Currency App</h1>
</header>
</div>
<div className="mainDiv">
<div className="col">
<div className="col-lg-6 currencyTableContainer">
<CurrencyTable
data = { this.state.shownData }
saveToLS= {(data) => {
this.saveToLocalStorage(data)}}/>
</div>
<div className="col-lg-6 searchAndRevertContainer">
<div className="row">
<div className="row-md-4">
<SearchBar
value={this.state.value}
allData = { this.state.allData }
onChangeValue={this.handleChangeValue}/>
</div>
<div className="row-md-4 revertButton">
<RevertButton
onClick={this.handleRevertClick}/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default App;
And here's the code for the onClick function in the table component, that deletes a row (after which initialData seems to go wrong):
onRemoveClick(rate){
const newShownData = this.state.myTableData;
let index = _.findIndex(newShownData, {rate: rate});
if (index>-1)
newShownData.splice(index, 1);
this.setState({myTableData: newShownData});
this.props.saveToLS(this.state.myTableData);
}
I'm super lost, help? :)
Solved by creating a deep copy of initialData before giving its value to shownData:
...
var temp = JSON.parse(JSON.stringify(this.initialData))
this.setState({shownData: temp});
...

How to Map values in reactjs?

I am confused with reactjs api json call back.
In my container>> i have an api response from this --> let broadcast = this.props.broadcastDetailsPageApiResponse;
I need to map the values to 'broadcastData' variable to print it in component page html.(fileds like 'channel','description','recordedUrl',
How can i do it?
The api response as follows.
broadcastdetailcontainer.jsx: place to map console.log("need to map");line no 34.
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux';
import BroadcastDetailPage from '../components/broadcastList/broadcastDetailPage.jsx'
import * as detailPageActions from '../actions/detailPageActions.jsx'
class BroadcastDetailContainer extends Component {
constructor(props) {
super(props);
this.state = {broadcastData: {}};
this.updateBroadcastData = this.updateBroadcastData.bind(this);
}
componentDidMount() {
let broadcastId = this.props.params.broadcastId;
console.log("Broadcast Id in the componentDidMount: " + broadcastId);
this.updateBroadcastData(broadcastId);
}
updateBroadcastData(broadcastId) {
let broadcastData;
let broadcast = this.props.broadcastDetailsPageApiResponse;
// console.log("broadcast: " + JSON.stringify(broadcast));
if (broadcast != null) {
/////////////////////////////////////////////////////////////////
broadcastData = broadcast.channel;
broadcastData = broadcast.user;
console.log("need to map");
//console.log(broadcastData);
/////////////////////////////////////////////////////////////////
}
let content = this.props.homePageApiResponse.content;
if (content != null & broadcastData == null) {
console.log('entered when the home API');
for (let i = 0; i < content.length; i++) {
if (content[i].id == broadcastId) {
broadcastData = content[i];
break;
}
}
}
if (broadcastData == null) {
content = this.props.searchPageApiResponse.content;
if (content != null) {
console.log('entered when the search API');
for (let i = 0; i < content.length; i++) {
if (content[i].id == broadcastId) {
broadcastData = content[i];
console.log(broadcastData);
break;
}
}
}
}
if (broadcastData == null) {
this.props.actions.callDetailApi(broadcastId);
}
else{
this.setState({broadcastData: broadcastData});
}
}
componentWillReceiveProps(nextProps) {
let broadcastId = nextProps.params.broadcastId;
console.log('componentWillReceiveProps');
this.updateBroadcastData(broadcastId);
}
render() {
//console.log("rendered: "+JSON.stringify(this.props.broadcastDetailsPageApiResponse));
return (
<div>
{this.state.broadcastData &&
<BroadcastDetailPage
broadcastDetail={this.state.broadcastData}/>
}
</div>
)
}
}
const mapStateToProps = (state) => ({
homePageApiResponse: state.homePageApiResponse,
searchPageApiResponse: state.searchPageApiResponse,
broadcastDetailsPageApiResponse: state.detailPageApiResponse,
})
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(detailPageActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)
(BroadcastDetailContainer)
I need to display the map values in the following component page:
import React, {Component} from "React";
import {Row, Clearfix} from "react-bootstrap/lib";
import VideoPlayer from "./videoPlayer.jsx";
class BroadcastDetailPage extends Component {
render() {
let broadcastData = this.props.broadcastDetail;
return (
<Row>
<div className="popup-cont-container">
<VideoPlayer broadcastData={broadcastData}/>
<div className="video-info">
<div className="info-left">
<div className="main-tag">
# {broadcastData.channel}
</div>
<div className="datenTime">
On Date
</div>
<div className="datenTime"> Location
</div>
<div className="video-info-main"> </div>
<div className="at">#
{broadcastData.handle} </div>
</div>
<div className="info-right">
<span><img src="images/eyeico.png"/>
<a> </a> Live viewers
</span>
<span><img src="images/heart.png"/>
<a> </a> Likes
</span>
</div>
</div>
<Clearfix/>
</div>
</Row>
);
}
}
export default BroadcastDetailPage
{"id":4,"user":{"userId":1,"chatUserId":0,"handle":"satishkrypto","countryCode":"+971","phone":"569942359","picUrl":"https://ss-ap-southeast-1.amazonaws.com/production-speakerswire-images/user-profiles/male3x.png","userStat":{"id":1,"followers":4,"followings":5,"broadcastLikes":0,"broadcastViews":90},"name":"satish verma","existingUser":false,"userProfileImageType":"MALE","invitesLeft":4,"userPriviledge":"SW_BROADCASTER"},"description":"satish's Broadcast","streamId":"pub11489928734882","audio":false,"screenShotUrl":"https://ss-api-eb-dev.s3-ap-southeast-1.amazonaws.com/broadcast-images/pub11489928734882-1489928751454.jpg","recordedUrl":"https://production-ss-videos-red5pro.s3-ap-southeast-1.amazonaws.com/video/record/pub581489997130311.mp4","status":"RECORDED","type":"PUBLIC","locationEnabled":false,"commentsEnabled":true,"channel":{"channelId":14,"channel":"Productivity","color":"#4A148C","priority":986,"created":1489922543000,"updated":1489922543000,"broadcast":0,"liveBroadcast":0},"broadcastStat":{"id":4,"likes":0,"views":14,"viewers":5,"liveViewers":0},"latitude":0.0,"longitude":0.0,"startTime":1489928737000,"endTime":1489928787000,"updated":1489928737000,"event":{"id":4,"description":"satish's Broadcast","type":"BROADCAST_PUBLIC","startTime":1489928737000,"endTime":1489928787000,"status":"FINISHED","eventReminderStatus":"NOT_SENT","updatedAt":1489928787000},"expectedDuration":0,"serviceProvider":"agora"}
Update:
broadcastData = {
...broadcast,
broadcaster: {
name: broadcast.user.name,
handle: broadcast.user.handle
},
channel: {
// channelName: broadcast.channel
},
likes: broadcast.broadcastStat.likes,
views: broadcast.broadcastStat.views,
}
Component 1
render() {
return (
<div className="flexslider tabs_slider no-slide">
<ul className="slides">
{this.state.Movies.map((item, index) => <Component2
key={item.db_recom_id}
item={item}
/>)}
</ul>
</div>
)
}
Component 2
render() {
return (
<li>
<img className="wrap_me" src={this.props.item.db_movies_img} />
)
}
Values that you specified in ques, are present directly inside object, you can access them by data.channel.any_key, data.description, data.recordedUrl.
If you want to print all the values of data.channel, then use map otherwise you can access directly by data.channel.channel.
Check this example:
let data = {
"id":4,
"user":{
"userId":1,
"chatUserId":0,
"handle":"satishkrypto",
"countryCode":"+971",
"phone":"569942359",
"picUrl":"https://ss-ap-southeast-1.amazonaws.com/production-speakerswire-images/user-profiles/male3x.png",
"userStat":{
"id":1,
"followers":4,
"followings":5,
"broadcastLikes":0,
"broadcastViews":90
},
"name":"satish verma",
"existingUser":false,
"userProfileImageType":"MALE",
"invitesLeft":4,
"userPriviledge":"SW_BROADCASTER"
},
"description":"satish's Broadcast",
"streamId":"pub11489928734882",
"audio":false,
"screenShotUrl":"https://ss-api-eb-dev.s3-ap-southeast-1.amazonaws.com/broadcast-images/pub11489928734882-1489928751454.jpg",
"recordedUrl":"https://production-ss-videos-red5pro.s3-ap-southeast-1.amazonaws.com/video/record/pub581489997130311.mp4",
"status":"RECORDED",
"type":"PUBLIC",
"locationEnabled":false,
"commentsEnabled":true,
"channel":{
"channelId":14,
"channel":"Productivity",
"color":"#4A148C",
"priority":986,
"created":1489922543000,
"updated":1489922543000,
"broadcast":0,"liveBroadcast":0
},
"broadcastStat":{
"id":4,
"likes":0,
"views":14,
"viewers":5,
"liveViewers":0
},
"latitude":0.0,
"longitude":0.0,
"startTime":1489928737000,
"endTime":1489928787000,
"updated":1489928737000,
"event":{
"id":4,
"description":"satish's Broadcast",
"type":"BROADCAST_PUBLIC",
"startTime":1489928737000,
"endTime":1489928787000,
"status":"FINISHED",
"eventReminderStatus":"NOT_SENT",
"updatedAt":1489928787000
},
"expectedDuration":0,
"serviceProvider":"agora"
};
class App extends React.Component{
render(){
return(
<div>
Channel Details: {Object.keys(data.channel).map(el=>{
return <p>{el}: {data.channel[el]}</p>
})}
Description: {data.description}
RecordedUrl: {data.recordedUrl}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>

Cannot read property of undefined Angular 2

I am trying to do a simple import but I am getting a massive stack trace issue.
I have tried searching everywhere for issues related to this but to me, the stack trace doesn't provide much information.
EDIT: I have tried setting it a variable that isn't fetched from Firebase and it works fine. I guess the question now is how do I handle this information from Firebase so that it loads when it is ready.
Here are the relevant files:
main.ts:
import { bootstrap } from '#angular/platform-browser-dynamic';
import {AppComponent} from './app.component';
import { HTTP_PROVIDERS } from '#angular/http';
bootstrap(AppComponent, [HTTP_PROVIDERS]);
player.services.ts:
import { Injectable } from '#angular/core';
import {Player} from "../classes/player";
#Injectable()
export class PlayerService {
player: Player;
getPlayer()
{
return Promise.resolve(this.player);
}
createPlayer(uid: string, name: string, firebaseRef: Firebase)
{
this.player = {
'uid': uid,
'name': name,
'level': 1,
'maxHealth': 100,
'health': 100,
'maxEnergy': 50,
'energy': 50,
'fun': 1,
'skill': 1,
'knowledge': 1
}
firebaseRef.child('players').child(uid).set(this.player);
}
setPlayer(player: Player)
{
this.player = player;
}
}
app.component.ts
import { Component, OnInit } from '#angular/core'
import { PlayerDetailComponent } from './components/player-detail.component';
import {PlayerService} from "./services/player.service";
import {FirebaseEventPipe} from "./firebasepipe";
import {Player} from "./classes/player";
#Component({
selector: "my-app",
templateUrl: 'app/views/app.component.html',
directives: [PlayerDetailComponent],
providers: [PlayerService],
pipes: [FirebaseEventPipe]
})
export class AppComponent implements OnInit{
title = "title";
authData: any;
private firebaseUrl: string;
private firebaseRef: Firebase;
private loggedIn = false;
player: Player;
constructor(private playerService: PlayerService) {
this.firebaseUrl = "https://!.firebaseio.com/";
this.firebaseRef = new Firebase(this.firebaseUrl);
this.firebaseRef.onAuth((user) => {
if (user) {
this.authData = user;
this.loggedIn = true;
}
});
}
getPlayer() {
this.firebaseRef.once("value", (dataSnapshot) => {
if (dataSnapshot.child('players').child(this.authData.uid).exists()) {
this.firebaseRef.child('players').child(this.authData.uid).once("value", (data) => {
this.player = data.val();
this.playerService.setPlayer(this.player);
console.log(this.player);
});
} else {
this.playerService.createPlayer(this.authData.uid, this.getName(this.authData), this.firebaseRef);
this.playerService.getPlayer().then(player => this.player);
console.log(this.player);
}
});
}
ngOnInit() {
this.getPlayer();
}
authWithGithub() {
this.firebaseRef.authWithOAuthPopup("github", (error) =>
{
if (error) {
console.log(error);
}
});
}
authWithGoogle() {
this.firebaseRef.authWithOAuthPopup("google",(error) =>
{
if (error) {
console.log(error);
}
});
}
getName(authData: any) {
switch (authData.provider) {
case 'github':
return authData.github.displayName;
case 'google':
return authData.google.displayName;
}
}
}
player-detail.component.ts
import { Component, Input, OnInit } from '#angular/core';
import { Player } from '../classes/player';
#Component({
selector: "player-details",
templateUrl: "app/views/player-detail.component.html",
styleUrls: ['app/style/player-detail.component.css'],
})
export class PlayerDetailComponent implements OnInit{
#Input() player: Player;
ngOnInit() { console.log(this.player)}
}
app.component.html
<nav class="navbar navbar-default">
<div class="container">
<ul class="nav navbar-nav">
<li class="navbar-link">Home</li>
</ul>
</div>
</nav>
<div class="jumbotron" [hidden]="loggedIn">
<div class="container">
<h1>Angular Attack Project</h1>
<p>This is a project for the Angular Attack 2016 hackathon. This is a small project where set goals
in order to gain experience as a player and person. In order to begin, please register with on of the following services</p>
<button class="btn btn-social btn-github" (click)="authWithGithub()"><span class="fa fa-github"></span>Sign Up With Github </button>
<button class="btn btn-social btn-google" (click)="authWithGoogle()"><span class="fa fa-google"></span>Sign Up With Github </button>
</div>
</div>
<player-details [player]="player" [hidden]="!loggedIn"></player-details>
player-detail.component.html
<div id="player" class="panel panel-default">
<div id="player-stats" class="panel-body">
<img id="player-image" class="img-responsive" src="../app/assets/images/boy.png"/>
<div class="health-bars">
<div class="health-bar">HEALTH:<br/><progress value="{{ player.health }}" max="{{ player.maxHealth }}"></progress></div>
<div class="energy-bar">ENERGY:<br/><progress value="{{ player.energy }}" max="{{ player.maxEnergy }}"></progress></div>
<div class="player-attributes"><span class="fa fa-futbol-o player-attr fun">: {{ player.fun }} </span><span class="fa fa-cubes player-attr skill">: {{ player.skill }}</span> <span class="fa fa-graduation-cap player-attr knowledge">: {{ player.knowledge }}</span></div>
</div>
</div>
</div>
In your service you don't have to return with the promise. You can use a getter
private player: Player;
get CurrentPlayer()
{
return this.player;
}
Then in your component:
getPlayer() {
this.firebaseRef.once("value", (dataSnapshot) => {
if (dataSnapshot.child('players').child(this.authData.uid).exists()) {
this.firebaseRef.child('players').child(this.authData.uid).once("value", (data) => {
this.playerService.setPlayer(this.player);
console.log(this.player);
});
} else {
this.playerService.createPlayer(this.authData.uid, this.getName(this.authData), this.firebaseRef);
console.log(this.player);
}
});
ngOnInit() {
this.player = this.playerService.CurrentPlayer();
this.getPlayer();
}
If you setup the reference first, it should automatically update. You can also throw an *ngIf player-details component definition in the DOM and only show it once the player object isn't undefined.
Edit
Just saw someone else posted about *ngIf prior to me, so if that is the solution, please mark theirs.
The player variable was undefined when the PlayerDetailComponent was loaded therefore there was no such object as player.
To fix this, OnChanges can be implemented like this:
import { Component, Input, OnChanges, SimpleChange } from '#angular/core';
import { Player } from '../classes/player';
import {HealthBarComponent} from "./health-bar.component";
import {ChecklistComponent} from "./checklist.component";
#Component({
selector: "player-details",
templateUrl: "app/views/player-detail.component.html",
styleUrls: ['app/style/player-detail.component.css'],
directives: [HealthBarComponent, ChecklistComponent],
})
export class PlayerDetailComponent implements OnChanges{
#Input()
player: Player;
ngOnChanges(changes: {[propName: string]: SimpleChange}) {
}
}
and then we can add *nfIf="player" within the template to ensure that the player object isn't blank before loading the element.

Resources