As i mentioned in yhe title i have problem with retrieving data from nested json array and assigning it to datasource of mat table . So i hope there a person who faced similar problem as I and can help me. Below I paste my code :
COURSE.SERVICE.ts
import { Injectable } from '#angular/core';
import { environment } from 'src/environments/environment';
import { HttpClient } from '#angular/common/http';
import { Course } from '../_models/course';
import { CourseEnrolment } from '../_models/available_exams/course_enrolment';
#Injectable({
providedIn: 'root'
})
export class CourseService {
baseUrl = environment.apiUrl;
getAllExams(id) {
return this.http.get<CourseEnrolment[]>(this.baseUrl + 'allexams/' + id);
}
AVAILABLE-EXAM.COMPONENT.ts
import { Component, OnInit, ViewChild } from '#angular/core';
import { MatTableDataSource, MatPaginator, MatSort } from '#angular/material';
import { CourseService } from 'src/app/_services/course.service';
import { AuthService } from 'src/app/_services/auth.service';
import { CourseEnrolmentsExam } from 'src/app/_models/courseEnrolmentsExam';
import { Exam } from 'src/app/_models/available_exams/exam';
import { Users } from 'src/app/_models/available_exams/users';
import { CourseEnrolment } from 'src/app/_models/available_exams/course_enrolment';
import { ExamList } from 'src/app/_models/available_exams/exam_list';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-available-exams',
templateUrl: './available-exams.component.html',
styleUrls: ['./available-exams.component.scss']
})
export class AvailableExamsComponent implements OnInit {
dataSourceExam = new MatTableDataSource();
exams: Exam[];
courseEnrolments: CourseEnrolment[];
displayedColumns = [
'id',
'name',
'subject',
'timeLimit',
'examResult'
];
#ViewChild(MatPaginator) paginator: MatPaginator;
#ViewChild(MatSort) sort: MatSort;
constructor(
private courseService: CourseService,
private authService: AuthService,
private httpService: HttpClient
) {}
ngOnInit() {
this.courseService
.getAllExams(this.authService.decodedToken.nameid)
.subscribe(result => {
this.courseEnrolments = result['courseEnrolments'];
console.log(result['courseEnrolments']);
console.log(result['subject']);
console.log(result['courseEnrolments.subject']);
console.log(result['firstName']);
console.log(result['subject']);
console.log(result['courseEnrolments.subject']);
console.log(result['courseEnrolments']);
console.log(result['exams']);
console.log(result['courseEnrolments.subject']);
console.log(result['firstName']);
if (!result) {
return;
}
this.dataSourceExam = new MatTableDataSource(this.courseEnrolments);
this.dataSourceExam.paginator = this.paginator;
this.dataSourceExam.sort = this.sort;
});
}
applyFilter(filterValue: string) {
filterValue = filterValue.trim();
filterValue = filterValue.toLowerCase();
this.dataSourceExam.filter = filterValue;
}
handleClick(event: Event) {
console.log('‘Click’', event);
}
}
Here is my json response from webapi :
try setting the data source like this, add .data after your data source :
this.dataSourceExam.data = result['courseEnrolments'];
Related
Why my movie component is not updating after fetching the data. Also not saving the data if I have added a new movie or made changes in existing movies.
It is just saving and fetching the data which is written in movie.service.ts file. Also the fetched data is not rendering on the movie component.
Data-storage.service
import { Injectable } from '#angular/core';
import { MovieService } from '../movies/movies.service';
import { HttpClient, HttpHeaders, HttpParams, HttpRequest } from '#angular/common/http';
import { Movie } from '../movies/movie.model';
import { Observable} from 'rxjs';
import { map } from 'rxjs/operators';
// import 'rxjs/Rx';
// import 'rxjs/Rx';
#Injectable({
providedIn: 'root'
})
export class DataStorageService {
constructor(private httpClient: HttpClient,
private movieService: MovieService,) { }
storeMovies(): Observable<any> {
const req = new HttpRequest('PUT', 'https://moviepedia-4211a.firebaseio.com/movies.json', this.movieService.getMovies(), {reportProgress: true});
return this.httpClient.request(req);
}
getMovies() {
this.httpClient.get<Movie[]>('https://moviepedia-4211a.firebaseio.com/movies.json', {
observe: 'body',
responseType: 'json'
})
.pipe(map(
(movies) => {
console.log(movies);
return movies;
}
))
.subscribe(
(movies: Movie[]) => {
this.movieService.setMovies(movies);
}
);
}
}
movie.service.ts :
import { Injectable } from '#angular/core';
import {Subject} from 'rxjs';
import { Movie } from './movie.model';
#Injectable()
export class MovieService {
moviesChanged = new Subject<Movie[]>();
private movies: Movie[] = [
new Movie(
'Movie test', 'Movie details', 'https://s18672.pcdn.co/wp-content/uploads/2018/01/Movie-300x200.jpg'
),
new Movie(
'Movie test 2', 'Movie details 2', 'https://s18672.pcdn.co/wp-content/uploads/2018/01/Movie-300x200.jpg'
),
new Movie(
'Movie test 2', 'Movie details 3', 'https://s18672.pcdn.co/wp-content/uploads/2018/01/Movie-300x200.jpg'
)
];
constructor(){}
getMovie(index: number) {
return this.movies[index];
}
getMovies() {
return this.movies.slice();
}
addMovie(movie: Movie) {
this.movies.push(movie);
this.moviesChanged.next(this.movies.slice());
}
updateMovie(index: number, newMovie: Movie) {
this.movies[index] = newMovie;
this.moviesChanged.next(this.movies.slice());
}
deleteMovie(index: number) {
this.movies.splice(index, 1);
this.moviesChanged.next(this.movies.slice());
}
setMovies(movies: Movie[]) {
this.movies = movies;
this.moviesChanged.next(this.movies.slice());
}
}
movie.model.ts
export class Movie {
public name: string;
public description: string;
public imagePath: string;
constructor(name: string, description: string, imagePath: string) {
this.name = name;
this.description = description;
this.imagePath = imagePath;
}
}
movie.component :
import { Component, OnInit, EventEmitter, Output, OnDestroy } from '#angular/core';
import { Movie } from '../movie.model'
import { MovieService } from '../movies.service';
import { Router, ActivatedRoute } from '#angular/router';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-movie-list',
templateUrl: './movie-list.component.html',
styleUrls: ['./movie-list.component.css']
})
export class MovieListComponent implements OnInit, OnDestroy {
subscription: Subscription;
movies: Movie[] = [];
constructor(private movieService: MovieService,
private router: Router,
private route: ActivatedRoute) { }
ngOnInit() {
this.subscription = this.movieService.moviesChanged
.subscribe(
(movies: Movie[]) => {
this.movies = movies;
}
);
this.movies = this.movieService.getMovies();
}
onNewMovie() {
this.router.navigate(['new'], {relativeTo: this.route});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
What can I do save and fetch data which will render on page.
I think the problem with your PUT request is that the url you used expects json but you are sending Movie object. you should send and receive json to this url.
wish it helps ...
I am trying to get back only a certain content_type with by building a dynamic contentful service that could be re-used for different methods in the JS SDK. The filter on the content type - 'product' or sys.id is not working.
Service File:
import { Injectable } from '#angular/core';
import { createClient, Entry} from 'contentful';
import { environment } from '../environments/environment';
import { Observable, from} from 'rxjs';
import { map } from 'rxjs/operators';
export interface QueryObj {
content_type: string;
select?: string;
}
#Injectable({
providedIn: 'root'
})
export class ContentfulService {
private client = createClient({
space: environment.contentful.spaceId,
accessToken: environment.contentful.token
});
queries: QueryObj[];
constructor() {
}
getContentfulEntries(): Observable<QueryObj[]> {
const contentEntries = this.client.getEntries(this.queries);
return from(contentEntries).pipe
(map ((res: any) => res.items));
}
Controller File:
import {Component, OnDestroy, OnInit} from '#angular/core';
import {ContentfulService} from '../services/contentful.service';
import { Subscription} from 'rxjs';
import { QueryObj} from '../services/contentful.service';
#Component({
selector: 'r-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
title = 'Setup Environment';
private entriesSubscription: Subscription;
queries: QueryObj[] = [
{
content_type: 'product',
select: 'sys.id'
}
];
constructor(private contentfulService: ContentfulService) {}
ngOnInit() {
this.entriesSubscription = this.contentfulService.getContentfulEntries()
.subscribe((queries: QueryObj[]) => {
this.queries = queries;
console.log(this.queries);
});
}
ngOnDestroy () {
this.entriesSubscription.unsubscribe();
}
}
I'm not too familiar with Angular and the related TypeScript but are you passing an Array to getEntries? getEntries excepts only a query object. :)
getContentfulEntries(): Observable<QueryObj[]> {
// this.queries is a collection or? 👇🏻
const contentEntries = this.client.getEntries(this.queries);
return from(contentEntries).pipe
(map ((res: any) => res.items));
}
I don't get i, how to communicate between components and services.. :(
I have read and tried a lot about even if some examples somehow work, I do not understand why (?)
what I want to achieve:
I have one parent and two child-components:
dashboard
toolbar
graph
in the toolbar-component I have a searchfield, which gets it's result from a external source (works via service).. when the result arrives, I need to trigger the updateGraph()-Method in the graph-component
toolbar.component.ts
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
import { FormControl } from '#angular/forms';
import { WebsocketsService } from '../../../services/websockets/websockets.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.scss'],
providers: [WebsocketsService, DataService]
})
export class ToolbarComponent implements OnInit {
#Output() newGraphData: EventEmitter<boolean> = new EventEmitter();
searchField: FormControl;
search: string;
private isNewGraph = false;
constructor(private _websocketsService: WebsocketsService, private _dataService: DataService) {
}
ngOnInit() {
this.searchField = new FormControl();
this.searchField.valueChanges
.subscribe(term => {
this.search = term;
});
}
private applySearch() {
const res = this._websocketsService.sendQuery(this.search);
this._dataService.setGraphData(res);
this.newGraphData.emit(true);
this.search = '';
this.searchField.reset();
}
}
graph-component.ts
import { Component, OnInit} from '#angular/core';
import { HttpService } from '../../../services/http/http.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'graph',
templateUrl: './graph.component.html',
styleUrls: ['./graph.component.scss'],
providers: [HttpService, DataService]
})
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
ngOnInit() {
}
public renderResult() {
console.log( this._dataService.getGraphData() );
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class DataService {
private graphData: Subject<string> = new Subject<string>();
public setGraphData(data) {
this.graphData.next( data );
}
public getGraphData() {
return this.graphData;
}
constructor() { }
}
I simply want ´renderResult()´to be executed after the searchresult has been written to ´graphData´. please help i am confused.
If I understand, you want communication between components and service.
A[component] (make a information) -----(notification)-----> B[service] ----(send)----> C[component] (consume the information)
It's correct? Let's go.
You need create a subscription of graphData(data.service.ts) in GraphComponent.
import { Subscription } from 'rxjs/Subscription';
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
private subscription: Subscription;
ngOnInit() {
this.subscription = this._dataService.getGraphData().asObservable().subscribe((data) => {
console.log(data);
});
}
}
Look here to help you.
http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject
Short answer, I think you need to subscribe to the getGraphData subject, something like this (NOT RECOMMENDED):
public renderResult() {
this._dataService.getGraphData().subscribe(d => {
console.log(d)
});
}
It is not recommended as per the lead of RxJS says: https://medium.com/#benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
Better answer, create an observable in your service and subscribe to that instead.
data.service.ts
graphObservable = this.graphData.asObservable();
graph-component.ts
public renderResult() {
this._dataService.graphObservable().subscribe(d => {
console.log(d)
});
}
Im trying a simple profile app, and all the sudden Im getting error TS2554
ERROR in /app/components/profile/profile.component.ts(25,3): error TS2554: Expected 1 arguments, but got 0.
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { FlashMessagesService } from 'angular2-flash-messages';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object;
constructor(
private auth: AuthService,
private flashMsg: FlashMessagesService,
private router: Router
) {
}
ngOnInit() {
this.auth.getProfile().subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
}
}
auth.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable()
export class AuthService {
authToken: any;
user: any;
constructor(
private http: Http
) {
}
getProfile(user) {
let headers = new Headers();
this.loadToken();
headers.append('Authorization', this.authToken);
headers.append('Content-Type','application/json');
return this.http.get('http://localhost:3000/users/profile', {headers:headers})
.map(res => res.json());
}
loadToken() {
const token = localStorage.getItem('id_token');
this.authToken = token;
}
}
Your getProfile is expecting an argument named user but you are not passing it from the component
You need to pass an argument as follows,
this.auth.getProfile(user).subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
or if you don't need an argument , remove it from your service method.
I'm trying to learn #ngrx/store with Angular 2 - RC4. I've got a service that I'm calling from my component, and I'm just trying to console log a list of vehicles whenever it changes.
Vehicle
export interface Vehicle {
...stuff...
}
Vehicle Reducer
import { Vehicle } from '../models';
export interface VehiclesState {
vins: string[];
vehicles: { [vin: string]: Vehicle };
}
const initialState: VehiclesState = {
vins: [],
vehicles: {}
}
export const vehicles = (state: any = initialState, {type, payload}) => {
switch (type) {
case 'LOAD_VEHICLES':
const vehicles: Vehicle[] = payload;
const newVehicles = vehicles.filter(vehicle => !state.vehicles[vehicle.vin]);
const newVehicleVins = newVehicles.map(vehicle => vehicle.vin);
const newVehiclesList = newVehicles.reduce((vehicles: { [vin: string]: Vehicle }, vehicle: Vehicle) => {
return mergeObjects(vehicles, {
[vehicle.vin]: vehicle
});
}, {});
return {
vins: [...state.vins, ...newVehicleVins],
vehicles: mergeObjects({}, state.vehicles, newVehiclesList)
}
}
}
main.ts
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from '#angular/core';
import { HTTP_PROVIDERS } from '#angular/http';
import { provideStore } from '#ngrx/store'
import { AppComponent, environment } from './app/';
import { vehicles } from './app/shared/reducers/vehicles'
if (environment.production) {
enableProdMode();
}
bootstrap(AppComponent,
[
HTTP_PROVIDERS,
provideStore(vehicles, {
vins: [],
vehicles: {}
})
]
);
VehiclesService
import {Http, Headers} from '#angular/http';
import {Injectable} from '#angular/core';
import {Store} from '#ngrx/store';
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/map';
import {Vehicle} from '../models/vehicle.model';
const BASE_URL = 'http://localhost:3000/vehicles/';
const HEADER = { headers: new Headers({ 'Content-Type': 'application/json' }) };
#Injectable()
export class VehiclesService {
vehicles: Observable<Array<Vehicle>>;
constructor(private http: Http, private store: Store<Vehicle[]>) {
this.vehicles = this.store.select('vehicles');
}
loadVehicles() {
this.http.get(BASE_URL)
.map(res => res.json())
.map(payload => ({ type: 'LOAD_VEHICLES', payload: payload }))
.subscribe(action => this.store.dispatch(action));
}
}
AppComponent
import { Component, OnInit, Input } from '#angular/core';
import { Observable } from "rxjs/Observable";
import { Store } from '#ngrx/store';
import { Vehicle, VehiclesService } from './shared';
#Component({
moduleId: module.id,
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
providers: [VehiclesService]
})
export class AppComponent implements OnInit{
title = 'app works!';
vehicles: Observable<Vehicle[]>;
constructor(private vehiclesService: VehiclesService) {
this.vehicles = vehiclesService.vehicles;
vehiclesService.loadVehicles();
}
ngOnInit() {
console.log(this.vehicles)
this.vehicles.subscribe(vehicles => console.log(vehicles));
}
}
But when it runs, I get a TypeError TypeError: Cannot read property 'vehicles' of undefined
The first console.log returns a Store object, but the subscription seems to fail.
Any idea what I'm doing wrong?
You need to change your provideStore to be provideStore({vehicles: vehicles}).
In my case, the error was the use of different names in the reducer object and the interface:
import { LeaderboardParticipant } from './...';
import * as fromLeaderboard from './...';
export interface IState {
leaderboard: fromLeaderboard.IState;
}
export const reducers = {
leaderboard: fromLeaderboard.reducer // I used to have 'search' instead of 'leaderboard'
};
export function selectChallengeId(state: IState): string {
return state.leaderboard.challengeId;
}
export function selectFilterTerms(state: IState): string {
return state.leaderboard.filterTerms;
}
There is not enough information: template app.component.ts is missing in the question.
However, check how do you access the member vehicles in your template. Try operator ?. instead of .. Something like:
{{ yourObject?.vehicles }}
When you declare your Store (Store), I think the right way is:
constructor(private http: Http, private store: Store<VehicleState>)