I am having an issue I do not understand at all. I am fairly new angular so it may be something small but never the less.
When I try to build my project to publish it to a github page, the HTML components are failing on the build as the properties from the component do not exist. All of the errors relate to the HTML components not being away of the object properties. (Which are provided via an API service anyway!)
I have attempted to provide the minimum required code to illustrate the problem.
Error Dump:
ERROR in src\app\users\users.component.html(4,20): : Property 'queryString' does not exist on type 'UsersComponent'.
src\app\users\users.component.html(9,7): : Property 'queryString' does not exist on type 'UsersComponent'.
src\app\users\users.component.html(4,20): : Property 'queryString' does not exist on type 'UsersComponent'.
src\app\details\details.component.html(1,5): : Property 'name' does not exist on type 'Object'.
src\app\details\details.component.html(4,32): : Property 'RunnerName' does not exist on type 'Object'.
src\app\details\details.component.html(5,29): : Property 'LastTime' does not exist on type 'Object'.
src\app\details\details.component.html(6,29): : Property 'LastDistance' does not exist on type 'Object'.
src\app\details\details.component.html(7,29): : Property 'date' does not exist on type 'Object'.
user.component.html
<h1>Runners</h1>
<div>
<input type="text" [(ngModel)]="queryString" placeholder = "Search Runner Name">
</div>
<ul>
<li *ngFor = "let user of users | filterdata: queryString : 'RunnerName' ; let i = index">
<a routerLink = "/details/{{ user.RunnerId }}">{{ user.RunnerName }}</a>
<ul>
<li><strong>Runner ID: {{ user.RunnerId }}</strong></li>
</ul>
</li>
</ul>
user.component.ts
import { Component, OnInit } from '#angular/core';
//Importing my users service, up one level in project structure from here.
import { DataService } from '../data.service';
//RXJS will hold the data which is returned from the API...
//RESEARCH RXJS
import { Observable } from 'rxjs';
import { FormsModule } from '#angular/forms';
import { Pipe, PipeTransform } from '#angular/core';
#Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
})
//Export class implenets OnInit.
export class UsersComponent implements OnInit {
//Prop which holds returned API data
//of type obect.
users: Object;
//Creating instance of the service via dependancy injection.
constructor(private data: DataService) { }
//NG on init is one of the "lifecycle hooks" for angular components.
//Code in here will be executed when the component loads for ngOnInit.
ngOnInit() {
//Executing the method which is provided by the service.
//Adding data bind via subscribe.
this.data.getUsers().subscribe(
//returning the user data via single line return function
//passing the data value into the function.
(data) => {
//assinging the data to the user object.
this.users = data
//sorting the users object by runner ID.
//this.users.sort((a,b) => a.RunnerId - b.RunnerId);
}
);
}
}
data.service.ts
import { Injectable } from '#angular/core';
//Importing te angular HTTP Client
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
//Class which exports the service to the APP.
//We will import this class into the components when required.
export class DataService {
//Utilising the HTTP client import Class
//HTTP Client request expects JSON return data as default, their is no need to parse JSON anymore.
constructor(private http: HttpClient) {}
//Custom Method to return Users collection from the web API.
getUsers(){
//single line return statement.
return this.http.get('http://rundistance.azurewebsites.net/api/RunnerService')
}
//Function to return the detail of a single user, passing in the ID prop of currently selected target of objects master layer.
getUser(userId){
//single line return statement getting target object from API.
return this.http.get('http://rundistance.azurewebsites.net/api/RunnerService/'+userId)
}
//Returning posts from API.
getPosts(){
//single line return statement.
return this.http.get('https://jsonplaceholder.typicode.com/posts')
}
}
filterdata.pipe
import { Pipe, PipeTransform } from '#angular/core';
import { DataService } from './data.service';
#Pipe({
name: 'filterdata'
})
export class FilterdataPipe implements PipeTransform {
transform(items: any[], value: string, label:string): any[] {
if (!items) return [];
if (!value) return items;
if (value == '' || value == null) return [];
return items.filter(e => e[label].toLowerCase().indexOf(value) > -1 );
}
}
Related
I know this is an extremely simple question but I have yet to find a resource solution that will work or explain in a way that makes complete sense. I'm trying to get back into Angular after many years and never used TypeScript before. Currently struggling a lot with errors and what TypeScript is actually expecting me to do.
I have an app that connects to the Open Brewery DB. I'm trying to make a details page that fetches data based on an :id URL param.
app.com/breweries, give me a list of breweries
app.com/breweries/:id, give me specific details on that brewery
I have a list component that grabs a list of Breweries. So whatever comes back gets displayed in a list.
http.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { IBrewery } from './brewery/brewery';
#Injectable({
providedIn: 'root'
})
export class HttpService {
constructor(private http: HttpClient) { }
getBreweries() {
return this.http.get('https://api.openbrewerydb.org/breweries');
}
getBrewery(id) {
return this.http.get<IBrewery[]>(`https://api.openbrewerydb.org/breweries/${id}`)
}
}
list.component.ts
import { Component, OnInit } from '#angular/core';
import { HttpService } from '../http.service';
#Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {
breweries: Object;
constructor(private _http: HttpService) { }
ngOnInit(): void {
this._http.getBreweries().subscribe(data => {
this.breweries = data;
});
}
}
list.component.html
<h1>Breweries</h1>
<ul *ngIf="breweries">
<li *ngFor="let brewery of breweries">
<p class="name">{{ brewery.name }}</p>
<p class="country">{{ brewery.country}}</p>
Visit Website
</li>
</ul>
So all this works no errors everything seems fine...then comes the profile and where things break down.
brewery.component.ts
import { Component, OnInit } from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import { HttpService } from '../http.service';
#Component({
selector: 'app-brewery',
templateUrl: './brewery.component.html',
styleUrls: ['./brewery.component.scss']
})
export class BreweryComponent implements OnInit {
brewery: object = {};
breweryId: string;
constructor(private _http: HttpService, private activatedRoute: ActivatedRoute) { }
ngOnInit(): void {
this.breweryId = this.activatedRoute.snapshot.params.id;
this._http.getBrewery(this.breweryId).subscribe(data => {
this.brewery = data;
})
}
}
brewery.component.html
<ul *ngIf="brewery">
<li>
{{brewery.name}}
</li>
<li>
{{brewery.city}}, {{brewery.state}}
</li>
</ul>
brewery.ts
export interface IBrewery {
name: string,
city: string,
state: string
};
The errors I'm getting are:
- ERROR in src/app/brewery/brewery.component.html:7:13 - error TS2339: Property 'name' does not exist on type 'object'.
- Error occurs in the template of component BreweryComponent.
src/app/brewery/brewery.component.html:10:13 - error TS2339: Property 'city' does not exist on type 'object'.
- Error occurs in the template of component BreweryComponent.
src/app/brewery/brewery.component.html:10:31 - error TS2339: Property 'state' does not exist on type 'object'.
So the problem I believe is that brewery needs to have assigned properties and types associated to those properties before I can declare them in the component template. If that is true, for the life of me I cannot figure out how or where I'm supposed to take the IBrewery and properly use it. I've seen examples where it gets used in the service as well as the mycomponent.component.ts file. In either instance it's about as clear as mud on how to fix the problem.
Short Answer: use Safe Navigation Operator
Update your html as below.
<ul *ngIf="brewery">
<li>
{{brewery?.name}}
</li>
<li>
{{brewery?.city}}, {{brewery?.state}}
</li>
</ul>
Better approach: use a loading spinner.
<div *ngIf="loading">
some loading spinner
</div>
<div *ngIf="!loading">
<li>
{{brewery?.name}}
</li>
<li>
{{brewery?.city}}, {{brewery?.state}}
</li>
</ul>
export class BreweryComponent implements OnInit {
brewery; // be default type will be any.
breweryId: string;
loading = false; // loading spinner.
constructor(private _http: HttpService,
private activatedRoute: ActivatedRoute) { }
ngOnInit(): void {
this.breweryId = this.activatedRoute.snapshot.params.id;
this.get();
}
get() {
this.loading = true;
this._http.getBrewery(this.breweryId)
.subscribe(data => {
this.brewery = data;
this.loading = false; // loading spinner hidden.
}, (error) => {
// handle error;
});
}
}
First of all, you should get the correct typing in your service. It should look like this:
getBreweries() {
return this.http.get<IBrewery[]>('https://api.openbrewerydb.org/breweries');
}
getBrewery(id) {
return this.http.get<IBrewery>(`https://api.openbrewerydb.org/breweries/${id}`)
}
As you can see, I added the expected type to getBreweries and changed the expected type in getBrewery(id). I'm not sure why it was set to IBrewery[] before, since you told us it should only give one specific detail of a brewery.
Now, when you subscribe to these, the parameter inside the subscibe function will be inferred to be the types you have set in the get type parameter. Therefore, it's a good idea to set the type of the component instance variable to that type too, like this:
export class ListComponent implements OnInit {
breweries: IBrewery[];
...
}
and
export class BreweryComponent implements OnInit {
brewery: IBrewery;
...
}
In general, you don't want to use the type object or Object, because it tells you nothing about the structure of the type. If you don't know the exact structure of your type or are too lazy to create an interface, you should use any.
And btw, the reason why the ListComponent worked to begin with was kinda lucky. the let x of y syntax is allowed for y of type object for some reason, and it seems like x is inferred to as any, so you could write whatever you wanted without getting an error. It's important to understand that typescript won't change anything in runtime, so the runtime types will be whatever they are no matter what your typescript types say.
I am using AngularJS for web app and in that I am trying to read data from APIs. Thus i have made few Models in accordance with the API's result set. Among many Models, Lets talk about a single Model TYPE
//This is the JSON API is returning
{
"records":[
{
"ID":"1",
"TYPE":"mythological"
}
],
"pagination":{
"count":"1",
"page":1,
"limit":10,
"totalpages":1
}
}
Now I have made the following Model for TYPE
//type.ts
export class Type {
"ID":string;
"TYPE":string;
}
After fetching the data from API i am successfully storing it and running through my code using following TYPE component ts.
//gallery.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from 'src/app/services/data.service';
import { Type } from 'src/app/models/type';
#Component({
selector: 'app-gallery',
templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.scss']
})
export class GalleryComponent implements OnInit {
types: Type;
constructor(private data: DataService) { }
ngOnInit() {
}
clickfunction(){
this.data.getData().subscribe(data=>{
this.types=data.records;
console.log(this.types);
});
}
}
ALso, i am fetching data from this service
//data.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
dataUrl:string = 'http://localhost/api-slim/public/index.php/api/info/type';
constructor(private http: HttpClient) { }
getData() {
return this.http.get(this.dataUrl);
}
}
Although the application is running its obviously giving me the following error, which i need to radicate.
Date: 2019-09-26T19:42:06.903Z - Hash: a1b41d5889df87ba0aa3
5 unchanged chunks
Time: 780ms
ℹ 「wdm」: Compiled successfully.
ERROR in src/app/components/gallery/gallery.component.ts(23,21): error TS2339: Property 'records' does not exist on type 'Object'.
NOW
The pagination data that the API is providing is common in each of the API response, but as you can see none of my models are consuming it. What would be the best way to store and use that pagination in each of my model. I have tried to made a temporary demo class in gallery.component.ts as follows,
export class TEMP {
records: TYPE[];
pagination: [];
}
But it's ugly. Is there any efficient fix?
Your model class doesn't really reflect the API response.
A model is like a custom data structure that you can use like a data type like this:
export TEMP { //consider renaming this to something more meaningful
records: Array<Type>;
pagination: Pagination;
}
export class Type {
ID: string;
TYPE: string;
}
export Pagination{
count: string;
page: number;
limit: number;
totalpages: number;
}
EDIT: Comment by OP:
"Sorry , but I think I had had slight typo in enviroment/environment, sorry for wasting your time ,it seems to work now"
I have having trouble passing data from app components to child component in angular 2 . I recently started toying with angular 2 and trying to understand how it works. I tried to used the concept shown in this tutorial to do pass data to child component
https://angular.io/docs/ts/latest/tutorial/toh-pt3.html
But I think I am missing something
Here is my project: App component:
import { Component, ViewChild } from '#angular/core';
import { WorkflowService } from './components/workflow_display/workflow.service';
import { WorkflowDisplayComponent } from './components/workflow_display/workflow-display.component';
import { PropertyService } from './shared/property.service';
import '../../public/css/styles.css';
#Component({
selector: 'my-app',
template: require('./app.component.html')
})
export class AppComponent {
title = 'Hello World';
#ViewChild("taskDisplay") workflowDisplay: WorkflowDisplayComponent;
myEnvironment: String; //the variable I am trying to bind from
errorMessage: String;
workbenchBaseUrl : String = 'workbenchBaseUrl';
public selectedNavID : String = 'workspace_control_workStreamView';
public isWorkOrdersCollapsed = false;
public isWorkStreamsCollapsed = false;
constructor(private _propertyService : PropertyService){
}
ngOnInit(): void {
this._propertyService.getValue(this.workbenchBaseUrl)
.subscribe(environment => this.myEnvironment = environment,
error => this.errorMessage = <any>error);
}
}
app.component.html
<div>
<div>
<div>
<!--some html-->
<main class="col-sm-9 offset-sm-3 col-md-10 offset-md-2 pt-3 mh-100">
<workflow-display [environment] ="myEnvironment" #taskDisplay></workflow-display>
</main>
</div>
</div>
</div>
WorkDisplay component
import { Component, Input} from '#angular/core';
import { OnInit } from '#angular/core';
import { IGrcTask } from './grc-task';
import { WorkflowService } from './workflow.service';
import { PropertyService } from '../../shared/property.service';
#Component({
selector: 'workflow-display',
template: require('./workflow-display.component.html')
})
export class WorkflowDisplayComponent implements OnInit {
taskMode: string = 'workstream'; // 'workorder' or 'workstream' to currently identify the columns to display
taskQuery: string = 'process=workstream&taskStatus=RUNNING'; // the query parameters to pass to the tasks web service
workbenchUrl: string = 'http://localhost:8081'; // workbench URL
workbenchTaskPage: string = 'wsIndex'; // workbench page to use to open tasks
infoMessage: string;
errorMessage: string;
tasks: IGrcTask[];
currentTask: IGrcTask;
#Input()
environment: String; //the variable I am trying to bind to
workbenchBaseUrl : String = 'workbenchBaseUrl';
constructor() {
}
//called when user clicks a row
openTask(event: any, task: any) {
// this.environment is still undefined
window.open(this.environment + this.workbenchTaskPage + "?taskId=" + task.taskId + "&activitiWorkflow=true");
}
}
WorkDisplay.component.html
<--!some html-->
<tbody *ngIf='(taskMode == "workorder") && tasks && tasks.length'>
<ng-container *ngFor='let task of tasks; let i=index'>
<tr (click)="setCurrentTask($event, task)" (dblclick)="openTask($event, task)"
<--!some html-->
Property.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
/**
* Service return Property value/values from the project property file
*
*/
#Injectable()
export class PropertyService {
//ReST Url for the PopertyService on the back end
private _url = '/grcworkflow/resources/grcWorkflow/environment/';
constructor(private _http: Http) {}
/**
* Method return an Observable<String -> Value> for any property
* Method make an http get call to the server to fetch the property
* #Param key for the property in the property file
*/
getValue(key: String): Observable<String> {
return this._http.get(this._url+key)
.map((response: Response) => <String> response.text())
.do(data => console.log('All: ' + data))
.catch(this.handleError);
}
private handleError(error: Response) {
return Observable.throw(error.json().error || 'Server error');
}
}
NOTE I have removed some function definitions and variable from the components which might be irrelevant.
I am trying to bind myEnviroment value of the app.component enviroment value. myEnviroment get set when proerty service returns a string. Although enviroment value still stays undefined .
I am looking for one way binding i.e when myEnvironment(parent) changes environment(child) should change too. But this doesn't seem to happen. Please help out here
I am having a hard time using a async object in a html composition.
Here is my model:
export class Version {
isGood: boolean;
constructor(isGood: boolean) {
this.isGood= isGood;
}
}
This model is called by a component as follows:
#Injectable()
export class MyComponent {
public version: Version;
constructor(private _myService: VersionService) {}
getVersion(): void {
// async service that gets the versions
this._myService.getVersion().subscribe(
data => this.version= data,
error=> console.log(error),
() => console.log("getting all items complete")
);
}
}
My template references to the version variable as follows:
<button (click)="getVersion()">Get Version</button>
<hr>
<p style="color:red">{{error}}</p>
<h1>Version</h1>
<p>{{version.isGood}}</p>
However, I get an exception:
Cannot read property 'isGood' of undefined
From scavenging the internet, I see that my problem is because the version object is null. If I do something like:
<p>{{version | json}}</p>
I can see the correct version
If I do something like
<p>{{version.isGood | async}}</p>
I see nothing
If I edit MyComponent, and set
public version: Version = new Version();
I can execute the .isGood property fetch, but it is always empty.
Is there a different way I am supposed to load a property if I am using it in an asynchronous manner?
Use the ? operator or use an *ngIf.
<p>{{version?.isGood}}</p>
<p *ngIf="version">{{version.isGood}}</p>
Try this:
<p>{{version?.isGood}}</p>
This tells Angular to protect against version.isGood being undefined or null until you click and fetch the data for version through your service.
First me correct you. #Injectable() makes a normal typescript class as injectable service where you can share data.
To make a component you need to use #Component decoratore.
The process of data sharing between component and within the application is to create a service and add that as provides in module. And then its singleton object will available everyshere.
//module
import {NgModule} from '#angular/core';
import {YourService} from "./services/your-service";
#NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent
],
providers: [
YouService
],
bootstrap: [AppComponent]
})
export class AppModule {
}
//this is your component
import {Component} from '#angular/core';
import {YourService} from "../../services/your-service";
#Component({
selector: 'component-app',
templateUrl: '../../views/app.component.html',
})
export class HeaderComponent {
constructor(public yourService: YourService) {
}
}
//your service
import {Injectable} from "#angular/core";
#Injectable()
export class YourService {
private _message: string = 'initial message';
private _style: string = 'success';
get message(): string {
return this._message;
}
set message(value: string) {
this._message += value;
}
get style(): string {
return this._style;
}
set style(value: string) {
this._style = value;
}
}
//finally your view
<div class="row">
<div [class]=""><h1>{{swapService.message}}</h1></div>
</div>
Observable Data services.
#Injectable()
export class MyComponent {
public version = new ReplaySubject<Version>();
constructor(private _myService: VersionService) {}
init(): void {
// async service that gets the versions
this._myService.getVersion().subscribe(
data => this.version.next(data),
error=> console.log(error),
() => console.log("getting all items complete")
);
}
getVersion(): void {
this.version.asObservable();
}
}
In the template
<button (click)="init()">Get Version</button>
<hr>
<p style="color:red">{{error}}</p>
<h1>Version</h1>
<p>{{(version |async)?.isGood}}</p>
I'm trying to play with Angular 2-beta and I want to work with Http component. But there is a serious problem here:
I read this and
I know in Angular 2(Unlike Angular 1), Http component is not a service that returns a Promise. It returns something called Observable. We know that a Component is better not to use Http directly. Efficient way is to make a service that is responsible to consume Http. But how?! Should this after completing a request, return a promise? (look at here)
Does it make sense at all?!
It's possible with Angular 2 to implement services. They simply correspond to injectable classes as described below. In this case this class can be injected into other elements like components.
import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
import 'rxjs/add/operator/map';
#Injectable()
export class CompanyService {
constructor(http:Http) {
this.http = http;
}
}
You can inject an Http object in it (using its constructor) at the condition you specified HTTP_PROVIDERS when bootstraping the main component of your application:
import {bootstrap} from 'angular2/platform/browser'
import {HTTP_PROVIDERS} from 'angular2/http';
import {AppComponent} from './app.component'
bootstrap(AppComponent, [
HTTP_PROVIDERS
]);
This service can be then injected into a component, as described below. Don't forget to specify it within the providers list of the component.
import { Component, View, Inject } from 'angular2/core';
import { CompanyService } from './company-service';
#Component({
selector: 'company-list',
providers: [ CompanyService ],
template: `
(...) `
})
export class CompanyList {
constructor(private service: CompanyService) {
this.service = service;
}
}
You can then implement a method leveraging the Http object in your service and return the Observable object corresponding to your request:
#Injectable()
export class CompanyService {
constructor(http:Http) {
this.http = http;
}
getCompanies() {
return this.http.get('https://angular2.apispark.net/v1/companies/')
.map(res => res.json());
}
}
The component can then call this getCompanies method and subscribe a callback on the Observable object to be notify when the response is there to update the state of the component (in the same way you did with promises in Angular1):
export class CompanyList implements OnInit {
public companies: Company[];
constructor(private service: CompanyService) {
this.service = service;
}
ngOnInit() {
this.service.getCompanies().subscribe(
data => this.companies = data);
}
}
Edit
As foxx suggested in his comment, the async pipe could be also used to implicitly subscribe on the observable object. Here is the way to use it. First update your component to put the observable object in the attribute you want to display:
export class CompanyList implements OnInit {
public companies: Company[];
constructor(private service: CompanyService) {
this.service = service;
}
ngOnInit() {
this.companies = this.service.getCompanies();
}
}
Use then the async pipe in your template:
#Component({
selector: 'company-list',
providers: [ CompanyService ],
template: `
<ul>
<li *ngFor="#company of companies | async">{{company.name}}</li>
</ul>
`
})
export class CompanyList implements OnInit {
(...)
}
This article in two parts could give more details as well:
http://restlet.com/blog/2015/12/30/implementing-an-angular-2-frontend-over-an-apispark-hosted-web-api-part-1/
http://restlet.com/blog/2016/01/06/implementing-an-angular-2-frontend-over-an-apispark-hosted-web-api-part-2/
Hope it helps you,
Thierry
There is no need to convert the observable returned by Http's get() method into a promise. In most cases, the service can simply return the observable.
If we are fetching an array or a primitive type (i.e., string, number, boolean) from the server, we can simplify our controller logic by using the returned observable directly in our template, with the asyncPipe. This pipe will automatically subscribe to the observable (it also works with a promise) and it will return the most recent value that the observable has emitted. When a new value is emitted, the pipe marks the component to be checked for changes, hence the view will automatically update with the new value.
If we are fetching an object from the server, I'm not aware of any way to use asyncPipe, we could use the async pipe, in conjunction with the safe navigation operator as follows:
{{(objectData$ | async)?.name}}
But that looks complicated, and we'd have to repeat that for each object property we wanted to display.
Instead, I suggest we subscribe() to the observable in the component and store the contained object into a component property. We then use the safe navigation operator (?.) or (as #Evan Plaice mentioned in a comment) NgIf in the template. If we don't use the safe navigation operator or NgIf, an error will be thrown when the template first tries to render, because the object is not yet populated with a value.
Note how the service below always returns an observable for each of the get methods.
service.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map'; // we need to import this now
#Injectable()
export class MyService {
constructor(private _http:Http) {}
getArrayDataObservable() {
return this._http.get('./data/array.json')
.map(data => data.json());
}
getPrimitiveDataObservable() {
return this._http.get('./data/primitive.txt')
.map(data => data.text()); // note .text() here
}
getObjectDataObservable() {
return this._http.get('./data/object.json')
.map(data => data.json());
}
}
app.ts
import {Component} from 'angular2/core';
import {MyService} from './my-service.service';
import {HTTP_PROVIDERS} from 'angular2/http';
#Component({
selector: 'my-app',
providers: [HTTP_PROVIDERS, MyService],
template: `
<div>array data using '| async':
<div *ngFor="#item of arrayData$ | async">{{item}}</div>
</div>
<div>primitive data using '| async': {{primitiveData$ | async}}</div>
<div>object data using ?.: {{objectData?.name}}</div>
<div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>`
})
export class AppComponent {
constructor(private _myService:MyService) { console.clear(); }
ngOnInit() {
this.arrayData$ = this._myService.getArrayDataObservable();
this.primitiveData$ = this._myService.getPrimitiveDataObservable();
this._myService.getObjectDataObservable()
.subscribe(data => this.objectData = data);
}
}
Note: I put "Observable" in the service method names – e.g., getArrayDataObervable() – just to highlight that the method returns an Observable. Normally you won't put "Observable" in the name.
data/array.json
[ 1,2,3 ]
data/primitive.json
Greetings SO friends!
data/object.json
{ "name": "Mark" }
Output:
array data using '| async':
1
2
3
primitive data using '| async': Greetings SO friends!
object data using .?: Mark
object data using NgIf: Mark
Plunker
One drawback with using the async pipe is that there is no mechanism to handle server errors in the component. I answered another question that explains how to catch such errors in the component, but we always need to use subscribe() in this case.