Use an Observable working with another Observable - html

I'm trying to show information, like that :
user-settings.component.html
<div *ngIf="(_user | async) as user">
<div fxLayoutAlign="center center">
<h3>{{ user.surname + ' ' + user.name }}</h3>
</div>
<div>
<span>
<b>Email : </b>
{{ user.email }}
</span>
</div>
[...]
Actually, in TS I do :
_user = this.db.collection('user').doc("IDENTIFICATION").valueChanges({ idField: 'id' }) as Observable<User>;
problem is that IDENTIFICATION is statics so I tried to do :
user-settings.component.ts
_user: Observable<User> = this.getUser();
[...]
getUser() {
var userTempo: Observable<User>;
this.authService.getUser().subscribe(user => {
userTempo = this.db.collection('user').doc(user.uid).valueChanges({ idField: 'id' }) as Observable<User>;
console.log(user.uid);
});
return userTempo;
}
to know this.authService.getUser() is comming from :
authService.ts
constructor(private afAuth: AngularFireAuth) {}
[...]
getUser() {
return this.afAuth.user;
}
In this configuration, _user returns undefined, so nothing can be showed.
Does anyone have an idea?
Thank you for your time

You have to return an Observable, but what your returning is an object, as you’ve subscribed to it and assigned userTempo an object.
getUser(): Observable<User> {
return this.authService.getUser().map(user => {
console.log(user.uid);
return this.db.collection('user').doc(user.uid).valueChanges({ idField: 'id' });
});
}
If this.db.collection('user').doc(user.uid).valueChanges({ idField: 'id' }) returns an Observable then you will have to do this.
getUser(): Observable<User> {
return this.authService.getUser().switchMap(user => {
return this.db.collection('user').doc(user.uid).valueChanges({ idField: 'id' });
}).map(response => {
return response;
});
}

Related

Unable to print a list in html using ngFor

I am new to angular and front end. I am trying to call a rest Api to fetch a product list. And then I am trying to print them in the html page.
product.component.html
<div class="page-title">Product List</div>
<div class="bgblue">
<div class="product-preview-container">
<ul *ngIf="!emptyProductList">
<p>Available Products</p>
<li *ngFor="let product of products">
<p>Product Name : {{ product.productName }}</p>
<p>Product Code : {{ product.productCode }}</p>
<p>Product Price : {{ product.productPrice }}</p>
</li>
</ul>
<ul *ngIf="emptyProductList">
<p>No Product Found </p>
</ul>
</div>
</div>
product.component.ts
export class ProductComponent implements OnInit {
errorMessage = '';
emptyProductList = true;
products: Product[];
constructor(private productService: ProductService) {
this.products = [];
}
ngOnInit() {
console.log('Called ngOnInit method');
this.productService.fetchProductList().subscribe(
(productList: Product[]) => {
this.products = productList;
if(this.products.length>0){
this.emptyProductList = false;
console.log('not empty');
}
}, err => {
this.errorMessage = err.error;
})
}
}
UPDATE
product.service.ts
export class ProductService {
constructor(private http: HttpClient, private adapter: ProductAdapter) { }
fetchProductList(): Observable<Product[]> {
return this.http.get(PRODUCT_LIST_API, httpOptions).pipe(
map((data: any[]) => [data].map((item) => this.adapter.adapt(item)))
);
}
}
product.model.ts
export class Product {
constructor(
public id: number,
public productCode: string,
public productName: string,
public productPrice: number
) {}
}
#Injectable({
providedIn: "root",
})
export class ProductAdapter implements Adapter<Product> {
adapt(item: any): Product {
return new Product(item.id, item.productCode, item.productName, item.productPrice);
}
}
Updating the productList values :
[Product]
0: Product
id: undefined
productCode: undefined
productName: undefined
productPrice: undefined
__proto__: Object
length: 1
Response json in Postman:
[
{
"id": 1,
"productCode": "P01",
"productName": "Pencil",
"productPrice": 10.0
}
],
Output :
Product List
Available Products
Product Name :
Product Code :
Product Price :
What am I doing wrong here?
The problem is in [data].map((item) => this.adapter.adapt(item))). Note that data is already array, so there is no need to [].
Try this one:
data.map((item) => this.adapter.adapt(item)))

clicking checkbox doesn't change it

I tried to use handleChange method to change the completed boolean who is responsible for checking the box but it does not change .. I can't find where it's missed up
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
return {
todos: updatedTodos
}
})
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item} handleChange={this.handleChange}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
and this is my ToDoItem component
function TodoItem(props) {
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
Issue
You are mutating your state objects. When you don't return new object references React doesn't consider the value to be different and bails on rerendering with updated values.
if (todo.id === id) {
todo.completed = !todo.completed // <-- state mutation!
}
return todo // <-- same todo object reference
Solution
You need to also shallow copy any nested state you are updating.
handleChange(id) {
this.setState((prevState) => ({
todos: prevState.todos.map(todo => todo.id === id ? {
...todo, // <-- shallow copy todo
completed: !todo.completed, // <-- update completed property
} : todo)
});
}

How to update subscribed value in html on change in Angular?

After clicking "Edit", "editStatus" function is called, then the value of "order.status" is changing. But the html view remains the same - displays the old status of order. It changes only after refreshing the page. How can I do the status show the updated variable after change?
html:
<div *ngIf="order">
<p>Id:<b> {{ order._id }}</b></p>
<p>Status:<b> {{ order.status }}</b></p>
</div>
<button (click)="editStatus(order)">Edit</button>
ts file:
private subscribe: Subscription;
order: Order;
constructor(private orderService: OrderService, private route: ActivatedRoute, public router: Router) { }
ngOnInit() {
this.subscribe = this.route.params.pipe(
map(({ id }) => id),
switchMap((id: string) => this.orderService.getOrderById(id)))
.subscribe((res) => {
this.order = res;
});
}
ngOnDestroy() {
this.subscribe.unsubscribe();
}
editStatus(order) {
const orderEdited = { order, status: 'order_canceled' };
this.orderService.editStatus(orderEdited).subscribe(
res => {
console.log(res);
},
err => console.log(err)
);
}
order service:
private userOrdersUrl = 'http://localhost:3000/api/auth/user-orders';
getOrderById(orderId): Observable<Order> {
return this.http.get<Order>(`${this.userOrdersUrl}/${orderId}`);
}
editStatus(order) {
return this.http.put<Order>(this.userOrdersUrl, order);
}
Looks like a student homework...
You do not assign the updated order to your variable. Change to the following:
res => {
this.order = res;
},

Having this error while displaying list of data in Angular => ERROR: TypeError: Cannot read property '0' of undefined

I'm new in Angular and I have an error need to fix. This is the error I've encountered when I'm trying to display a list of data from jsonplaceholder. I don't know what is wrong in the code. May I ask for a help guys? Thank you.
ERROR TypeError: Cannot read property '0' of undefined
at RecipeService.push../src/app/recipes/recipe.service.ts.RecipeService.getRecipe
(recipe.service.ts:25)
at SafeSubscriber._next (recipe-detail.component.ts:26)
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:196)
at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next
(Subscriber.js:134)
at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next
(Subscriber.js:77)
at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:54)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/BehaviorSubject.js.BehaviorSubject._subscribe
(BehaviorSubject.js:22)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe
(Observable.js:43)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Subject.js.Subject._trySubscribe
(Subject.js:89)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe
(Observable.js:29)
recipe-detail.component.ts
ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = +params['id'];
this.recipe = this.recipeService.getRecipe(this.id);
}
);
}
recipe.service.ts
#Injectable()
export class RecipeService {
recipesChanged = new Subject<Recipe[]>();
private url = "https://jsonplaceholder.typicode.com/users/";
private recipes: Recipe[];
constructor(private slService: ShoppingListService, private http: Http) {}
getRecipes() {
return this.http.get(this.url).pipe(map(res => res.json()));
}
getRecipe(index: number) {
return this.recipes[index];
}
recipe-detail.component.html
<div class="row">
<div class="col-xs-12" *ngFor="let recipe of recipes">
<h1 class="list-group-item-heading">{{ recipe.id }}</h1>
<h4 class="list-group-item-heading">{{ recipe.name }}</h4>
<h4 class="list-group-item-heading">{{ recipe.username }}</h4>
<h4 class="list-group-item-heading">{{ recipe.email }}</h4>
<h4 class="list-group-item-heading">{{ recipe.phone }}</h4>
</div>
</div>
It could be that the recipes are null/undefined. So when you call getRecipe(0) this error comes up.. Try changing your method to something like this:
getRecipe(index: number) {
if (!!this.recipes && this.recipes.length > index) {
return this.recipes[index];
} else {
return 0; //Whatever
}
}
In the component you pass in the id to the getRecipe() method of the service. But, you cannot be sure it exists which is why it is occurring. Also, where, in the service, does 'recipes' come from? You initialize it, but never assign it any value (especially as an array). I.e., you aren't calling getRecipes() at all.
So alter the service:
getRecipe(index: number) {
return this.recipes[index] ? this.recipes[index] : null;
}
So now in your component this.recipe may equal 'null' but you won't get the error.
private recipes: Recipe[] is not initialized here. You must have check for undefined and length before accessing the element.
Component
getRecipe(index: number) {
if(this.recipes && this.recipes.length > index){
return this.recipes[index];
}else{
return null;
}
}
RecipeService
#Injectable()
export class RecipeService {
recipesChanged = new Subject<Recipe[]>();
private url = "https://jsonplaceholder.typicode.com/users/";
private recipes: Recipe[];
constructor(private slService: ShoppingListService, private http: Http) {
this.getRecipes().subscribe(recipes=>this.recipes); //<-- you may call from somewhere else.
}
getRecipes() {
return this.http.get(this.url).pipe(map(res => res.json()));
}
getRecipe(index: number) {
return this.recipes[index];
}

Angular 4 Display Elements from a Promise

I have the following Typescript service (app.component.ts):
import { Component, OnInit } from '#angular/core';
import { ApiService } from './shared/api.service';
import {PowerPlant} from './shared/models/powerplant.model';
import 'rxjs/add/operator/toPromise';
#Component({
selector: 'app-root',
providers: [ApiService],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
// represents the URL's
allPowerPlantsURL = 'powerPlants';
// represents the data
powerPlants: PowerPlant[];
ngOnInit(): void {
this.allPowerPlants();
}
constructor(private apiService: ApiService) {
}
allPowerPlants(onlyActive: boolean = false, page: number = 1): void {
const params: string = [
`onlyActive=${onlyActive}`,
`page=${page}`
].join('&');
const path = `${this.allPowerPlantsURL}?${params}`;
this.apiService.get(path)
.toPromise()
.then(elem => {
console.log('In the allPowerPlants');
console.log(elem); **// prints undefined here**
this.powerPlants = <PowerPlant[]> elem; }
)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
This is my app.component.html (just a snippet from it):
<div class="ui grid posts">
<app-powerplant
*ngFor="let powerPlant of powerPlants"
[powerPlant]="powerPlant">
</app-powerplant>
</div>
Now, in my powerplant.component.html, I have this:
import {Component, Input, OnInit} from '#angular/core';
import { PowerPlant } from '../shared/models/powerplant.model';
#Component({
selector: 'app-powerplant',
templateUrl: './powerplant.component.html',
styleUrls: ['./powerplant.component.css']
})
export class PowerplantComponent implements OnInit {
#Input() powerPlant: PowerPlant;
constructor() { }
ngOnInit() {
}
}
And finally, the one that is supposed to display the PowerPlant items are like this:
<div class="four wide column center aligned votes">
<div class="ui statistic">
<div class="value">
{{ powerPlant.powerPlantId }}
</div>
<div class="label">
Points
</div>
</div>
</div>
<div class="twelve wide column">
<div class="value">
MaxPower: {{ powerPlant.maxPower }} MinPower: {{ powerPlant.minPower }}
</div>
<div class="value">
MaxPower: {{ powerPlant.maxPower }} MinPower: {{ powerPlant.minPower }}
</div>
<div class="value">
PowerPlantType: {{ powerPlant.powerPlantType }} Organization: {{ powerPlant.powerPlantName }}
</div>
</div>
I can see that the server is sending me the array as the following console log on the get method shows:
get(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
console.log('sending request to ' + `${environment.api_url}${path}`);
return this.http.get(`${environment.api_url}${path}`, { search: params })
.catch(this.formatErrors)
.map((res: Response) => {
console.log(res.json());
res.json();
});
}
Where the line console.log prints me the following as seen in the screenshot:
So why is the toPromise() fails? Just for information, this is how my PowerPlant model looks like:
export interface PowerPlant {
powerPlantId: number;
powerPlantName: string;
minPower: number;
maxPower: number;
powerPlantType: string;
rampRateInSeconds?: number;
rampPowerRate?: number;
}
Is there a specific reason to use the toPromise() method ? Does it work when subscribing normally ?
Try changing this
this.apiService.get(path)
.toPromise()
.then(elem => {
console.log('In the allPowerPlants');
console.log(elem); **// prints undefined here**
this.powerPlants = <PowerPlant[]> elem; }
)
to this :
this.apiService.get(path).subscribe(result => {
console.log('Im the result => ', result);
this.powerPlants = <PowerPlant[]> result;
});
Then it might be because you don't return the parsed result in your .map() method and thus you can't get the response in your promise / subscription.
.map((res: Response) => res.json()); // return is inferred in this syntax
.map((res: Response) => {
return res.json(); // here it's not
});
It is related to you your ApiService, you forgot to return res.json in your .map
get(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
console.log('sending request to ' + `${environment.api_url}${path}`);
return this.http.get(`${environment.api_url}${path}`, { search: params })
.catch(this.formatErrors)
.map((res: Response) => {
console.log(res.json());
return res.json();
});
}