How can i display followers count of a follower?
What i have setup right now:
Field (input) to enter Github username in, when button is clicked all of followers of the user will be listed bellow. Follower username & follower ID are already shown, but i also would like to show the amount of followers the follower has.
Picture for better understanding of what I'm looking for:
SCREENSHOT
github-profile.component.ts
import {Component} from 'angular2/core';
import {HTTP_PROVIDERS} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';
import {GitHubService} from './github.service';
#Component({
selector: 'github-profile',
styles: [
`
/*noinspection CssInvalidPropertyValue*/.avatar {
width: 100;
height: 100;
border-radius: 100%;
}
`
],
template: `
<i *ngIf="isLoading" class="fa fa-spinner fa-spin fa-3x"></i>
<form #f="ngForm" class="animated fadeInRight">
<div class="form-group">
<label for="name">Follower finder</label>
<input ngControl="username"
#username="ngForm"
[(ngModel)] = "inputusr"
type="text"
class="form-control"
placeholder="Enter GitHub username"
required
minlength="3">
<div *ngIf="username.touched && username.errors">
<div *ngIf="username.errors.required"
class="alert alert-danger">
Username is required.
</div>
<div class="alert alert-danger"
*ngIf="username.errors.minlength">
First name should be minimum 3 characters.
</div>
</div>
</div>
<button (click)=OnClick($event) type="submit" class="btn btn-default" [disabled]="!f.valid">Search</button>\
<button type="submit" class="btn btn-danger" (click)=ClearResults($event)>Clear</button>
</form>
<h3 *ngIf="isVisible">#{{user.login}} aka {{user.name}}</h3>
<img *ngIf="isVisible" class="avatar" src="{{ user.avatar_url }}">
<h3 *ngIf="isVisible">Followers</h3>
<div *ngIf="isVisible" class="row">
<div *ngFor="#follower of followers" class="col-sm-3 animated fadeInRight">
<div class="thumbnail">
<img *ngIf="isVisible" src="{{ follower.avatar_url }}" alt="...">
<div class="caption">
<h4 class="text-center">{{ follower.login }}</h4>
<p class="text-center">User ID: {{ follower.id }}</p>
<p class="text-center">Followers: Show followers of a follower here.</p>
</div>
</div>
</div>
</div>
`,
providers: [HTTP_PROVIDERS, GitHubService]
})
export class GitHubProfileComponent {
inputusr: string;
isVisible = false;
isLoading = false;
username: string;
user = {};
followers = {};
constructor(private _gitHubService: GitHubService){
}
ClearResults($event){
this.isVisible = false;
this.inputusr = "";
}
OnClick($event){
this.isLoading = true;
this.username = this.inputusr;
Observable.forkJoin(
this._gitHubService.getUser(this.username),
this._gitHubService.getFollowers(this.username)
)
.subscribe(
res => {
this.user = res[0];
this.followers = res[1];
},
null,
() => { this.isLoading = false; this.isVisible = true; })
}
}
github.service.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class GitHubService {
private _baseUrl = "https://api.github.com/users/";
constructor(private _http: Http){}
getUser(username){
return this._http.get(this._baseUrl + username)
.map(res => res.json());
}
getFollowers(username){
return this._http.get(this._baseUrl + username + "/followers")
.map(res => res.json());
}
}
There is a key named "followers" in the response for https://api.github.com/users/odetocode which is pretty much what u need.
The below method can be used if u want the basic details of each follower of the user.
The URL https://api.github.com/users/:username/followers basically returns an array of objects, with each object representing a follower.
There is an upper limit of 30 on the number of entries returned in the response.
Workaround
So basically in order to fetch more users we can append to the url a query string parameter ?page=1, ?page=2and so on.
Check for the response till the length of array = 0;
That will give u the last page till which followers exists.
Then count the number of followers in the last page.
Example: I have 340 followers
So response for https://api.github.com/users/:username/followers?page=13 will be and array of length 0.
Then send a request for https://api.github.com/users/:username/followers?page=12 which will return an array of length 10
Count of followers: (30 * 11) + (10 * 1) = 340
Related
I would like to fill a form by Id with Angular FormBuilder. My problem is that I don't now how to do it and i've try many of the topics solutions, but nothing really helps. here is what i want : when i click on a button update, another page opens with some fields that match, and thoose fields have to be filled. The parent component is working and is sending an Id via a router navigate. But the component that should catch and made the view working isn't working at all. Here is the code of that component :
import { Component, OnInit, OnDestroy, Input } from '#angular/core';
import { Subject } from 'rxjs';
import { Site } from '#shared/models/site.model';
import { MessagesService } from '#core/messages/messages.service';
import { FormBuilder, FormGroup, FormArray } from '#angular/forms';
import { BmrGenerationService } from './bmr-generation.service';
import { SiteService } from '#shared/services/site.service';
import { takeUntil } from 'rxjs/operators';
import { MmrService } from '#components/mmr/mmr.service';
import { Mmr } from '#shared/models/mmr.model';
#Component({
selector: 'idea-bmr-generation',
templateUrl: './bmr-generation.component.html',
styleUrls: ['./bmr-generation.component.scss']
})
export class BmrGenerationComponent implements OnInit, OnDestroy {
private unsubscribe$ = new Subject();
settings: any;
siteSelected: Site;
constructor(
private fb: FormBuilder,
private messageService: MessagesService,
private bmrGenerationService: BmrGenerationService,
private siteService: SiteService,
) { }
form: FormGroup;
isFormModified = false;
items: FormArray;
ngOnInit() {
this.createForm();
this.listenerForm();
// Lors d'un changement de site
this.siteService.currentSite$.pipe(takeUntil(this.unsubscribe$)).subscribe(
(site) => {
if (site) {
this.siteSelected = site;
this.getSettingsSite(site.id);
}
});
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
getSettingsSite(idSite: number): void {
/*this.adminSiteService.getSettingsBySite(idSite).subscribe((settings) => {
this.settings = settings;
this.createForm();
this.listenerForm();
});
}*/
}
createForm() {
this.form = this.fb.group({
productCode: [{ value: null, disabled: false }],
userReference: [{ value: null, disabled: false }],
productDescription: [{ value: null, disabled: false }],
});
}
listenerForm() {
if (this.form) {
this.form.valueChanges.subscribe(() => {
this.isFormModified = true;
});
}
}
onSubmit() {
if (this.isFormModified) {
this.isFormModified = false;
this.messageService.showSuccess('GENERATION_VERIF');
}
}
onCancel() {
if (this.isFormModified) {
this.createForm();
this.listenerForm();
this.isFormModified = false;
}
}
}
For example, i do't know how to replace the "null" values with the matching values. And i don't know how to get the Id in this component.
Here is the template :
<idea-section-container>
<div title>
<fa-icon icon="cogs"></fa-icon>
{{'BMR.GENERATION.TITLE' | translate}}
</div>
<div body>
<div class="site-container">
<form *ngIf="form" [formGroup]="form" (ngSubmit)="onSubmit()" class="site-form">
<div class="site-form-line">
<div class="site-form-label">
<div>
<fa-icon icon="info-circle" tooltipPosition="bottom" tooltipStyleClass="site-tooltip"
pTooltip="Code du produit OF">
</fa-icon>
</div>
<span>
{{'BMR.GENERATION.PRODUCT-CODE' | translate}}
</span>
</div>
<mat-form-field class="site-form-field" appearance="outline">
<mat-label>{{'BMR.GENERATION.PRODUCT-CODE' | translate}}</mat-label>
<input matInput formControlName="productCode">
</mat-form-field>
</div>
<div class="site-form-line">
<div class="site-form-label">
<div>
<fa-icon icon="info-circle" tooltipPosition="bottom" tooltipStyleClass="site-tooltip"
pTooltip="référence du MMR">
</fa-icon>
</div>
<span>
{{'BMR.GENERATION.MMR_REF' | translate}}
</span>
</div>
<mat-form-field class="site-form-field" appearance="outline">
<mat-label>{{'BMR.GENERATION.MMR_REF' | translate}}</mat-label>
<input matInput formControlName="mmrReference">
</mat-form-field>
</div>
<div class="site-form-line">
<div class="site-form-label">
<div>
<fa-icon icon="info-circle" tooltipPosition="bottom" tooltipStyleClass="site-tooltip"
pTooltip="Description du produit">
</fa-icon>
</div>
<span>
{{'BMR.GENERATION.PRODUCT_DESCRIPTION' | translate}}
</span>
</div>
<mat-form-field class="site-form-field" appearance="outline">
<mat-label>{{'BMR.GENERATION.PRODUCT_DESCRIPTION' | translate}}</mat-label>
<input matInput formControlName="productDescription">
</mat-form-field>
</div>
<div class="margin-top20 site-form-footer">
<idea-button type="submit" color="principal-blue" [disabled]="!isFormModified" class="margin-right20">
{{'BMR.GENERATION.SAVE' | translate}}
</idea-button>
<idea-button type="button" color="principal-blue" [disabled]="!isFormModified" (click)="onCancel()">
{{'GENERAL.CANCEL' | translate}}
</idea-button>
</div>
</form>
</div>
</div>
</idea-section-container>
No error is displayed within the console.
Thank you for any help.
You can change the formControlName of the form elements in another page which open up on click of button and make it same as the id which is sent from previous page.
Then you can use
this.form.control.idSentFromPreviousPage.setValue(valueFromPreviousPage)
I am building an Angular Movie DB, the two components I am working with are the "search-movie" component and the "single-view" component. The "search-movie" component displays a list of movies, the "single-view" component shows further details of a given movie.
I would like it so that when I click on a movie from the list in the "search-movie" component, the page renders the "single-view" component for that movie.
I have managed to set this up so that navigating to the "single-view" path with a movie id (i.e. /movie/tt4425200) directly from the URL, correctly loads up the info for that movie by it's ID, so that's all correctly set up.
I just can't seem to connect the clicking of a movie in the "search-movie" component, to then successfully navigate to the correct "single-view" path. So using the example above, clicking on the relevant movie loads up the "single-view" component with the URL path /movie/tt4425200.
I'm sure that this is a case of using #Input to communicate between the two components, but I just can't figure it out.
search-movie.component.ts:
import { Component, OnInit, Input } from '#angular/core';
import { FormControl } from '#angular/forms';
import { DataService } from '../data.service';
#Component({
selector: 'app-search-movie',
templateUrl: './search-movie.component.html',
styleUrls: ['./search-movie.component.css']
})
export class SearchMovieComponent implements OnInit {
searchControl = new FormControl('');
movieResults: any = [];
constructor(private data: DataService) { }
ngOnInit() {}
/* GET request */
getData(event) {
const film = event.target.value;
this.data.searchFilm(film)
.subscribe( (res) => {
res = this.movieResults = res;
console.log(res);
});
}
}
search-movie.component.html:
<div class="container">
<h1 class="is-size-3">Find your favorite movies...</h1>
<form (ngSubmit)="onSubmit()">
<input
type="text"
(keyup)="getData($event)"
placeholder="Start typing..."
[formControl]="searchControl" />
</form>
{{searchControl.value}}
</div>
<div class="container">
<ul>
<li id="list-item" *ngFor="let x of movieResults; let i = index;">
<img [src]="x.Poster" onerror="this.src='../assets/images/default-poster.jpg'">
<p id="movie-title" class="is-size-5">{{x.Title}} ({{x.Year}}) </p>
</li>
</ul>
</div>
single-view.component.ts:
import { Component, OnInit, Input } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { DataService } from '../data.service';
#Component({
selector: 'app-single-view',
templateUrl: './single-view.component.html',
styleUrls: ['./single-view.component.css']
})
export class SingleViewComponent implements OnInit {
singleDetails: any = {};
constructor(
private data: DataService,
private route: ActivatedRoute) { }
ngOnInit() {
/** GENERATE FILM DETAILS BY movie/id where id is the imdbID */
const id = this.route.snapshot.paramMap.get('id');
this.data.moviebyID(id)
.subscribe( (res) => {
res = this.singleDetails = res;
});
}
}
single-view.component.html:
<div class="container singleDisplayContainer">
<div class="columns is-mobile">
<div class="column" id="poster-column">
<img id="poster" [src]="singleDetails.Poster" [alt]="singleDetails.Title" onerror="this.src='../assets/images/default-poster.jpg'">
</div>
<div id="info-column" class="column">
<div class="columns">
<div class="column is-three-fifths">
<h1 class="is-size-3">{{singleDetails.Title}} <span class="is-size-4">({{singleDetails.Year}})</span> </h1>
<p style="font-size: 14px"> {{singleDetails.Rated}} | {{singleDetails.Runtime}} | {{singleDetails.Genre}} | {{singleDetails.Released}} </p>
</div>
<div class="column">
<div class="columns">
<div class="column is-one-third">
<img id="star-rating" width="50px" src="../../assets/images/star-rating.png" alt="{{singleDetails.Title}}">
</div>
<div class="column">
<p style="font-size: 2em"> {{singleDetails.imdbRating}}<span style="font-size: 12px">/10</span></p>
<p>{{singleDetails.imdbVotes}}</p>
</div>
</div>
</div>
</div>
<br>
<p>Plot: {{singleDetails.Plot}} </p>
<br>
<p>Directed by: {{singleDetails.Director}} </p>
<br>
<p>Writers: {{singleDetails.Writer}} </p>
<br>
<p>Actors: {{singleDetails.Actors}} </p>
<br>
<p onerror="this.style.display='none'">Box office: {{singleDetails.BoxOffice}} </p>
</div>
</div>
</div>
In Your UL -> LI, add routerLink property and pass the movie id as the second param
[routerLink]="['/movie/,x.movieId]">
<ul>
<li id="list-item" *ngFor="let x of movieResults; let i = index;" [routerLink]="['/movie/,x.movieId]">
<img [src]="x.Poster" onerror="this.src='../assets/images/default-poster.jpg'">
<p
id="movie-title"
class="is-size-5">{{x.Title}} ({{x.Year}}) </p>
</li>
</ul>
Looks like you have everything set up, all you need to do is navigate to single-view on click of the movie.
search-movie.component.ts
showDetails(id : string) {
this.router.navigate(['movie',{ id }]);
}
search-movie.component.html
<!-- in showDetails you will pass id of the current movie while looping -->
<li id="list-item" *ngFor="let x of movieResults; let i = index;" (click)="showDetails(x.id)">
<img
[src]="x.Poster"
onerror="this.src='../assets/images/default-poster.jpg'"
>
<p
id="movie-title"
class="is-size-5">{{x.Title}} ({{x.Year}}) </p>
</li>
This will navigate to single-view page. is this what you want right?
or you need to load single-view in the same page as the list?
I am building an accordion based on array of data fetched from the server but the clicks are not working.
The accordion is working fine on hard-coded data but not on the data fetched using HttpClient. I even tried using button instead of anchor tags but to no avail.
<div class="accordion" id="chalsAccordion">
<div class="card rounded-0" *ngFor="let chal of chals">
<a data-toggle="collapse" href="#{{ chal._id }}"
><div class="card-header text-dark">
<p class="mb-0">
{{ chal.title }}<small> - {{ chal.author }}</small>
</p>
<p class="mb-0 ml-auto">{{ chal.points }}</p>
</div></a
>
<div id="{{ chal._id }}" class="collapse">
<div class="card-body" [innerHTML]="chal.desc"></div>
<div class="card-footer" *ngIf="!userService.admin">
<form [formGroup]="flagForm" style="width: 100%">
<div class="input-group">
<input type="text" class="form-control rounded-0" placeholder="Flag" formControlName="flag" />
<div class="input-group-append">
<button class="btn btn-primary rounded-0" [disabled]="!flagForm.valid" (click)="submitFlag(chal._id)">Submit</button>
</div>
</div>
</form>
</div>
<div class="card-footer" *ngIf="userService.admin">
<div class="ml-auto">
<button class="btn btn-danger rounded-0"><fa-icon [icon]="['fas', 'trash']"></fa-icon></button>
</div>
</div>
</div>
</div>
</div>
<button class="btn btn-primary add-chal-btn" routerLink="/chals/add" *ngIf="userService.admin"><fa-icon [icon]="['fas', 'plus']"></fa-icon></button>
import { Component, OnInit } from "#angular/core";
import { FormGroup, FormControl, Validators } from "#angular/forms";
import { ToastrService } from "ngx-toastr";
import { UserService } from "../services/user.service";
import { ChalService } from "../services/chal.service";
import { Response } from "../interfaces/response";
import { $ } from "protractor";
#Component({
selector: "app-chals",
templateUrl: "./chals.component.html",
styleUrls: ["./chals.component.scss"]
})
export class ChalsComponent implements OnInit {
chals = [];
flagForm = new FormGroup({
flag: new FormControl("", [Validators.required])
});
constructor(private toast: ToastrService, public userService: UserService, private chalService: ChalService) {}
ngOnInit() {
this.chalService.getAll().subscribe(
(data: Response) => {
this.chals = data.data["chals"];
},
err => {
this.toast.error(err.error.msg, "Oops!!!");
}
);
}
submitFlag(id: string) {}
}
Edit - The response is coming fine and the UI is also rendered correctly, just the problem is that the click does not expand the accordion.
Arrays are Index based so to access an element in an Array you have to provide an Index. To get the results from the response simply try to set the response to your array.
`ngOnInit() {
this.chalService.getAll().subscribe(
(data: Response) => {
this.chals = data.data; // -- updated the code here
},
err => {
this.toast.error(err.error.msg, "Oops!!!");
});
}`
Also please debug chalService to see if you are getting the response of your http call to the api.
I found the problem, it was that the _id field was starting with an int which was the root of the problem.
I solved it by adding an _ to every _id field.
I am designing a personal dictionary application using oxford dictionary api. I am trying to display a full page view of the word after clicking the search button or clicking the word in the list of suggested words. But I am getting error "Cannot read property "pathname" of undefined "after clicking the word in the list or the search button. Also the full page view is not coming
I have written two functions called btnClick() and onListClick() in search box component to achieve this. This has been handled in the respective HTML file. Also, I have one component called fullview which gives the full view of the word.
search-box.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { FormControl } from '#angular/forms';
import { WordService } from '../word.service';
import { ToastrService } from 'ngx-toastr';
import { filter , switchMap } from 'rxjs/operators';
#Component({
selector: 'app-search-box',
templateUrl: './search-box.component.html',
styleUrls: ['./search-box.component.css']
})
export class SearchBoxComponent implements OnInit {
public searchField : FormControl = new FormControl();
public wordList:any;
public displayList = false;
public link;
constructor(private router:Router, private http:WordService, private toastr:ToastrService) { }
ngOnInit() {
this.searchField.valueChanges
.pipe(
filter(value => !value || value.length > 2 || value !== ''),
filter(value => value !== ''),
switchMap( query => query.length > 2 ? this.http.getWord(query) : this.wordList = ' ')
)
// handling observable
.subscribe(
data => {
this.wordList = '';
this.displayList = true;
this.wordList = data;
},
// handling http error codes
error => {
console.log(error);
if(error.status === 404 || error.status === 414){
this.toastr.error('Try again with valid word');
}
else if(error.status === 403){
this.toastr.error(`Invalid credentials`,`Please enter correct details`);
}
else if(error.status === 414){
this.toastr.error(`Your word is too long`,`Please reduce the length of the word`);
}
else if(error.status === 500){
this.toastr.warning(`Something is broken`,`Please contact developer`);
}
else if(error.status === 502){
this.toastr.info(`Oxford Dictionaries API is down or being upgraded`,`Bad Gateway`);
}
else if(error.status === 503){
this.toastr.info(`Please try again later`, `Service Unavailable`);
}
else if(error.status === 504){
this.toastr.info(`Please try again later`, `Gateway timeout`);
}
}
);
}
// declared focus function for search box value
public onFocus(value)
{
if(value.length > 0)
{
this.displayList = true;
}
else
{
this.displayList = false;
}
}
// declared onListClick function for clicking word in list
public onListClick(e)
{
this.link = this.router.url.split('/');
if (this.link.indexOf('definition') > -1) {
this.displayList = false;
}
e.stopPropagation();
this.router.navigate([e.target.children['0'].pathname]); // error in this line
}
//declared button click function
public btnClick(value)
{
const letters = /^[A-Za-z ]*$/;
const regexp = value.match(letters);
if(regexp === null || value === '')
{
alert('Please enter a word');
}
else
{
this.router.navigate(['/definition',value]);
}
}
}
search-box.component.html
<div class="form-inline m-auto flex-center input-group">
<input #searchBox [formControl]="searchField" type="search" class="form-control" placeholder="Enter More than 3 Characters"
(focus)="onFocus(searchBox.value)" required>
<div class="input-group-append">
<button (click)="btnClick(searchBox.value)" class="btn-rounded btn-md btn-mdb-color m-0" type="button"
>Search
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
<div *ngIf="displayList">
<ul class="autocomplete-wrap mt-3" *ngIf="wordList">
<li *ngFor="let word of wordList.results " (click)="onListClick($event)">
<a [routerLink]="['/definition', word.id]">
{{word.word}}
</a>
</li>
</ul>
</div>
</div>
full-view.component.ts
import { Component, OnInit } from '#angular/core';
import { ToastrService } from 'ngx-toastr';
import { Router, ActivatedRoute } from '#angular/router';
import { WordService } from '../word.service';
import { unionBy, uniq, sortBy } from 'lodash-es';
#Component({
selector: 'app-full-view',
templateUrl: './full-view.component.html',
styleUrls: ['./full-view.component.css']
})
export class FullViewComponent implements OnInit {
public fulldata;
public antonyms = [];
public synonyms = [];
public resultObject;
public lexicalentries = [];
public varientforms = [];
public wordOrigin;
public currentWord;
public residueData = [];
public notes = [];
public show = false;
constructor(private router:Router,private route:ActivatedRoute,private http:WordService,private toastr:ToastrService) {}
ngOnInit() {
// Getting the parameter from URL
this.route.params.subscribe(routeParams => {
this.currentWord = routeParams.id;
this.http.getDefinition(routeParams.id).subscribe(
data => {
this.fulldata = data;
this.updateData(this.fulldata);
},
error => {
this.handleError(error);
}
);
});
}
public updateData(data) {
this.resultObject = data.results['0'];
this.lexicalentries = this.resultObject['lexicalEntries'];
// removing residue data from full data
this.residueData = this.lexicalentries.filter(
lexicalEntry => lexicalEntry.lexicalCategory !== 'Residual'
);
// making arrays empty for new data
this.antonyms = [];
this.synonyms = [];
this.wordOrigin = '';
this.notes = [];
this.extractData(this.residueData);
}
play(audio)
{
audio.play(); // play audio on clicking speak icon
}
public extractData(data) {
for(let singleData of data){
console.log(singleData); // printing the word
// extracting word origin data
if(singleData.entries['0'].etymologies){
this.wordOrigin = singleData.entries['0'].etymologies;
}
// extracting varient forms data
if(singleData.entries['0'].hasOwnProperty('varientforms')){
this.varientforms.push(singleData.entries['0'].varientforms['0'].text);
}
// extracting notes
if(singleData.entries['0'].hasOwnProperty('notes')){
const temp = [];
for(const note of singleData.entries['0'].notes){
temp.push(note);
}
const not = unionBy(temp, 'text');
this.notes = not;
}
}
this.getSyn();
this.toastr.success(`Definition of ${this.resultObject['word']} is Loaded`);
}
// function to get the synonyms
public getSyn() {
this.http.SynAnt(this.currentWord).subscribe(
data => {
let values = data;
this.separateData(values);
},
error => {
this.handleError(error);
}
);
}
// separate synonym and antonym into separate arrays
public separateData(values) {
const synonyms = [];
const antonyms = [];
for(const data of values.results['0'].lexicalentries){
for(const syn of data.entries['0'].senses){
if(syn.synonyms){
synonyms.push(syn.synonyms);
}
if(syn.antonyms){
antonyms.push(syn.antonyms);
}
}
}
this.separateSyn(synonyms);
this.separateAnt(antonyms);
}
// separating antonyms
public separateAnt(data) {
const temp = [];
data.map(i => {
i.map(j => {
temp.push(j.text);
});
});
this.antonyms = sortBy(uniq(temp));
}
// separating synonyms
public separateSyn(data) {
const temp = [];
data.map(i => {
i.map(j => {
temp.push(j.text);
});
});
this.synonyms = sortBy(uniq(temp));
}
// function to handle error responses
public handleError(error)
{
if(error.status === 404 || error.status === 414){
this.toastr.error('Try again with valid word');
}
else if(error.status === 403){
this.toastr.error(`Invalid credentials`,`Please enter correct details`);
}
else if(error.status === 414){
this.toastr.error(`Your word is too long`,`Please reduce the length of the word`);
}
else if(error.status === 500){
this.toastr.warning(`Something is broken`,`Please contact developer`);
}
else if(error.status === 502){
this.toastr.info(`Oxford Dictionaries API is down or being upgraded`,`Bad Gateway`);
}
else if(error.status === 503){
this.toastr.info(`Please try again later`, `Service Unavailable`);
}
else if(error.status === 504){
this.toastr.info(`Please try again later`, `Gateway timeout`);
}
}
}
full-view.component.html
<div class="mt-6 container-fluid">
<div class="mt-2 mb-3">
<app-search-box></app-search-box>
</div>
<div class="jumbotron z-depth-3" *ngIf="fullData">
<p> Definition of
<span class="text-secondary font-weight-bold">{{ resultObject.word }}</span> in English ({{ resultObject.language }}) </p>
<h1 class="h1 red-text text-capitalize"> {{ resultObject.word }} </h1>
<p *ngIf="wordOrigin" class="lead font-weight-normal">
<strong>Origin : </strong>
<span class="text-muted">{{wordOrigin}}</span>
</p>
<hr class="my-1">
<div *ngFor="let lexicalEntry of residueData">
<h4 class="blue-text h4-responsive">
<u>{{lexicalEntry.lexicalCategory}}</u> :
</h4>
<h6 class="font-weight-normal" *ngIf="lexicalEntry.pronunciations.length > 0">Pronunciations :
<span *ngFor="let pronunciations of lexicalEntry.pronunciations">
<span> {{ pronunciations.phoneticSpelling }}
<audio #audio [src]="pronunciations.audioFile"></audio>
<a (click)=play(audio)>
<i class="fa fa-volume-up" aria-hidden="true"></i>
</a>
</span>
</span>
</h6>
<p> Definitions :</p>
<span *ngFor="let sense of lexicalEntry.entries['0'].senses">
<p>
<i class="fa fa-arrow-right indigo-text" aria-hidden="true"></i> {{ sense.definitions }} </p>
</span>
<button class="btn btn-outline-secondary btn-rounded btn-sm waves-light" type="button" (click)="test.toggle()" (click)="show = !show"
mdbWavesEffect>Show More
<i class="fa ml-1" [ngClass]="show ? 'fa-minus' : 'fa-plus'" aria-hidden="true"></i>
</button>
<div class=" bg-vio p-2" [mdbCollapse]="isCollapsed" #test="bs-collapse">
<div *ngIf="lexicalEntry.entries['0'].senses">
<span>Examples :</span>
<ul *ngFor="let senses of lexicalEntry.entries['0'].senses">
<li #eg *ngFor="let example of senses.examples"> {{ example.text }} </li>
</ul>
</div>
</div>
<hr class="dropdown-divider m-2">
</div>
<div class="card pink darken-1 z-depth-2" *ngIf="varientforms.length > 0">
<div class="card-body">
<span class="white-text mb-0">
<span>Variant Forms : {{ varientforms }}</span>
</span>
</div>
</div>
<div class="card pink darken-1 z-depth-2 mt-2" *ngIf="notes.length > 0">
<div class="card-body">
<span class="white-text mb-0">
<span>Notes : </span>
<ul class="list-group">
<li class="list-group-item black-text" *ngFor="let note of notes">
<span class=" font-weight-bold blue-text">{{note.type | uppercase}} :</span>
<span> {{note.text}}</span>
</li>
</ul>
</span>
</div>
</div>
<hr class="dropdown-divider m-2" *ngIf="synonyms.length > 0">
<div class="row">
<div class="col-12" *ngIf="synonyms.length > 0">
<button class="btn btn-outline-primary btn-rounded btn-sm waves-light text-center" type="button" (click)="syn1.toggle()"
(click)="show = !show" mdbWavesEffect>
synonyms
<i class="fa ml-1" [ngClass]="show ? 'fa-minus' : 'fa-plus'" aria-hidden="true"></i>
</button>
<div class="card indigo z-depth-2" [mdbCollapse]="isCollapsed" #syn1="bs-collapse">
<div class="card-body">
<ul class="list-inline white-text mb-0">
<li class="list-inline-item" *ngFor="let syn of synonyms;let last=last"> {{syn}}{{last ? ' ' : ', '}} </li>
</ul>
</div>
</div>
</div>
<hr class="dropdown-divider m-2" *ngIf="antonyms.length > 0">
<div class="col-12" *ngIf="antonyms.length > 0">
<button class="btn btn-outline-primary btn-rounded btn-sm waves-light text-center" type="button" (click)="ant.toggle()" (click)="show = !show"
mdbWavesEffect>
antonyms
<i class="fa ml-1" [ngClass]="show ? 'fa-minus' : 'fa-plus'" aria-hidden="true"></i>
</button>
<div class="card indigo z-depth-2" [mdbCollapse]="isCollapsed" #ant="bs-collapse">
<div class="card-body">
<ul class="list-inline white-text mb-0">
<li class="list-inline-item" *ngFor="let syn of antonyms;let last=last"> {{syn}}{{last ? ' ' : ', '}} </li>
</ul>
</div>
</div>
</div>
</div>
</div>
So I'm sending a movie object from a parent to a child component. I use #Input from the core module to do this.
Now, I am able to use this data inside the template to display my view, but when I try to access it in for example editReview() then this.movie returns undefined.
Does anyone know how to fix this?
Child Component: the console.log returns undefined
export class ReviewListComponent implements OnInit {
#Input('movie') movie;
constructor(private reviewService: ReviewService){}
editReview() {
console.log(this.movie);
}
}
Child Component Template: I am able to display my name and the content of the review. What's not working unfortunately is when I click the button it says that this.movie is undefined
<ul class="list-group">
<li *ngFor="let review of movie?.reviews" class="list-group-item">
<p>{{review.user.firstName}} {{ review.user.lastName}}</p>
<hr>
<p>{{review.content}}</p>
</li>
</ul>
<button (click)="editReview()">Button</button>
Edit: I added the components and templates of the two parent components: The first one is the movie component where I retrieve the movie object asynchronously, the second is the review component which contains the review-list component that I had already posted above
movie.component.ts
import {Component, OnInit} from "#angular/core";
import { MovieService} from "./movie.service";
import {ActivatedRoute} from "#angular/router";
#Component({
selector: 'app-movie',
templateUrl: './movie.component.html',
styleUrls: ['./movie.component.css']
})
export class MovieComponent implements OnInit{
rating;
movie;
movieId;
constructor(private movieService: MovieService, private route: ActivatedRoute){
}
checkRating(){
return typeof this.rating === 'number';
}
ngOnInit(){
this.route.params.subscribe(params => {
this.movieId = params.id;
var userId = localStorage.getItem('userId');
this.movieService.getMovie(this.movieId)
.subscribe(movie => {
this.movie = movie.obj;
this.movie.averageRating = Number(this.movie.averageRating);
console.log(this.movie);
});
if (userId){
this.movieService.getRating(userId, this.movieId)
.subscribe( result => {
this.rating = result.rating;
})
}
});
}
addRating(score) {
var userId = localStorage.getItem('userId');
this.movieService.addRating(score, userId, this.movie._id)
.subscribe(data => {
this.rating = data.obj.rating;
});
}
}
movie.component.html: this gives movie object to review.component through element app-review
<div class="container">
<div class="col col-md-4 col-md-offset-2">
<img [src]="movie?.pictureUrl" class="img-responsive" alt="hello">
</div>
<div class=" col col-md-6">
<h2>{{movie?.title}} ({{movie?.year}})</h2>
<h3>Average Score:</h3>
<span class="glyphicon glyphicon-star"></span><span><h1>{{movie?.averageRating}}</h1></span><br>
<h3>My Score:</h3>
<div class="row">
<div class="col">
<div *ngIf="!checkRating()" ngbDropdown class="d-inline-block">
<button class="btn btn-outline-primary" ngbDropdownToggle>Rate The Movie!</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button value="1" (click)="addRating(1)" class="dropdown-item">1</button>
<button value="2" (click)="addRating(2)" class="dropdown-item">2</button>
<button value="3" (click)="addRating(3)" class="dropdown-item">3</button>
<button value="4" (click)="addRating(4)" class="dropdown-item">4</button>
<button value="5" (click)="addRating(5)" class="dropdown-item">5</button>
</div>
</div>
<div *ngIf="checkRating()">
<h1>{{rating}}</h1>
</div>
</div>
</div>
<h4>{{movie?.director}}</h4>
<h4>{{movie?.actors}}</h4>
<h4>{{movie?.country}}</h4>
<p> {{movie?.description}}</p>
</div>
</div>
<hr class="col-md-8 col-md-offset-2">
<div class="col-md-8 col-md-offset-2">
<ng-container *ngIf="!!movie">
<app-review [movie]="movie"></app-review>
</ng-container>
</div>
review.component.ts
import {Component, ElementRef, Input, OnInit, ViewChild} from "#angular/core";
import {NgForm} from "#angular/forms";
import {ReviewService} from "./review.service";
import {ActivatedRoute} from "#angular/router";
#Component({
selector: 'app-review',
templateUrl: './review.component.html',
styleUrls: ['./review.component.css']
})
export class ReviewComponent implements OnInit{
movieId;
#Input('movie') movie;
#ViewChild('textArea') textArea;
constructor(private reviewService: ReviewService, private route: ActivatedRoute){}
ngOnInit(){
this.route.params.subscribe( params => {
this.movieId = params.id
});
};
onClear(form: NgForm) {
form.resetForm()
}
onSubmit(form: NgForm) {
let content = form.value.review;
let userId = localStorage.getItem('userId');
let movieId = this.movieId;
this.reviewService.addReview(content, userId, movieId)
.subscribe( result => {
console.log(result.obj);
this.movie.reviews.unshift(result.obj);
form.resetForm()
})
}
}
review.component.html: this gives movie object to the review-list component through app-review-list
<ng-container *ngIf="!!movie">
<app-review-list [movie]="movie"></app-review-list>
</ng-container>
<hr>
<form ngNativeValidate (ngSubmit)="onSubmit(f)" #f="ngForm">
<div class="form-group">
<label for="review">Write A Review:</label>
<textarea
#textArea
(keyup.enter)="onSubmit(f)"
rows="4"
cols="50"
type="text"
id="review"
ngModel
class="form-control"
name="review"
required
></textarea>
</div>
<button type="button" class="btn btn-danger" (click)="onClear(f)">Clear</button>
<button class="btn btn-primary" type="submit">Save</button>
</form>
you can access it in ngOnInit() just implement OnInit interface