I want to show the Detail of a Blog in a different link in Angular. I already have a Blog file (blog.component.ts) and an Angular service where I can get all the blogs data from an API backend made with Strapi. There is one button in every single blog, which allows you to view the detail or complete Blog in a different link calling the unique ID, that is named 'pagina.component.ts'.
For that purpose, I think I must call the ID of every single Blog.
Here is my blog.component.html, where I already have the list of my blogs:
<section class="articles">
<article class="blue-article" *ngFor="let data of datas; index as i">
<div class="articles-header">
<time>{{ data.fecha }}</time>
<span class="articles-header-tag-blue">{{ data.relevante }}</span>
<span class="articles-header-category">
{{ data.category.name }}
</span>
</div>
<div class="articles-content">
<h1><a title="">{{ data.title }}</a></h1>
<!--<img *ngIf="!data.image" class="foto" [src]="data.image.name" alt="foto">-->
<div *ngIf="data.image">
<img
src="http://localhost:1337{{ data.image.url }}"
alt="foto"
width="100%"
/>
</div>
<p>
{{ data.content }}
</p>
<h3>{{ data.description }}</h3>
</div>
<div class="articles-footer">
<ul class="articles-footer-info">
<li><i class="pe-7s-comment"></i> 7 Respuestas
</li>
<li><i class="pe-7s-like"></i> 1221</li>
</ul>
<a [routerLink]="['./pagina',i]" class="btn">Leer más</a>
</div>
</article>
</section>
Here is my blog.component.ts file
import { Component, OnInit } from '#angular/core';
import { Meta, Title } from '#angular/platform-browser';
import { StrapiService } from '../../../services/strapi.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-blog',
templateUrl: './blog.component.html',
styleUrls: ['./blog.component.scss']
})
export class BlogComponent implements OnInit {
datas:any=[];
errores:string="";
constructor(
public strapiserv:StrapiService,
private router: Router
) { }
ngOnInit(): void {
this.title.setTitle('Blog');
this.strapiserv.getData().subscribe(res=>{
this.datas= res as string[];
}, error =>{
console.log(error);
if(error.status == 0){
this.errores="Código del error: "+error.status+" \n Ha ocurrido un error del lado del cliente o un error de red.";
}else{
this.errores="Código del error: "+error.status+"\n\n"+error.statusText;
}
})
}
}
Here is my Angular service named 'strapi.service.ts'
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class StrapiService {
REST_API: string ='http://localhost:1337/articles';
//https://strapi-dot-gmal-api-313723.uc.r.appspot.com/
httpHeaders = new HttpHeaders().set('Content-Type', 'application/json');
constructor(private httpClient: HttpClient) { }
getData():Observable<any>{
console.log();
let API=this.REST_API;
return this.httpClient.get(API,{headers:this.httpHeaders}) .pipe(
map((data:any) => {
return data;
}), catchError( error => {
return throwError(error);
})
)
}
/*getItem( idx:number ){
return this.data[idx];
}*/
}
And Here is my pagina.component.ts file where I want to show the complete detailed Blog
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Router } from '#angular/router';
import { StrapiService } from '../../../../services/strapi.service';
#Component({
selector: 'app-pagina',
templateUrl: './pagina.component.html',
styleUrls: ['./pagina.component.scss']
})
export class PaginaComponent implements OnInit {
data:any = {};
constructor( private activatedRoute: ActivatedRoute,
private router: Router,
public strapiserv:StrapiService
){
this.activatedRoute.params.subscribe( params => {
this.data = this.strapiserv.getData( params['id'] );
});
}
ngOnInit(): void {
}
}
My routes are:
const routes: Routes = [
{ path: 'blog', component: BlogComponent },
{ path: 'pagina/:id', component: PaginaComponent },
];
There are a lot of issues with what you have done:
The 'i' is the index of the loop, not the 'id' you want to use. I guess the 'id' is a property of data, so you should point to data.id
You don't have to use relative path when using [routerLink] (with brackets)
so replace this:
[routerLink]="['./pagina',i]"
with this:
[routerLink]="['/pagina', data.id]" // remove the '.'
Finally in your blog.component.ts file, your're receiving array of objects so you don't have to cast it as an array of string, this.datas= res; is enough
Related
What am I doing wrong?
As you can see the console.log prints the output fine but I can't seem to see it on the webpage.
get-cart.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Cart } from 'src/app/models/cartModel';
import { UsersService } from 'src/app/services/users.service';
#Component({
selector: 'app-get-one-cart',
templateUrl: './get-one-cart.component.html',
styleUrls: ['./get-one-cart.component.css']
})
export class GetOneCartComponent implements OnInit {
cart: Cart;
constructor(
private route: ActivatedRoute,
private userService: UsersService
) { }
ngOnInit(): void {
const id = this.route.snapshot.paramMap.get('id');
this.userService.getCartForUser(id)
.subscribe(data => {
this.cart = data;
console.log(this.cart)
});
}
}
get-cart.html
<div class="content-box">
<a routerLink="/users">
<button>Back</button>
</a>
<div *ngIf="cart">
<h2>{{cart._id}}</h2>
<p>{{cart.userId}}</p>
<div *ngFor="let product of cart.products">
<p>{{product.productId}}</p>
<p>{{product.quantity}}</p>
<p>{{product.price}}</p>
</div>
</div>
</div>
console.log and webpage outputs
Got the answer! I just had to set
await Cart.find
to
await Cart.findOne
This is an employee list from my mock db.json. I am trying to visualise it in the DOM.
In the app.component.html if I write in the loop {{employee}}, it visualises a list of 2 items and each item is[object Object]. Otherwise if I write {{employee.name}} the error is:
Property 'name' does not exist on type 'EmployeeService'.ngtsc(2339)
What am I missing? Any help will be appreciated.Thank you.
app.component.html:
{{title}}
<li *ngFor="let employee of employees">{{employee.name}}</li> //error with property name
app.component.ts
import { Component } from '#angular/core';
import { EmployeeService } from './service/employee.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'List of employees';
employees: EmployeeService[]=[];
constructor(private employeeService: EmployeeService) {}
ngOnInit(): void {
this.employeeService.getUsers().subscribe(emp => {
this.employees = emp;
})
}
}
employee.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class EmployeeService {
constructor(private http: HttpClient) { }
getUsers(): Observable<EmployeeService[]> {
return this.http.get<EmployeeService[]>('http://localhost:3000/employees')
}
}
db.json:
{
"employees": [
{
"id": 1,
"name": "Tim",
"hired": true
},
{
"id": 2,
"name": "Jess",
"hired": true
}
]
}
You could always see the data loaded to the template file using the json pipe operator,
Example <div>{{employees | json }}</div> , this will help you to understand the structure of data and access it correctly.
Solution to your problem:
The response from your getUsers() returns an object, that contains an array for employees. You were trying to access the data object.
Instead, you should retrieve the employees data from the object and loop through the employees data in your template file.
In your app.component.ts component:
import { Component } from '#angular/core';
import { EmployeeService } from './service/employee.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'List of employees';
employees: any[]=[]; // preferably a custom type/interface than 'any'
constructor(private employeeService: EmployeeService) {}
ngOnInit(): void {
this.employeeService.getUsers().subscribe(emp => {
this.employees = emp.employees; // ------------------> Change
})
}
}
Inside your app.component.html template:
<div *ngFor="let employee of employees">{{ employee.name }}</div>
In your employee.service.ts service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class EmployeeService {
constructor(private http: HttpClient) { }
// Your custom interface/type should be the return type
getUsers(): Observable<any[]> {
return this.http.get('http://localhost:3000/employees');
}
}
Working app in Stackblitz
// check whether the employee list is not empty before rendering it in UI
<ul *ngIf="employees">
<li *ngFor="let employee of employees">{{ employee.name }}</li>
</ul>
// To view complete JSON dump in web page follow below: Add this import statement to your app module
import { CommonModule } from '#angular/common';
then include it in imports
#NgModule({
...,
imports: [CommonModule, ...],
})
<!-- in your app.html: -->
<div *ngIf ="employees">
{{ employees | json}}
</div>
I created all-question component it is visible but when i click on particular question view that redirect to that id but not got details on the id page. here i attached some files,
Admin Service file
Model file
Detail question component file
Detail question ts file
All question component file
All question ts file
admin.service.ts - this is admin service file
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Admin } from "../model/admin";
#Injectable({
providedIn: 'root'
})
export class AdminService {
private ROOT_URL = "http://localhost:3300/questions";
private httpOptions = {
headers: new HttpHeaders().set("Content-Type", "application/json")
};
constructor(private http: HttpClient) { }
getQuestion(): Observable<Admin[]> {
return this.http.get<Admin[]>(this.ROOT_URL);
}
getQue(id: string){
return this.http.get<Admin>(`${this.ROOT_URL}/${id}`);
}
addQue(admin){
return this.http.post<any>(this.ROOT_URL, admin, this.httpOptions);
}
}
**model=>admin.ts** - this is model file
export interface Admin {
description: String,
// alternatives: String
alternatives: [
{
text: {
type: String,
required: true
},
isCorrect: {
type: Boolean,
required: true,
default: false
}
}
]
}
**que-detail.comp.html** - this is question detail component file
<div class="card">
<div class="card-body">
<h4>Question: {{admin.description}}</h4>
<h4>Options: {{admin.alternatives}}</h4>
</div>
</div>
**que-detail.comp.ts** - this is question details ts file
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Subscription } from 'rxjs';
import { Admin } from '../model/admin';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-question-detail',
templateUrl: './question-detail.component.html',
styleUrls: ['./question-detail.component.css']
})
export class QuestionDetailComponent implements OnInit {
id: string;
admin: Admin;
adminSub$: Subscription;
constructor(private adminService: AdminService, private route: ActivatedRoute ) { }
ngOnInit(): void {
this.id = this.route.snapshot.paramMap.get("id");
this.adminSub$ = this.adminService.getQue(this.id).subscribe(admin => {
this.admin = admin;
});
}
}
**all-que.comp.html** - this is all question component file
<div class="card bg-dark text-white my-4 dashboard-bg">
<div class="card-body text-center">
<ul class="questions" *ngIf="admin$ | async as questions">
<li *ngFor="let question of questions">
<h4 class="card-title my-2">{{question.description}}</h4>
<h5 *ngFor="let x of question.alternatives">{{x.text}}</h5>
<a [routerLink]="question._id">View</a>
</li>
</ul>
</div>
</div>
**all-que.comp.ts** - this is all question ts file
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
import { Admin } from '../model/admin';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-all-question',
templateUrl: './all-question.component.html',
styleUrls: ['./all-question.component.css']
})
export class AllQuestionComponent implements OnInit {
admin$: Observable<Admin[]>;
constructor(private adminService: AdminService) { }
ngOnInit(): void {
this.admin$ = this.adminService.getQuestion()
}
}
Your Question Details Component should be like,
<div class="card">
<div class="card-body">
<h4>Question: {{admin.description}}</h4>
<h5>Options:</h5>
<ng-container *ngFor="alternative of admin.alternatives">
<h6>{{alternative.text}}</h6>
</ng-container>
</div>
You are just using the undefined questions variable ig.
I'm trying to display the data for every corresponding name(data from json-server) like firstName, lastName, email etc.
user.component.html - this is where I want to display the corresponding data
<div>
<h2>{{selectedUser.firstName | uppercase}} Details</h2>
<div><span>id: </span>{{selectedUser.id}}</div>
<div>
<label>First name:
<input [(ngModel)]="selectedUser.firstName" placeholder="First Name" />
</label>
</div>
<div>
<label>Last name:
<input [(ngModel)]="selectedUser.lastName" placeholder="Last Name" />
</label>
</div>
<div>
<label>Email:
<input [(ngModel)]="selectedUser.email" placeholder="example.com" />
</label>
</div>
<div>
<label>Start date:
<input [(ngModel)]="selectedUser.startDate" placeholder="Start date" />
</label>
</div>
</div>
user.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { User } from '../user';
import { DataService } from '../data.service';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
users: any
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.getUsers()
.subscribe(data => this.users = data);
}
}
home.component.html - this is where I display the name for each user and when I click I want to display the content on the user page
<h2>Users</h2>
<ul class="users">
<a routerLink="/user"><li *ngFor="let user of users['users']"
[class.selected]="user === selectedUser"
(click)="onSelect(user)">
<span class="badge">{{user.id}}</span> {{user.firstName}} {{user.lastName}}
</li> </a>
</ul>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Router } from '#angular/router';
import { User } from '../user';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
public users = [];
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.getUsers()
.subscribe(data => this.users = data);
}
}
And here is the data.service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { Observable } from 'rxjs';
import { User } from "./user";
#Injectable({
providedIn: 'root'
})
export class DataService {
selectedUser: User;
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]>{
return this.http.get<User[]>("http://localhost:3000/users");
}
onSelect(user: User): void {
console.log(user);
this.selectedUser = user;
}
}
It's work when I display the names on the home page.
I've also made this display of data on a single page(home.component) and it worked, but now I must to display it on another page. I've tried to do this with BehaviourSubject but I got stuck.
You are incorrectly accessing the property in your .html files. Example:
<h2>Users</h2>
<ul class="users">
<a routerLink="/user"><li *ngFor="let user of users['users']"
[class.selected]="user === selectedUser"
(click)="onSelect(user)">
<span class="badge">{{user.id}}</span> {{user.firstName}} {{user.lastName}}
</li> </a>
</ul>
Should be:
<h2>Users</h2>
<ul class="users">
<a routerLink="/user"><li *ngFor="let user of users['users']"
[class.selected]="user === dataService.selectedUser"
(click)="dataService.onSelect(user)">
<span class="badge">{{user.id}}</span> {{user.firstName}} {{user.lastName}}
</li> </a>
</ul>
You can let the DataService cache the users for you in a BehaviorSubject and all components bind to users$ property of the DataService.
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
import { HttpClient, HttpResponse, HttpErrorResponse } from '#angular/common/http';
import { User } from "./user";
#Injectable({
providedIn: 'root'
})
export class DataService {
isLoading$ = new BehaviorSubject<boolean>(false);
users$ = new BehaviorSubject<User[]>(undefined);
constructor(private http: HttpClient) { }
loadUsers(): void {
this.isLoading$.next(true);
this.http.get<User[]>("http://localhost:3000/users", { observe: "response" }).subscribe(
(res: HttpResponse<User[]>) => {
this.users$.next(res.body);
},
(res: HttpErrorResponse) => this.onError(res),
() => this.isLoading$.next(false)
);
}
private onError(res: HttpErrorResponse) {
console.log(res.message);
}
}
I have a problem with Angular 2 routing. When I click on my link to get the team details, it takes the right route and loads the component specified (TeamComponent). But, immediately "gets back" to the previous component (TeamsComponent), which is the teams list.
This is the structure of my project:
/app
|_shared
|_team
|_team.component.css
|_team.component.html
|_team.component.ts
|_team.model.ts
|_team.service.ts
|_team-list
|_team-list.component.css
|_team-list.component.html
|_team-list.component.ts
|_teams
|_teams.component.css
|_teams.component.html
|_teams.component.ts
|_teams.module.ts
|_teams-routing.module.ts
First, I set the routes on teams-routing.module.ts:
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { TeamsComponent } from './teams.component';
import { TeamComponent } from '../shared/team/team.component';
const teamsRoutes: Routes = [
{
path: 'team/:id',
component: TeamComponent
},{
path: 'teams',
component: TeamsComponent
}
];
#NgModule({
imports: [
RouterModule.forChild(teamsRoutes)
]
})
export class TeamsRoutingModule { }
Load the team list from the teamService on teams.component.ts and send it to team-list on teams.component.html:
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/from';
import { TeamService } from '../shared/team/team.service';
import { Team } from '../shared/team/team.model';
#Component({
selector: 'app-teams',
templateUrl: './teams.component.html',
styleUrls: ['./teams.component.css']
})
export class TeamsComponent implements OnInit {
teamList: Team[] = [];
constructor(private teamService: TeamService) { }
ngOnInit() {
this.teamService.getTeams().subscribe(teams => {
Observable.from(teams).subscribe(team => {
this.teamList.push(team);
});
});
}
}
teams.component.html
<section class="teams">
<app-team-list [teams]="teamList"></app-team-list>
</section>
Then, with my teams list, I set the HTML list on team-list.component.html:
<section *ngFor="let team of teams" class="team_list">
<div class="card" style="width: 20rem;">
<img class="card-img-top" src="/assets/logos/{{team.logo}}" alt="Team Logo">
<div class="card-block">
<h4 class="card-title">{{team.name}}</h4>
<p class="card-text">{{team.location}}</p>
<a routerLink="/team/{{team.id}}" class="btn btn-primary">Team Info</a>
</div>
</div>
</section>
Finally, I get the team info from param "id" and the service in team.component.ts:
import { Component, Input, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { Team } from './team.model';
import { TeamService } from "./team.service";
import 'rxjs/add/operator/switchMap';
#Component({
selector: 'app-team',
templateUrl: './team.component.html',
styleUrls: ['./team.component.css']
})
export class TeamComponent implements OnInit {
team: Team = null;
constructor(private teamService: TeamService,
private activatedRoute: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
let teamId: number = this.activatedRoute.snapshot.params['id'];
console.log("Vamos a buscar el equipo");
this.teamService.getTeamById(teamId).subscribe(team => this.team = team);
}
}
It loads the TeamComponent HTML with the team data, but gets back to /teams direction (and doesn't print the team list). I tried to change the routes names (/detail/:id for example) but still doesn't work. Any suggestions? Thanks in advance.
Ok, got it. Your request will be exuted async, so at the creation-time of your component, team is null. I think you have a binding like this in your TeamComponent:
{{ team.name }}
If team is null, name cannot be accessed and it crashes. To be sure the html will be rendered without errors, use the elvis-operator like this:
{{ team?.name }}
This will only access name if team is not null or undefined
Update: The getTeamById service
getTeamById(id: number): Observable<Team> {
let team: Team = null;
return this.http.get(this.urlTeams+'/'+id)
.map(response => {
let dbTeam: any = response.json();
for(let i in dbTeam) {
team = new Team(dbTeam[i].teamId,dbTeam[i].teamName,dbTeam[i].teamLocation,dbTeam[i].teamFoundation,dbTeam[i].teamDivision,dbTeam[i].teamConference,dbTeam[i].teamStadium,dbTeam[i].teamAttendance,dbTeam[i].teamLogo,dbTeam[i].teamStadiumPhoto);
}
return team;
})
.catch(this.handleError);
}