I have received a JSON object (i think) from my HTTP Web Service but struggling to pull out the strings.
https://jsonplaceholder.typicode.com/posts/1 gives me
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
My code:
I set up a service:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class MyNewServiceService {
constructor(private http: Http) {}
getHTTP() {
return this.http.get('https://jsonplaceholder.typicode.com/posts/1').map(
response => response.json());
}
}
Called it from my app.component, trying and failing tooutput to the screen via title.
import { Component} from '#angular/core';
import { MyNewServiceService } from './my-new-service.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [MyNewServiceService]
})
export class AppComponent {
title = 'app works!';
constructor(MyNewServiceService: MyNewServiceService){
MyNewServiceService.getHTTP()
.subscribe(
JSONtitle => this.title = JSONtitle,
error => console.error('Error: ' + error),
() => console.log('Completed!')
);
}
}
I ended up outputting [object Object] to the screen.
I tried outputting it to console but got 'undefined' assuming service hadn't finished yet in angular2 lifecycle. So i created a new class and tried to cast with it but no luck
export class JsonResponseClass {
constructor(
public userid:number,
public id:string,
public title:string,
public body:string
)
{}
}
Template is simple...
<h1>
{{title}}
</h1>
How do i get my strings from the json?
You're returning response body as a mapping result from the service. Suiting the situation, you can access needed properties in your component as follows:
constructor(MyNewServiceService: MyNewServiceService){
MyNewServiceService.getHTTP()
.subscribe(
resBody => this.title = resBody.title,
error => console.error('Error: ' + error),
() => console.log('Completed!')
);
}
Btw, convention tells us to keep instance variables camelCased, so you can differentiate instance from the class itself:
constructor(private myNewServiceService: MyNewServiceService){
myNewServiceService.getHTTP()
.subscribe(
resBody => this.title = resBody.title,
error => console.error('Error: ' + error),
() => console.log('Completed!')
);
}
Related
Following is a very simple Nativescript Vue example from the starter. As it appears below, it displays the list of 5 post titles.
So above test is good to start as long as I only use computed to return data to template. However, if I attempt to use the create() or mounted() event/lifecycle hooks to set the posts property, I get nothing in the display. The console.log lines never display the message so they're never firing. Why not?
Also, if I try to use the fetch (call my fetchPosts() method) to pull posts from the test restapi, I get no data and console.error shows nothing. Why not?
<template>
<Page class="page">
<ActionBar class="action-bar">
<Label class="action-bar-title" text="Home"></Label>
</ActionBar>
<ScrollView>
<StackLayout class="home-panel">
<!--Add your page content here-->
<Label v-for="post in posts" :text="post.title" :key="post.id"/>
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
export default {
// posts: [],
// create() {
// console.log("create event fired");
// this.posts = this.getPosts();
// },
// mounted() {
// console.log("mounted event fired");
// this.posts = this.getPosts();
// },
computed: {
posts() {
//return this.fetchPosts();
return this.getPosts();
}
},
methods: {
fetchPosts() {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then(res => {
console.log("fetch response", res);
return res;
})
.catch(err => {
console.error(err);
return [{ id: 0, title: "Error: " + err }];
});
},
getPosts() {
return [
{
userId: 1,
id: 1,
title:
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
body:
"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
userId: 1,
id: 2,
title: "qui est esse",
body:
"est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{
userId: 1,
id: 3,
title: "ea molestias quasi exercitationem repellat qui ipsa sit aut",
body:
"et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
},
{
userId: 1,
id: 4,
title: "eum et est occaecati",
body:
"ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit"
},
{
userId: 1,
id: 5,
title: "nesciunt quas odio",
body:
"repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque"
}
];
}
}
};
</script>
<style scoped lang="scss">
// Start custom common variables
#import "../app-variables";
// End custom common variables
// Custom styles
.fa {
color: $accent-dark;
}
.info {
font-size: 20;
}
</style>
There are some problems I identify in your code:
the correct lifecycle hook name is created, not create
the posts list should be inside data:
data() {
return {
posts: []
};
},
the fetchPosts() doesn't return anything, but you expect to return the posts. You have to set the posts inside then callback:
fetchPosts() {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then(res => this.posts = res)
.catch(err => console.error(err));
}
and this because fetch returns a Promise. Q: How to return data from Promise? A: You can't!
Full code:
<script>
export default {
data() {
return {
posts: [{
title: '1 Title',
id: 1
}]
};
},
created() {
console.log("create event fired");
this.posts = this.fetchPosts();
},
mounted() {
console.log("mounted event fired");
this.fetchPosts();
},
methods: {
fetchPosts() {
return fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then(res => this.posts = res);
}
}
};
</script>
The code was tested here.
For me it is also not firing, instead of using mounted or created I added a loaded event on the page element
<template>
<page #loaded="startMyApp">
......
</template>
<script>
export default {
data() {
return {
}
},
methods: {
startMyApp()
{
}
}
</script>
I'm attempting to create a Wordpress theme compatible with 4.8.x that will render single posts and list of posts as per [this tutorial]:1
When I run the test script, I receive the following errors:
ERROR in C:/MyTheme/src/app/posts/post-list/post-list.component.spec.ts
(9,25): Expected 2 arguments, but got 0.
ERROR in C:/MyTheme/src/app/posts/post-single/post-single.component.spec.ts
(8,25): Expected 2 arguments, but got 0.
The code for both components is very similar and calls into the PostsService which is defined as:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Post } from './post';
import { environment} from '../../environments/environment';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class PostsService {
private _wpBase = environment.wpBase;
constructor(private http: HttpClient) { }
getPosts():Observable<Post[]> {
return this.http.get<Post[]>(this._wpBase + 'posts');
}
getPost(slug: string): Observable<Post[]> {
return this.http.get<Post[]>(this._wpBase + 'posts?slug=${slug}');
}
}
My post-list-component includes the following:
import { Component, OnInit } from '#angular/core';
import { Post } from '../post';
import { PostsService} from '../posts.service';
import { HttpErrorResponse } from '#angular/common/http';
import {Router} from '#angular/router';
#Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css'],
providers: [PostsService]
})
export class PostListComponent implements OnInit {
posts: Post[];
constructor( private postsService: PostsService, private router: Router ){}
ngOnInit() {
this.postsService.getPosts().subscribe(
(posts: Post[]) => this.posts = posts,
(err: HttpErrorResponse) => err.error instanceof Error ?
console.log('An error has occurred:',
err.error.message):console.log('Backend returned code $(err.status),
body was: ${err.error}'));
}
selectPost(slug) {
this.router.navigate([slug]);
}
}
The error is thrown in the following post.list.component.spec.ts:
/* tslint:disable:no-unused-variable */
import { TestBed, async } from '#angular/core/testing';
import { PostListComponent } from './post-list.component';
import {Router} from "#angular/router";
describe('Component: PostList', () => {
it('should create an instance', () => {
let component = new PostListComponent();
expect(component).toBeTruthy();
});
});
I am not sure how to resolve the errors. It seems to me that PostLisComponent() needs to be passed 2 arguments as per the error, but it's not clear what arguments should be passed. Can anyone assist me in better understanding how to resolve the errors?
its because the constructor use TestBed
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { PostListComponent } from './post-list.component';
describe('PostListComponent ', () => {
let component: PostListComponent ;
let fixture: ComponentFixture<PostListComponent >;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PostListComponent ]
})
.compileComponents();}));
beforeEach(() => {
fixture = TestBed.createComponent(PostListComponent );
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create an instance', () => {
expect(component).toBeTruthy();
});
});
From Angular's Testing Guide in regards to the TestBed, and why it would fit such a scenario:
TestBed is the first and most important of the Angular testing
utilities ... In effect, you detach the tested component from its own
application module and re-attach it to a dynamically-constructed
Angular test module tailored specifically for this battery of tests.
Right now, you're statically constructing instead of dynamically constructing using the TestBed, which is causing the error since the constructor of the PostListComponent contains two parameters which would be required to be filled in case of static constructing.
I am currently building an Angular2 application accessing an MVC web API i have built. However, it does not seem to retrieve any data. I am obviously missing something but i am not sure what.
I know that the URL i am using works along with the headers as i am able to retrieve the data correctly through fiddler.
My repack.service.ts is as follows:
import { Injectable } from '#angular/core';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { RepackIndex } from './RepackIndex';
#Injectable()
export class RepackService{
private baseUrl = 'https://localhost:44321/api/Repack/All';
private headers = new Headers({'Content-Type': 'application/json'});
constructor(private http: Http) { }
getAllRepacks(): Promise<RepackIndex[]>{
var data = this.http.get(this.baseUrl)
.toPromise()
.then(response => response.json().data as RepackIndex[])
.catch(this.handleError);
return data;
}
private handleError(error: any): Promise<any>{
console.error("An error occured in repack.service", error);
return Promise.reject(error.message || error);
}
}
And this is my component:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { RepackIndex } from './repackIndex';
import { RepackService } from './repack.service';
#Component({
selector: 'index',
templateUrl: 'app/index.component.html',
providers: [RepackService]
})
export class IndexComponent implements OnInit{
repacks: RepackIndex[];
selectedRepack: RepackIndex;
constructor(private router: Router, private repackService: RepackService) { }
onSelect(repack: RepackIndex): void{
this.selectedRepack = repack;
}
getRepacks(): void{
this.repackService.getAllRepacks().then(repacks => this.repacks = repacks);
}
ngOnInit(): void{
this.getRepacks();
}
}
I have tried putting in a breakpoint and adding a console.log line but no data is returned to the component.
I am fairly new to Angular2 so any help would be greatly appreciated.
Thanks,
Right I have managed to get it to work by using an observable rather than a promise.
My service method now looks like this:
public GetAll = (): Observable<RepackIndex[]> => {
return this.http.get(this.baseUrl)
.map((response: Response) => <RepackIndex[]>response.json())
.catch(this.handleError);
}
And my Component call now looks like this:
getRepacks(): void{
this.repackService.GetAll()
.subscribe((data:RepackIndex[]) => this.repacks = data,
error => console.log(error),
() => console.log('Get All repacks complete'));
}
I found the answer here
Hope this helps someone else
Hi I'm having trouble navigating with a browsers back/forward buttons. It only occurs when navigating between routes that share the same component via route parameters.
I have an application that lists lots of animals. Every time I navigate to example.com/animal/cat or example.com/animal/dog, a Http Get request is made to a restful Api which returns the relevant data. If I navigate to /animal/cat then to /animal/dog everything seems ok. The problem starts when I click on the browsers Back Button to go back to /animal/cat. Instead of loading the data for the cat, the URL changes but the data is still listed for the dog. I'm using router 3.0.0-beta.2 with RC4.
heres my animals details.component page:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ROUTER_DIRECTIVES, ActivatedRoute, Router } from '#angular/router';
import { Response } from '#angular/http';
import { Subscription } from 'rxjs/Rx';
import { DataService } from '../../data.service';
#Component({
moduleId: module.id,
selector: 'animal-details',
templateUrl: 'animal-details.component.html',
styleUrls: ['animal-details.component.css'],
directives:[ROUTER_DIRECTIVES]
})
export class AnimalDetailsComponent implements OnInit, OnDestroy {
constructor(private activatedRoute: ActivatedRoute, private dataService: DataService, private router: Router) {
}
private subscription: Subscription;
animal = {};
link: string;
table = 'animal/';
private url = '?related=family_by_link';
ngOnInit() {
this.subscription = this.activatedRoute.params.subscribe(
(param: any) => this.link = param['link']
);
this.dataService.getData(this.table, this.link, this.url)
.map((response: Response) => response.json())
.subscribe(
(data: any) => {this.animal = data},
err => { console.log('error404') }
);
};
nextData(){
this.dataService.getData(this.table, this.link, this.url)
.map((response: Response) => response.json())
.subscribe(
(data: any) => {this.animal = data},
err => { console.log('error404') }
);
window.scroll(0,0);
};
ngOnDestroy() {
this.subscription.unsubscribe();
};
}
I use the getData() method when navigating between animals on the same component. My AnimalComponentDetails html:
<div class="row">
<div class="col-sm-6 col-md-4 col-lg-3 text-xs-center" *ngFor="let family of animal.family_by_link" (click)="nextData()">
<a [routerLink]="['/animal', family.ani_link]">
<img src="/images/animal/{{family.ani_link}}.png">
<p>{{family.name}}</p>
</a>
</div>
Heres my DataService:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Subscription, Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map'
#Injectable()
export class DataService {
constructor(private dataService: Http) {
}
urlprefix = 'http://123.123.123.123/api/v2/_table/';
api = '&api_key=7201521drgdc71';
getData(table, link, url): Observable<any> {
return this.dataService.get(this.urlprefix + table + link + url +this.api);
}
}
my app.routes:
import { provideRouter } from '#angular/router';
import { AnimalComponent } from './animal/animal.component';
import { AnimalDetailsComponent } from './animal/animal-details/animal-details.component'
{ path: '', component: HomeComponent },
{ path: 'animal', component: AnimalComponent },
{ path: 'animal/:link', component: AnimalDetailsComponent },
{ path: '**', redirectedTo: 'error404', component: Error404Component }
];
export const APP_ROUTES_PROVIDER = [
provideRouter(APP_ROUTES)
];
And my boot:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from '#angular/core';
import { AppComponent, environment } from './app/';
import { HTTP_PROVIDERS } from '#angular/http';
import { APP_ROUTES_PROVIDER } from './app/app.routes'
if (environment.production) {
enableProdMode();
}
bootstrap(AppComponent, [APP_ROUTES_PROVIDER, HTTP_PROVIDERS]);
Chrome's Network indicates when I navigate back or forward on the same component between animal/:cat and animal/:dog via browser buttons, no new Http requests are made unless I change to a different component.
This is the last bug I'm down too. Would appreciate any help, thanks!
I'trying to use the HTTP service in Angular2 and i have some concerns.
I'm taking meteo datas from openweather API and I just want to put it inside a typeScript variable (meteo: {}) and use it as i want in my template.
Here are my .ts files:
meteo.service.ts
import {Injectable} from "angular2/core";
import {Http, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";
import {MeteoComponent} from "../widgets/meteo/meteo.component";
import {Meteo} from "../widgets/meteo/meteo";
#Injectable()
export class MeteoService {
constructor(private http: Http) {}
// Nom de la ville sans accent
private _ville = 'Montreal';
// Initiales du pays
private _country = 'ca';
// Units (metric/imperial)
private _units = 'metric';
// API KEY
private _APPID = 'ewfw54f5646';
// url to get data
private _meteoUrl = 'http://api.openweathermap.org/data/2.5/weather?q='+this._ville+','+this._country+'&units='+this._units+'&APPID='+this._APPID;
getMeteo (): Observable<Meteo> {
return this.http.get(this._meteoUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || { };
}
private handleError(error: any) {
let errMsg = error.message || 'server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
}
meteo.component.ts
import {Component, OnInit, OnChanges, AfterContentInit} from "angular2/core";
import {MeteoService} from "../../services/meteo.service";
import {Meteo} from "./meteo";
#Component({
selector: 'meteo',
templateUrl: 'dev/widgets/meteo/meteo.component.html',
providers: [MeteoService]
})
export class MeteoComponent implements OnInit {
errorMessage: string;
meteo: Meteo;
// We inject the service into the constructor
constructor (private _meteoService: MeteoService) {}
// Instantiate data in the ngOnInit function to keep the constructor simple
ngOnInit() {
this.getMeteo();
}
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo = data,
error => this.errorMessage = <any>error);
}
}
meteo.ts
export class Meteo {
data: {};
}
and meteo.component.html
<span class="meteo">{{meteo | json}}°C</span>
Actually the result is the entire json object:
{
"coord": {
"lon":-73.59,
"lat":45.51
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04d"
}
],
"base":"cmc stations",
"main":{
"temp":3.96,
"pressure":1020,
"humidity":32,
"temp_min":2,
"temp_max":6.67
},
"wind":{
"speed":2.1
},
"clouds":{
"all":75
},
"dt":1461594860,
"sys":{
"type":1,
"id":3829,
"message":0.004,
"country":"CA",
"sunrise":1461577807,
"sunset":1461628497
},
"id":6077243,
"name":"Montreal",
"cod":200
}
And I would like to display just the temp field.
If you have any idea guys it's welcomed!
Thanks a lot.
You could leverage the Elvis operator since your data are loaded asynchronously:
<span class="meteo">{{meteo?.main.temp | json}}°C</span>
Try setting the data on this.meteo.data
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo.data = data,
error => this.errorMessage = <any>error);
}
and then displaying it with
<span class="meteo">{{meteo.data.main.temp}}°C</span>