I'm trying to do a crud with angular and node.
My rest API is completed (made with node and mysql);
Trying to display my JSON data at the HTML template, but I'm not being successful...
Thank you if you could help me :)
My service:
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { map } from "rxjs/operators";
import { Produto} from './produto';
import { catchError } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
#Injectable()
export class ProductService {
constructor(private _http : Http){ }
getProdutos(): Observable<Produto[]>{
return this._http
.get("http://localhost:8080/produtos").pipe(
map(res => res.json()));
}
}
Html:
<tr>
<th>Product</th>
<th>Price</th>
<th>Description</th>
<th>Category</th>
<th>Actions</th>
</tr>
<!-- Use *ngFor directive to loop throught our list of products. -->
<tr *ngFor="let produto of produtos">
<td>{{produto.nome}}</td>
<td>{{produto.descricao}}</td>
<td>{{produto.valor}}</td>
<td>{{produto.src}}</td>
<td>
Component
import { Component, OnInit, Input, Output, EventEmitter } from
'#angular/core';
import { ProductService } from '../product.service';
import { Observable } from 'rxjs';
import { Produto } from '../produto';
#Component({
selector: 'app-read-products',
templateUrl: './read-products.component.html',
styleUrls: ['./read-products.component.css'],
providers: [ProductService]
})
export class ReadProductsComponent implements OnInit {
produtos: Produto[];
constructor(private productService: ProductService){}
ngOnInit(){
this.productService.getProdutos()
.subscribe(produtos =>
this.produtos=produtos['records']
);
}
}
Class:
export class Produto {
constructor(
public produto_id: number,
public nome: string,
public descricao: string,
public valor: number,
public src: string
){}
}
My json response (when goes to the link):
[{"produto_id":10,"nome":"caderno","descricao":"maycon","valor":23.2212,"src":"aasssaa"}]
Have more classes in my project, if someone thinks that the problem is in another, just tell...
OBS: CREATED WITH ANGULAR CLI AND FOLLOWING THIS TUTORIAL
If you get un-named array from backend then you should assign result directly to productos :
this.productService.getProdutos()
.subscribe(produtos =>
this.produtos=produtos
);
If you get your array data here in productos then your HTML ngFor loop will work correctly :
<tr *ngFor="let produto of produtos">
<td>{{produto.nome}}</td>
<td>{{produto.descricao}}</td>
<td>{{produto.valor}}</td>
<td>{{produto.src}}</td>
<td>
</tr>
You should return the changed response inside map function. Like this
return this._http
.get("http://localhost:8080/produtos").pipe(
map(res =>
return res.json()));
}
Do a console.log to check whether you are receiving the data and identify the structure of the data. For the json response you have mentioned , in your component just assign it directly to the array
this.produtos=produtos;
Hope this helps.
Related
I have an Angular and Node JS project with Typescript in which I am trying to create the communication between them using a service.
When making a get() request from the front I can't get anything from the back, I don't know what I might be missing to configure or what problem I have in the service.
This is the API response:
This is the service.ts:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
#Injectable({
providedIn: 'root'
})
export class DashboardService {
private url = 'http://localhost:8080/list/product'
constructor(
private http: HttpClient
) {}
public getTest() {
return this.http.get(`${this.url}`);
}
}
This is the component.ts where I am trying to display the message on the console:
import { Component, OnInit } from '#angular/core';
import { DashboardService } from './dashboard.service';
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit {
constructor(
private dashboardService: DashboardService
) {
}
ngOnInit() {
console.log('hola')
console.log(this.dashboardService.getTest().subscribe())
}
}
This is what I get on the console:
How should I do the GET() request to communicate with the back?
You are subscribing but then doing nothing with the subscription. I recomment you read up on angular subscriptions a bit more, but effectively what you want to do is get the result from the subscription like so.
items: string[];
this.dashboardService.getTest().subscribe({
next: result => items = result,
});
Also your get method in your service needs a bit of work.
public getTest(): Observable<string[]> {
return this.http.get<string[]>(`${this.url}`);
}
This is for returning a list of strings
I'm struggling with the Angular mat-table library. I got an app with a Node.js backend and an Angular frontend. The node app provides data from a MySQL database in JSON.
Now I want to display this data in a mat-table. I have logged the data in the console, which allows me to see that the data is actually retrieved but just not displayed.
However, the HTML table is empty:
This is my Angular component:
component.html
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8 demo-table">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>ID</th>
<td mat-cell *matCellDef="let element">{{element.id}}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let element">{{element.name}}</td>
</ng-container>
<ng-container matColumnDef="pop">
<th mat-header-cell *matHeaderCellDef>Population</th>
<td mat-cell *matCellDef="let element">{{element.population}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr
mat-row
(click)="clickedRows.add(row)"
[class.demo-row-is-clicked]="clickedRows.has(row)"
*matRowDef="let row; columns: displayedColumns;"
></tr>
</table>
component.ts
import {Component, OnInit,ViewChild} from '#angular/core';
import { Player } from '../player';
import { PlayerService } from '../player.service';
import { MatTableDataSource } from '#angular/material/table';
import {MatPaginator} from '#angular/material/paginator';
import {MatSort, SortDirection} from '#angular/material/sort';
/**
* #title Binding event handlers and properties to the table rows.
*/
#Component({
selector: 'app-players',
styleUrls: ['players.component.css'],
templateUrl: 'players.component.html',
})
export class PlayersComponent implements OnInit {
displayedColumns: string[] = ['id', 'name', 'pop'];
dataSource = new MatTableDataSource<Player>();
#ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
#ViewChild(MatSort, { static: true }) sort!: MatSort;
constructor(private playerService:PlayerService) { }
ngOnInit(): void {
this.getPlayers();
}
getPlayers() {
this.playerService.getPlayers().subscribe(players => {
console.log(players);
this.dataSource.data = players;
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
});
}
clickedRows = new Set<Player>();
}
player.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { Player } from './player';
#Injectable({
providedIn: 'root'
})
export class PlayerService {
constructor(private http: HttpClient) { }
rootURL = '/api';
getPlayers(): Observable<Player[]> {
return this.http.get<Player[]>(this.rootURL+ '/players');
}
}
Any ideas on this?
EDIT:
Could it have something to do with how the array comes back from the API? In Node.js it is retrieved with sequelize and maybe it is the response?
// Get all Players
exports.findAll = (req, res) => {
Player.findAll().then((players) => {
// Send all players as response
res.status(200).json({
status: true,
data: players,
});
});
};
Issue
The data returned based on the screenshot and Node.js API is not Player array, but it is an object with status and data properties.
{
"status": 200,
"data": [...]
}
This line expected that HTTP GET to receive Player array which is conflict with your data.
this.http.get<Player[]>(this.rootURL+ '/players');
Hence, it returns Observable with an empty array and your <mat-table> will not show the data.
Solution
Transform the data to Player array with map rxjs operator.
import { map } from 'rxjs';
getPlayers(): Observable<Player[]> {
return this.http
.get(this.rootURL+ '/players')
.pipe(map((response: any) => response.data as Player[]));
}
Sample Solution on StackBlitz
Create a instance of MatTableDataSource and assign to data Source.
Try this option
this.dataSource = new MatTableDataSource(players);
this.dataSource.sort = this.sort;
Ref: Using HTTP GET request to fetch data in an Angular Material Table
I am using this wrapper for the azure maps library. I am currently implementing a symbol layer and using one of the default markers works well, but I am not able to add my own marker. I tried to add a custom marker like in my mapReady function, but the response is always undefined and the image is not added.
this is my component:
import {Component, Input, OnInit} from '#angular/core';
import * as atlas from 'azure-maps-control';
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.scss']
})
export class MapComponent implements OnInit {
private markerImagePath = 'assets/images/map-marker.png';
public dataSource: atlas.source.DataSource;
markerDescription: 'marker';
public options: atlas.IconOptions = {
image: this.markerDescription
};
points = [
[52.52437, 13.41053],
[51.50853, -0.12574]
];
ngOnInit() { }
mapReady(map: atlas.Map) {
map.imageSprite.add(this.markerDescription, this.markerImagePath).then(r => {
console.log(r);
console.log(map.imageSprite.getImageIds());
this.dataSource = new atlas.source.DataSource('markers');
this.points.forEach(p => {
const point = new atlas.Shape(new atlas.data.Point([p[1], p[0]]));
this.dataSource.add([point]);
});
});
}
}
this is my html:
<section>
<div class="row">
<div class="col-12 map-dimensions my-2 mx-auto" azure-map zoom="2"
[dataSources]="[dataSource]" (onReady)="mapReady($event.map)">
<map-symbol-layer dataSourceId="markers"
[iconOptions]="options"></map-symbol-layer>
</div>
</div>
</section>
I suspect, that I access the map data wrongly... Do any of you guys know, how I can add a custom image to the imageSprites in order for me to use it as a marker in the symbol layer?
Your code looks fine. imageSprite.add returns a Promise<void>, so your console.log will always log undefined. Could your icon be the issue ? I have been trying a similar solution and all works fine on my side :
import { Component } from '#angular/core';
import * as atlas from 'azure-maps-control';
#Component({
selector: 'app-root',
template: '<azure-map zoom="2" [dataSources]="[dataSource]" (onReady)="mapReady($event.map)">' +
'<map-symbol-layer [id]="blueLayerId" dataSourceId="blue" [iconOptions]="blueIconOptions"></map-symbol-layer>' +
'</azure-map>',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
public dataSource: atlas.source.DataSource;
public blueLayerId: string = "blueLayer";
public blueIconOptions: atlas.IconOptions = {
image: 'campground'
};
mapReady(map: atlas.Map) {
map.imageSprite.add('campground', 'assets/campground.png').then(() => {
this.dataSource = new atlas.source.DataSource('blue');
for (let i = 0; i < 10; i++) {
const point = new atlas.Shape(new atlas.data.Point([i * 5, i * 5]));
this.dataSource.add([point]);
}
});
}
}
I am working on an angular project. I have a text file which that is red using node js. The content is then stored in a url. I need to apply an http get method to get the data from the server and display it on the client side.
I tried the following code but when I hit the button I don't get the file data displayed. What is the problem?
src/app/file.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class ConfigService {
constructor(private http: HttpClient) {}
getFile() {
return this.http.get('http://localhost:3000/hello');
}
}
src/app/app.component.html
<p>
<button (click)="getFile()">get data</button>
</p>
You have a few problems. First, you're not subscribing to the Observable, so nothing is going to happen. Second, you also need to handle the Observable, you're not. Third, you have not defined a place to inject the data once received.
...
#Injectable()
export class ConfigService {
data = {};
constructor(private http: HttpClient) { }
getFile() {
this.http.get('http://localhost:3000/hello').subscribe(result => {
this.data = result });
}
}
then you need somewhere to display it,
<p>
<button (click)="getFile()">get data</button>
<div *ngIf="data">{{ data }}</div>
</p>
I am working on a recipe app. I'm using Yummly API I am getting a response however I am confused how to render the data I get back from the API because the response is a Object with an array of recipes. When I try to render the array I get this error:
Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {imageUrlsBySize, sourceDisplayName, ingredients, id, smallImageUrls, recipeName, totalTimeInSeconds, attributes, flavors, rating}). If you meant to render a collection of children, use an array instead.
Link to an image of what the API response looks like:
Object from API
"Matches" is the part I want to render in my component
Action.js
import Axios from 'axios';
import {LOOK_UP_RECIPE`enter code here`} from './types';
const API_ID = '########';
const API_KEY = '######';
const ROOT_LOOK_UP_URL = `http://api.yummly.com/v1/api/recipes?
_app_id=${API_ID}&_app_key=${API_KEY}`
export function lookuprecipesYummly(ingredients) {
const yummlyurl =`${ROOT_LOOK_UP_URL}&q=${ingredients}`;
const request = Axios.get(yummlyurl);
return {
type: LOOK_UP_RECIPE,
payload: request
};
}
Reducer.js
import { LOOK_UP_RECIPE } from '../actions/types'
export default function(state = [], action) {
console.log(action)
switch (action.type){
case LOOK_UP_RECIPE:
return [ action.payload.data, ...state ];
default:
return state;
}
}
Component:
import _ from "lodash";
import React, {Component} from 'react';
import { connect } from 'react-redux';
class RecipeList extends Component {
renderRecipe(recipeData) {
return (
<tr key={0}>
<td key={1}>{recipeData.matches}</td>
</tr>
)
}
render() {
return(
<table>
<thead>
<tr key={1}>
<th>Recipe</th>
</tr>
</thead>
<tbody>
{this.props.recipes.map(this.renderRecipe)}
</tbody>
</table>
)
}
}
function mapStateToProps({recipes}) {
return {
recipes
}
};
export default connect(mapStateToProps)(RecipeList);
You need to use the data for each recipe inside some JSX. Here is an example of how to populate a table row:
import _ from "lodash";
import React, {Component} from 'react';
import { connect } from 'react-redux';
class RecipeList extends Component {
renderRecipe(recipe) {
return (
<tr key={recipe.id}>
<td>{recipe.recipeName}</td>
<td>{recipe.rating}</td>
<td>{recipe.attributes.course[0]}</td>
<td>{recipe.ingredients.join(', ')}</td>
</tr>
)
}
render() {
return(
<table>
<thead>
<tr>
<th>Recipe</th>
<th>Rating</th>
<th>Course</th>
<th>Ingredients</th>
</tr>
</thead>
<tbody>
{this.props.recipes.matches.map(this.renderRecipe)}
</tbody>
</table>
)
}
}
function mapStateToProps({recipes}) {
return { recipes }
};
export default connect(mapStateToProps)(RecipeList);