Angular 6 : Real time data - javascript

I have app in which a user can add a comment , and the comment is displayed , my problem is comments are not displayed untill I refresh the page.
I want a comment to be displayed after the user click just enter or submit button
Here is what I have so far:
Getting data service.ts
this.activeRouter.params.subscribe((params) => {
let id = params['id'];
this.moviesService.getComments(id)
.then(comments => {
console.log(comments);
this.comments = comments;
});
});
2.Then display to the front end: html
<div *ngFor="let comment of comments" class="col-md-7">
<ul class="list-group">
<li class="list-group-item">Author: {{comment.author}}</li>
<li class="list-group-item">Comments: {{comment.description}}</li>
</ul>
<br>
</div>
Unfortunately when my server updates the JSON, the html does not update at all until I refresh the page then I can see the added comments wrong
what am I missing in my code to accomplish what I want? newbie though

Your code is good but unfortunately a Promise only resolves to one value.
However, observables can provide you with a real time stream of data!
Make the moviesService.getComments() method return an observable which returns comments.
It should look a little something like this (assume you are using the angular HttpClient to fetch the comments):
// movieService.service.ts
import { HttpClient } from '#angular/common/http'
...
constructor(
private http: HttpClient
)
getComments() {
return this.http.get<Comments>(url)
}
...
You can consume the observable like so:
// comment.component.ts
...
comments: Observable<Comments>
...
ngOnInit() {
this.comments = this.movieService.getComments()
}
...
And finally in the template:
// comments.component.html
<div *ngFor="let comment of comments | async" class="col-md-7">
<ul class="list-group">
<li class="list-group-item">Author: {{comment.author}}</li>
<li class="list-group-item">Comments: {{comment.description}}</li>
</ul>
<br>
</div>

Using Async Pipe & Observables
Pipes in Angular work just as pipes work in Linux. They accept an input and produce an output. What the output is going to be is determined by the pipe's functionality. This pipe accepts a promise or an observable as an input, and it can update the template whenever the promise is resolved or when the observable emits some new value. As with all pipes, we need to apply the pipe in the template.
Let's assume that we have a list of products returned by an API and that we have the following service available:
// api.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class ApiService {
constructor(private http: HttpClient) { }
getProducts() {
return this.http.get('http://localhost:3000/api/products');
}
}
The code above is straightforward - we specify the getProducts() method that returns the HTTP GET call.
It's time to consume this service in the component. And what we'll do here is create an Observable and assign the result of the getProducts() method to it. Furthermore, we'll make that call every 1 second, so if there's an update at the API level, we can refresh the template:
// some.component.ts
import { Component, OnInit, OnDestroy, Input } from '#angular/core';
import { ApiService } from './../api.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/switchMap';
#Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
#Input() products$: Observable<any>;
constructor(private api: ApiService) { }
ngOnInit() {
this.products$ = Observable
.interval(1000)
.startWith(0).switchMap(() => this.api.getProducts());
}
}
And last but not least, we need to apply the async pipe in our template:
<ul>
<li *ngFor="let product of products$ | async">{{ product.prod_name }} for {{ product.price | currency:'£'}}</li>
</ul>
This way, if we push a new item to the API (or remove one or multiple item(s)) the updates are going to be visible in the component in 1 second.

Related

angular *ngFor from json

I am new to angular. Let me first give an overview of what i am trying to achieve because this is a long code I am showing the relevant part.
I have a list display component.
And I have (lets say 2) components animals, zone.
Lets say :
zone has 2 columns zone name and zone code,
animals has 3 columns animal code, animal name, animal zone
and so on (lets say for 10 other components)
each component will generate JSON and send it to display list component.
display list will parse the JSON and display it with ngFor
in short :
each component will make JSON and send it to service , which has behavior subject
service has behavior subject, that will receive that JSON
display component will get the latest json from service's behavior subject
finally display component will parse json and will display them using ngfor
My generating and sending JSON to display list component is ok.
For example, I will show you the JSON of zone component that is send to display component.
I need your help to process the JSON so that I can display it using ngFor on display component html.
Code:
data.service.ts:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class DataService {
private messageSource = new BehaviorSubject(null);
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: any) {
console.log('changed');
console.log(message);
this.messageSource.next(message);
}
}
for zone (zone.component.ts) : [car.component.ts is as same as zone.component.ts don't get confused]
import { Component, OnInit } from '#angular/core';
import { DataService } from "../../services/data.service";
import { Router } from '#angular/router';
#Component({
selector: 'app-cars',
templateUrl: './cars.component.html',
styleUrls: ['./cars.component.css']
})
export class CarsComponent implements OnInit {
jsonData: any;
data: any;
constructor(private router: Router, private dataService: DataService) {
}
dd() {
this.setData(this.jsonData);
}
ngOnInit(): void {
this.setJsonData();
}
async getJsonData() {
const myurl: string = "http://localhost:3000/zone/get/all";
const response = await fetch(myurl, { headers: { 'Content-Type': 'application/json' } });
return await response.json();
}
async setJsonData() {
this.jsonData = await this.getJsonData();
}
setData(newJsonData: any) {
this.data = Object.entries(newJsonData);
}
navigateToDisplayList(){
this.router.navigateByUrl('display-list');
}
newMessage() {
this.dataService.changeMessage(this.jsonData);
// console.log(this.jsonData);
// console.log(this.data);
this.navigateToDisplayList();
}
}
for display : display-list.component.ts :
import { Component, OnInit } from '#angular/core';
import { DataService } from "../../services/data.service";
#Component({
selector: 'app-display-list',
templateUrl: './display-list.component.html',
styleUrls: ['./display-list.component.css']
})
export class DisplayListComponent implements OnInit {
data: any;
constructor(private dataService: DataService) { }
ngOnInit(): void {
this.dataService.currentMessage.subscribe(message => this.data = message);
}
dd(){
console.log(this.data);
document.body.innerText = this.data.toString();
}
}
A special note :
Please don't think I haven't researched it yet just because I am not posting the display-list.html
LOOK MY ALGORITHM IS SIMPLE :
OUTER LOOP FOR EACH ROW
INNER LOOP FOR EACH COLUMN
THAT IS IT. I DON'T NEED ANYTHING ELSE IN HTML
I have tried this approach :
<tr *ngFor="let x of data">
<td *ngFor="let y of x">
{{y}}
</td>
</tr>
Each time I am getting error: ngFor is not a known property
(which is funny: If I just comment the ngfor error is gone
or
If I just ngfor on a static array like 1,2,3,4,5 no error there )
Some other time : data can not be iterated
(another funny thing: clearly my JSON can be iterated and no quotation or bracket is missing)
I simply don't get it why angular can't iterate this thing
JSON for zone list :
[
{
"zonecode":3,
"zonename":"d"
},
{
"zonecode":4,
"zonename":"d"
},
{
"zonecode":15,
"zonename":"kk"
}
]
Another very special note :
You don't need to post an answer or comment if you are suggesting capture the JSON in a variable then just loop on console.log(object.zonename) .
Because I have no control over JSON, I have lets say 30 other components where no zonename is there. I have to display in HTML directly from JSON
updating my answer based on comments....I understand that you want to access the key value pairs inside the object and this can be done as below
<tr *ngFor="let x of data">
<td *ngFor="let y of x | keyvalue">
{{y.key}}:{{y.value}}
</td>
</tr>

Angular: How to get value from one component's frontend (app.compont.html) to another component's backend (other.component.ts)

Consider a simple crud scenario. I have a lot of input fields and buttons in app.component.html. When i press a button from app.component.html, it will send html field value to 'other.component.ts' component and will display the result back in app.component.html after processing (like add, subtract or other).
Here is app.component.html
<a routerLink="posts/">Show Posts</a>
<input type="number" [(ngModel)]="get-one-post-id">
<a routerLink="/post-by-id">Show One Posts</a>
<router-outlet>
</router-outlet>
post-by-id-component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-post-by-id',
templateUrl: './post-by-id.component.html',
styleUrls: ['./post-by-id.component.css']
})
export class PostByIdComponent implements OnInit {
posts: object;
constructor(private dataService: DataService) { }
ngOnInit(): void {
// const id = ??
this.GetPost(1);
}
async GetPost(id: number)
{
const response = await this.dataService.Get_A_Post(id);
const dataService = await response.json();
this.posts = dataService;
}
}
post-by-id-component.html
<div *ngFor="let post of posts">
<h3>{{post.title}}</h3>
<p>{{post.body}}</p>
</div>
I just want to get value from the field called get-one-post-id from app.component.html to post-by-id-component.ts [where I commented // const id = ??]. But i can't find a way to import it.
To share Data between Angular Components exists 4 different ways:
Parent to Child: Sharing Data via Input
Child to Parent: Sharing Data via ViewChild
Child to Parent: Sharing Data via Output() and EventEmitter
Unrelated Components: Sharing Data with a Service
You can read this useful article to see how it works.

In Angular 7 when passing data from one component to another, do I use a service and subscribe/listen in the receiving component?

I'm new to Angular 7 and I'd like to know if I'm on the right path.
I have an 'alert' component that just displays a boostrap alert box on the page at the top.
I want to be able to call this alert and display it from any component.
I'm pretty sure I need a service that I can call to pass a message and then have the alert component subscribe to the service to listen for incoming messages?
So far I can call the service and pass it a 'message' I just don't know how to subscribe/listen (I think that's the right terminology) in the alert component to listen for incoming messages to display.
ex. LoginComponent
constructor(public authService: AuthService, private router: Router, private alert: AlertService) {}
login() {
this.authService.login(this.model).subscribe(next => {
this.alert.success('Logged in successfully');
}, error => {
this.alert.failure('Log in failed');
}, () => {
// do something else
});
}
and then here is my service
ex. AlertService
import {
Injectable
} from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class AlertService {
constructor() {}
success(message: string) {
// do something here?
}
error(message: string) {
// do something here?
}
}
and then I have my AlertComponent, but not sure how I would subscribe/listen for incoming messages ti display from the AlertService.
ex. AlertComponent.ts
export class AlertComponent implements OnInit {
dismissible = true;
alerts: any[];
constructor() { }
ngOnInit() {
this.add();
}
// do something here to subscribe/listen to incoming messages from the service??
add(): void {
this.alerts.push({
type: 'info',
msg: `This alert will be closed in 5 seconds (added: ${new Date().toLocaleTimeString()})`,
timeout: 5000
});
}
}
and the html
<div *ngFor="let alert of alerts">
<alert [type]="alert.type" [dismissible]="dismissible" [dismissOnTimeout]="alert.timeout">{{ alert.msg }}</alert>
</div>
You can also read Angular Dependency Injection.
To have injectable service at disposal in some component you must put it constructor and let Angular DI to provide it: Costructor of AlertComponent shoud have:
constructor ( private/proteced alertService:AlertService) {
alertService.subsribe ((par)=> {
this.add(par);
...})
}
You have preaty a lot to learn. This is just lazy made example becouse overwrite observable every time. It's not an perfect code but shows a little bit how Observables work.
Alert Service:
import {
Injectable
} from '#angular/core';
import { Observable, of } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class AlertService {
alerts: Observable<any>
constructor() { }
success(message: any) {
this.alerts = of(message)
}
error(message: string) {
this.alerts = of(message)
}
}
Allert component where alert showns:
export class AlertComponent implements OnInit {
dismissible = true;
// just inject service
constructor(public alerts$: AlertService) { }
ngOnInit() {
}
}
Template:
<div *ngIf="alerts$ | async as alerts"> <!-- | async is an pipe it will subscribe for you. importat for observables to first be in *ngIf then in *ngFor loops-->
<ng-container *ngFor="let item of alerts">
<alert[type]="alert.type"[dismissible]="dismissible" [dismissOnTimeout]="alert.timeout"> {{ item }}</alert>
</ng-container>
</div>
Command triggering alert in any component You want:
login() {
this.authService.login(this.model).subscribe(next => {
this.alert.success({ type: 'info', timeout: '5000', msg: "Success!"});
}, error => {
this.alert.failure({ type: 'info', timeout: '5000', msg: "Success!"}); // `this function u can delete meend failure just succes refactor to 'open'`
}, () => {
// do something else
});
}
About services You need to remember to provide them in app.module.ts or any other module like providers: [AlertService] So application will know that this is an service. And you inject them eny where You wat by class constructor(). When injecting you need allways set an scope for them like 'private public or protected' Or You will end up with regular Variable in type or service class.
About Observables:
There are endless Observables and when you subscribe to them You need to unsubscribe read it abot it some where on internet. | async Pipe will do it for You if variable is an endless loop.

Angular: pass single object within ngFor to router linked component

How do I pass the data binded information within the <a> tag (Within the volume-links.component.html ) to my page-view component when the link is clicked.
I want to pass that particular diary object to my page-view.
I've looked into parent and child component interaction but I don't think that is the proper way to do it. I've looked into communicating via a service but I do not know how that would work for a problem such as this.
volume-links.component.html
<ul class="navigation">
<li *ngFor="let d of diary">
<a id={{d.notebook_id}} routerLink="/page-view" routerLinkActive="active">Volume {{d.notebook_id}}, {{ d.date }}, {{ d.volume_id }}, Add MS {{ d.ms_id }}</a>
</li>
</ul>
volume-links.component.ts
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import { HttpClient, HttpClientModule } from '#angular/common/http';
import 'rxjs/add/operator/map'
#Component({
selector: 'app-volume-links',
templateUrl: './volume-links.component.html',
styleUrls: ['./volume-links.component.scss'],
//encapsulation: ViewEncapsulation.None
})
export class VolumeLinksComponent implements OnInit {
diary : String;
constructor(private http: HttpClient) { }
ngOnInit() {
this.http.get('/api/diaries').subscribe(data => {
this.diary = data["data"]["docs"];
console.log(this.diary);
})
}
}
You want to look at https://angular.io/guide/component-interaction
There are several methods / ways to achieve this and depending on your use case choose one.
I would define an Input property in VolumeLinksComponent and pass the diary object in there (that's the first part "Pass data from parent to child with input binding").
This would look something like:
<a *ngFor='let diary of diaries' (click)='chooseDiary(diary)'>
<my-diary-container [diary]='selectedDiary'></my-diary-container>
and that parent component of course needs a property 'selectedDiary' and a method:
chooseDiary(diary: Diary): void {
this.selectedDiary = diary;
}
But in your provided case it seems like you just need the specific id since you want to retrieve details from the api? In that case you could just define a route with the id and when the route is accessed ask an additional DiaryService to retrieve what you need.

Angular 2 *ngFor doesn't display data

I'm working on an application using Ionic 2 together with Angular 2. Now I'm trying to the data from an API and display this on a page.
I can log the data and I think it's correct, but for some reason nothing is being displayed on the page itself:
The API where I'm receiving the data from is located here: http://peerligthart.com/grotekerk/v1/api.php/zerken?transform=1
*ngFor on my view
<ion-content padding>
<h1 *ngFor="let z of zerken">
{{ z.naam }}
</h1>
</ion-content>
Controller
import { Component } from '#angular/core';
import { NavController, PopoverController } from 'ionic-angular';
import { PopoverPage } from '../popover/popover';
import { ZerkenProvider } from '../../providers/zerken';
#Component({
selector: 'page-lijst',
templateUrl: 'lijst.html',
providers: [ZerkenProvider]
})
export class LijstPage {
zerken: Array<any>;
constructor(public navCtrl: NavController, public popoverCtrl: PopoverController, public zerkenProvider: ZerkenProvider) {
this.zerkenProvider.getZerken().subscribe(
data => {
console.log(data.zerken);
this.zerken = data.zerken.results;
}
)
}
openPopover(event) {
let popover = this.popoverCtrl.create(PopoverPage);
popover.present({
ev: event
});
}
}
And last, the provider
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class ZerkenProvider {
static get parameters() {
return [[Http]];
}
constructor(public http: Http) {
}
getZerken() {
var url = "http://peerligthart.com/grotekerk/v1/api.php/zerken?transform=1";
var response = this.http.get(url).map(res => res.json());
    
return response;
}
}
So, what the page is displaying itself:
As you can see.. nothing. I hope someone has a solution, kind regards!
-------------EDIT-------------
I changed this.zerken = data.zerken.results to this.zerken. After doing this it's giving me an error:
Your zerken in your response doesn't seem to have an results object, so
this.zerken = data.zerken.results;
should be:
this.zerken = data.zerken;
Remember to initialize the array in your component:
zerken: Array<any> = [];
so that you won't get an error that zerken is undefined, since view is usually rendered before data has been received. Having it initialized will prevent that.
You need an *ngIf encapsulating the *ngFor since zerken is obtained at a later point of time.
Try:
<ion-content padding>
<div *ngIf="zerken">
<h1 *ngFor="let z of zerken">
{{ z.naam }}
</h1>
</div>
</ion-content>
Also you need to set zerken = data.zerken; as mentioned in the other answer by #AJT_82.

Categories

Resources