How to display object data in child component from parent in angular - javascript

I am a beginner in Angular.
I have created a demo app.
below is my code
servers.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { Server } from './server.model';
#Component({
selector: 'app-servers',
templateUrl: './servers.component.html',
styleUrls: ['./servers.component.css'],
})
export class ServersComponent implements OnInit {
allowNewServer = false;
serverCreationStatus = 'no server created';
serverName = '';
ipAddress = '';
owner = '';
server: Server[] = [];
serverCreated = false;
constructor() {
}
ngOnInit(): void {}
onCreateServer() {
this.serverCreated = true;
this.serverCreationStatus = 'server created';
this.server_list.push(server;
}
}
I have one model file where i have declared one class and constructor to create server
server.model.ts
export class Server {
public serverName: string;
public ipAddress: string;
public owner: string;
constructor(ame: string, ip: string, owner: string) {
this.serverName = name;
this.ipAddress = ip;
this.owner = owner;
}
}
in servers.component.html i am using ngModel to bind object property with forms field
servers.component.html
<form action="">
<div class="row">
<label for="serverName" class="col-sm-2">Server Name</label>
<input
type="text"
class="form-control col-sm-10"
[(ngModel)]="server.serverName"
[ngModelOptions]="{ standalone: true }"
/>
</div>
<div class="row">
<label for="ipAddress" class="col-sm-2">IP Adress</label>
<input
type="text"
class="form-control col-sm-10"
[(ngModel)]="server.ipAddress"
[ngModelOptions]="{ standalone: true }"
/>
</div>
<div class="row">
<label for="owner" class="col-sm-2">Owner</label>
<input
type="text"
class="form-control col-sm-10"
[(ngModel)]="server.owner"
[ngModelOptions]="{ standalone: true }"
/>
</div>
<button
class="btn btn-primary"
[disabled]="!allowNewServer"
(click)="onCreateServer()"
>
Add server
</button>
</form>
<app-server *ngFor="let s of server" [data]="s"> </app-server>
and my child component `server.component.ts`
import { Component, Input } from '#angular/core';
import { Server } from '../servers/server.model';
#Component({
selector: 'app-server',
templateUrl: './server.component.html',
styles: [
`
.online {
color: white;
}
`,
],
})
export class ServerComponent {
serverStatus = 'offline';
#Input() data: Server;
constructor() {
this.serverStatus = Math.random() > 0.5 ? 'online' : 'offline;';
}
getColor() {
return this.serverStatus === 'online' ? 'green' : 'red';
}
}
my server.component.html
<p
[ngStyle]="{ backgroundColor: getColor() }"
[ngClass]="{ online: serverStatus === 'online' }"
>
Server created with name {{ data }}
</p>
i am gettig two error in above code
when using ngModel in servers.component.html
error TS2339: Property 'ipAddress' does not exist on type 'Server[]'.
34 [(ngModel)]="server.ipAddress"
getting this error for all field
2.not able to display created server data (not able to pass object data to child component)
calling child component in servers.component.html
Please help.
Thanks in advance

You have too many typos and errors, Here is the updated servers and server component classes, Compare this with your question and you can figure out the differences and errors. Hope this works !!
servers.component .ts & .html:
.html:
<form action="">
<div class="row">
<label for="serverName" class="col-sm-6">Server Name</label>
<input
id="serverName"
type="text"
class="form-control col-sm-10"
[(ngModel)]="serverName"
[ngModelOptions]="{ standalone: true }"
/>
</div>
<div class="row">
<label for="ipAddress" class="col-sm-6">IP Adress</label>
<input
id="ipAddress"
type="text"
class="form-control col-sm-10"
[(ngModel)]="ipAddress"
[ngModelOptions]="{ standalone: true }"
/>
</div>
<div class="row">
<label for="owner" class="col-sm-6">Owner</label>
<input
id="owner"
type="text"
class="form-control col-sm-10"
[(ngModel)]="owner"
[ngModelOptions]="{ standalone: true }"
/>
</div>
<button
class="btn btn-primary"
(click)="onCreateServer()">
Add server
</button>
</form>
<app-server *ngFor="let s of server" [data]="s"> </app-server>
.ts:
import { Component, OnInit } from '#angular/core';
import {Server} from '../../models/Server.model';
#Component({
selector: 'app-servers',
templateUrl: './servers.component.html',
styleUrls: ['./servers.component.css']
})
export class ServersComponent implements OnInit {
allowNewServer = false;
serverCreationStatus = 'no server created';
serverName = '';
ipAddress = '';
owner = '';
server: Server[] = [];
serverCreated = false;
constructor() {
}
ngOnInit(): void {}
onCreateServer() {
this.serverCreated = true;
this.serverCreationStatus = 'server created';
this.server.push(new Server(this.serverName, this.ipAddress, this.owner));
}
}
server.component .ts & .html:
.html:
<p
[ngStyle]="{ backgroundColor: getColor() }"
[ngClass]="{ online: serverStatus === 'online' }">
Server created with name {{ data.serverName }}
</p>
.ts:
import {Component, Input} from '#angular/core';
import {Server} from '../../models/Server.model';
#Component({
selector: 'app-server',
templateUrl: './server.component.html',
styleUrls: ['./server.component.css']
})
export class ServerComponent {
#Input() data: Server;
serverStatus = 'offline';
constructor() {
this.serverStatus = Math.random() > 0.5 ? 'online' : 'offline;';
}
getColor() {
return this.serverStatus === 'online' ? 'green' : 'red';
}
}
finally Server model class:
export class Server {
public serverName: string;
public ipAddress: string;
public owner: string;
constructor(name: string, ipAddress: string, owner: string) {
this.serverName = name;
this.ipAddress = ipAddress;
this.owner = owner;
}
}

Related

Angular how to wrap input value with double curly braces

What I want to achieve is that when I write inside input field "Foo"
it will become {{Foo}}
First create this directive :
#Directive({
selector: '[format-input]',
})
export class FormatDirective implements DoCheck {
valueIsNull:boolean = true;
constructor(public _elementRef: ElementRef<HTMLInputElement>,
private _renderer: Renderer2) { }
ngDoCheck(): void {
setTimeout(() => {
if(this.valueIsNull){
this.format();
}
}, 150)
fromEvent(this._elementRef.nativeElement, 'blur')
.pipe(
debounceTime(150),
distinctUntilChanged(),
tap(() => {
this.format();
})
)
.subscribe();
}
format(){
this._elementRef.nativeElement.value = "{{ " + this._elementRef.nativeElement.value + " }}"
this.valueIsNull = false;
}
}
Then Import it to your module : e.g app.module :
#NgModule({
declarations: [
FormatDirective
],
imports: [CommonModule],
exports: [
FormatDirective
]
})
export class AppModule { }
Then you can use it anywhere you want :
<input type="text" format-input />
You should use angular templating to achieve this Official Documentation for templating and interpolation and a code sample is given below. this will help you to achieve your usecase.
https://angular.io/guide/interpolation
https://angular.io/api/forms/NgModel
import {Component} from '#angular/core';
import {NgForm} from '#angular/forms';
#Component({
selector: 'example-app',
template: `
<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
<input name="first" ngModel required #first="ngModel">
<input name="last" ngModel>
<button>Submit</button>
</form>
<p>First name value: {{ first.value }}</p>
<p>First name valid: {{ first.valid }}</p>
<p>Form value: {{ f.value | json }}</p>
<p>Form valid: {{ f.valid }}</p>
`,
})
export class SimpleFormComp {
onSubmit(f: NgForm) {
console.log(f.value); // { first: '', last: '' }
console.log(f.valid); // false
}
}
Thanks
Rigin Oommen

i get error Cannot read properties of undefined (reading 'include')

I get this error when i click login button in my login page
: this error is here :
Cannot read properties of undefined (reading 'include') when i call
window.location.reload();
what is the problem, I searched but did not find the problem
I am thankful with your guidance
my ts code is here :
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../_services/auth.service';
import { TokenStorageService } from '../_services/token-storage.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
form: any = {
username: null,
password: null
};
isLoggedIn = false;
isLoginFailed = false;
errorMessage = '';
roles: string[] = [];
constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
ngOnInit(): void {
if (this.tokenStorage.getToken()) {
this.isLoggedIn = true;
this.roles = this.tokenStorage.getUser().roles;
}
}
onSubmit(): void {
const { username, password } = this.form;
this.authService.login(username, password).subscribe(
data => {
this.tokenStorage.saveToken(data.accessToken);
this.tokenStorage.saveUser(data);
this.isLoginFailed = false;
this.isLoggedIn = true;
this.roles = this.tokenStorage.getUser().roles;
this.reloadPage();
},
err => {
this.errorMessage = err.error.message;
this.isLoginFailed = true;
}
);
}
reloadPage(): void {
window.location.reload();
}
}
and my html code is
<div id="image">
<div class="container">
<div class="row justify-content-center">
<div class="col-sm-10 col-md-8 col-lg-6 col-xl-5 p-5 ">
<mat-card>
<mat-card-header >
<mat-card-title> </mat-card-title>
</mat-card-header>
<mat-card-content>
<img src="https://s4.uupload.ir/files/user3_r3fq.png" class="p-2">
<form *ngIf="!isLoggedIn"
name="form"
(ngSubmit)="f.form.valid && onSubmit()"
#f="ngForm"
novalidate
>
<div class="form-group justify-content-center ">
<mat-form-field >
<label for="username"></label>
<i class="fa fa-user icon"></i>
<input matInput
type="text"
name="username"
placeholder="Username"
[(ngModel)]="form.username"
required
#username="ngModel"
/></mat-form-field>
<div id="mes"
role="alert"
*ngIf="username.errors && f.submitted"
>
please enter user name
</div>
</div>
<div class="form-group"> <mat-form-field>
<label for="password"></label>
<input matInput
type="password"
placeholder="Password"
name="password"
[(ngModel)]="form.password"
required
minlength="4"
#password="ngModel"
/>
</mat-form-field>
<div
role="alert"
*ngIf="password.errors && f.submitted"
>
<div id="mes" *ngIf="password.errors.required">Please Enter pass</div>
<div *ngIf="password.errors.minlength">
Password must be at least 6 characters
</div>
</div>
</div>
<div >
<button mat-raised-button class="btn btn-primary" >
<span>Login</span>
</button>
</div>
<div >
<div
role="alert"
*ngIf="f.submitted && isLoginFailed"
>
Login failed: {{ errorMessage }}
</div>
</div>
</form>
</mat-card-content>
</mat-card>
<div *ngIf="isLoggedIn">
Logged in as {{ roles }}.
</div>
</div>
</div>
</div>
</div>
and my user.service.ts is
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
const API_URL = 'http://localhost:8080/api/test/';
#Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) { }
getPublicContent(): Observable<any> {
return this.http.get(API_URL + 'all', { responseType: 'text' });
}
getUserBoard(): Observable<any> {
return this.http.get(API_URL + 'user', { responseType: 'text' });
}
getModeratorBoard(): Observable<any> {
return this.http.get(API_URL + 'mod', { responseType: 'text' });
}
getAdminBoard(): Observable<any> {
return this.http.get(API_URL + 'admin', { responseType: 'text' });
}
}
and my auth.service.ts is
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
const AUTH_API = 'http://192.168.1.135:8282/api/Users/authenticate';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: 'root'
})
export class AuthService {
m:any;
constructor(private http: HttpClient) { }
login(username: string, password: string): Observable<any> {
this.m= this.http.post(AUTH_API , {username,password}, httpOptions);
console.log(this.m);
return this.m;
}
register(username: string, email: string, password: string): Observable<any> {
return this.http.post(AUTH_API + 'signup', {
username,
email,
password
}, httpOptions);
}
}
Seem you have issues in this line, you need to check if getUser is returning what you need. To suppress the error you need to change:
this.roles = this.tokenStorage.getUser().roles;
Try changing to:
this.roles = this.tokenStorage.getUser()?.roles;

Component communication in Angular fails from child to parent component

I am trying to apply #Output in my sample project following this tutorial. I am unable to get it working. There are no compilation errors or any specific error for this in console. I am unable to figure out how to proceed with debugging #Output using developer tools. Below are my files.
app-component.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
serverElements = [{type: 'server', name: 'testServer', content: 'Just a Server'}];
constructor(){
console.log(this.serverElements);
}
onServerAdded(serverData: {serverName: string, serverContent: string}) {
console.log("Server Added");
this.serverElements.push({
type: 'server',
name: serverData.serverName,
content: serverData.serverContent
});
console.log("Server Added");
}
onBlueprintAdded(blueprintData: {serverName: string, serverContent: string}) {
this.serverElements.push({
type: 'blueprint',
name: blueprintData.serverName,
content: blueprintData.serverContent
});
}
}
app-component.html
<app-cockpit (serverAdded)="onServerAdded($event)"
(blueprintAdded)="onBlueprintAdded($event)"></app-cockpit>
<hr>
<div class="row">
<div class="col-xs-12">
<app-server-element *ngFor="let serverElement of serverElements" [element] = "serverElement"></app-server-element>
</div>
</div>
</div>
cockpit-component.ts
#Component({
selector: 'app-cockpit',
templateUrl: './cockpit.component.html',
styleUrls: ['./cockpit.component.css']
})
export class CockpitComponent implements OnInit {
#Output() serverAdded = new EventEmitter<{serverName: string, serverContent: string}>();
#Output() blueprintAdded = new EventEmitter<{blueprintName: string, blueprintContent: string}>();
newServerName = '';
newServerContent = '';
constructor() { }
ngOnInit(): void {
}
onAddServer() {
console.log(this.newServerContent);
console.log(this.newServerName);
this.serverAdded.emit({
serverName:this.newServerName,
serverContent:this.newServerContent
});
}
onAddBlueprint() {
this.blueprintAdded.emit({
blueprintName:this.newServerName,
blueprintContent:this.newServerContent
});
}
}
cockpit-component.html
<div class="col-xs-12">
<p>Add new Servers or blueprints!</p>
<label>Server Name</label>
<input type="text" class="form-control" [(ngModel)]="newServerName">
<label>Server Content</label>
<input type="text" class="form-control" [(ngModel)]="newServerContent">
<br>
<button
class="btn btn-primary"
(click)="onAddServer()">Add Server</button>
<button
class="btn btn-primary"
(click)="onAddBlueprint()">Add Server Blueprint</button>
</div>
</div>
I can see the console prints in the browser for cockpit-component.ts file but this does not lead to a call to onServerAdded() method in app component.
It works now after starting angular and vscode again

Add Select inside Angular FormGroup and bind selected value

I have a form with a text input and then I want to add a select inside that Form:
The select is an array:
typeAlert: TypeAlert[] = [
{ value: 'MEDICAL'},
{ value: 'POLICE'},
{ value: 'FIRE'}
];
I want to bind the value selected on the select. I have a var called select to store which the user has selected. I have to add the selected inside formGroup?
Also I am getting this error:
ngModel cannot be used to register form controls with a parent formGroup directive
component.html
<form class="container" [formGroup]="alertForm">
<div class="actions">
<div class="action" [routerLink]="['/alertes']">
<span>Torna a Alertes</span>
</div>
<div class="space"></div>
<button class="action" (click)="save()" [disabled]="!alertForm.valid">
Guardar
</button>
</div>
<div class="dades">
<div class="card">
<div class="card-title">
<b>Title</b>
</div>
<div class="card-content">
<mat-form-field>
<mat-label>Title</mat-label>
<input formControlName="title" matInput>
</mat-form-field>
</div>
</div>
</div>
<div class="dades">
<div class="card">
<div class="card-title">
<b>Type Alert</b>
</div>
<div class="card-content">
<mat-form-field appearance="fill">
<mat-label>Type Alert</mat-label>
<mat-select [(ngModel)]="selected">
<mat-option *ngFor="let alert of typeAlert" [value]="alert.value">
{{alert.value}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
</form>
component.ts:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { FormGroup, FormControl, FormArray, Validators } from '#angular/forms';
import { ActivatedRoute, Router } from '#angular/router';
import { AlertesService } from 'src/app/core/services/alertes.service';
import { MatDialog } from '#angular/material/dialog';
import { DialogNotificationsComponent } from 'src/app/shared/components/dialog-notifications/dialog-notifications.component';
interface TypeAlert {
value: string;
}
#Component({
selector: 'app-alert',
templateUrl: './alert.component.html',
styleUrls: ['./alert.component.scss']
})
export class AlertComponent implements OnInit, OnDestroy {
typeAlert: TypeAlert[] = [
{ value: 'MEDICAL'},
{ value: 'POLICE'},
{ value: 'FIRE'}
];
selected: string = this.typeAlert[0].value;
alertForm: FormGroup;
alert;
constructor(
private alertesService: AlertesService,
private route: ActivatedRoute,
private router: Router,
public dialog: MatDialog
) { }
ngOnInit() {
this.alert = this.route.snapshot.data.alert;
this.initForm();
}
ngOnDestroy() {
}
initForm() {
this.alertForm = new FormGroup({
title: new FormControl(this.alert ? this.alert.title : '', [Validators.required]),
});
}
save() {
console.log(this.selected);
if(this.alert) {
this.alertesService.update(this.alert._id, this.alertForm.value).subscribe(() => {
this.router.navigate(['/alertes'])
})
}
else {
const dialogRef = this.dialog.open(DialogNotificationsComponent, {
width: '600px',
data: {title: "Nova alerta", msg: this.alertForm.value.title}
});
dialogRef.afterClosed().subscribe(result => {
console.log(result);
if(result != undefined && result != null) {
this.alertesService.create({
notification: result ? result: null,
...this.alertForm.value
}).subscribe(() => {
this.router.navigate(['/alertes'])
})
}
});
}
}
}
You should [formControlName] with form groups, instead of ngmodel.
Please check https://angular.io/api/forms/FormControlName

Accessing ControlValueAccessor from different component

I have component where I implement ControlValueAccessor and I'm having problems understanding the correct way to use it:
import { Component, OnInit, forwardRef, Output, EventEmitter, OnChanges, Input, ViewChild } from '#angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '#angular/forms';
import { UserOrEmail } from '../entities/UserOrEmail';
export const USER_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
// tslint:disable-next-line
useExisting: forwardRef(() => AddUserOrEmailComponent),
multi: true,
};
const noop = () => {
// Placeholder operation
};
#Component({
selector: 'app-add-user-or-email',
templateUrl: './add-user-or-email.component.html',
providers: [USER_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class AddUserOrEmailComponent implements OnInit, ControlValueAccessor {
#Input()
user: any = UserOrEmail;
#Output()
change: EventEmitter<UserOrEmail> = new EventEmitter<UserOrEmail>();
users: any = [];
ngOnInit() {
this.user = {
userId: 'ull',
name: 'null',
email: 'null'
};
this.users = ConstantService.UserArray;
}
// #region [ Value Accessor Interface ]--------------------------------------------------------
// Placeholders for the callbacks which are later provided
// by the Control Value Accessor
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: any) => void = noop;
get value(): any {
return this.user;
}
// [ ControlValueAccessor interface implementation ]-------------------------------------------
set value(v: any) {
if (this.user !== v) {
this.user = <UserOrEmail>v;
this.onChangeCallback(v);
this.change.next(this.user);
}
}
writeValue(value: any) {
if (value !== this.user)
this.user = <UserOrEmail>value;
}
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
}
and html:
<div>
<div class="form-column">
<div class="form-row">
<label>
{{'GENERIC.USER'|translate}}
</label>
<select>
<option [ngValue]="'default'"></option>
<option *ngFor="let user of users" [ngValue]="user">{{user.login}}</option>
</select>
</div>
<div class="form-row">
<label for="addPersonEmail" >{{'GENERIC.EMAIL' | translate}}</label>
<input type="email" placeholder="{{'GENERIC.EMAIL'|translate}}" pattern="^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$">
</div>
</div>
</div>
I try to use it in another component:
hmtl:
<app-modal class="form" #addMilestoneModal [width]="550">
<div header>{{'MODALS.ADD_MILESTONE'|translate}}</div>
<div body>
<div class="form-column">
<div class="form-value">
<app-add-user-or-email #addUser [(ngModel)]="milestone.assignee"></app-add-user-or-email>
</div>
<div class="form-row">
<label for="addMilestoneDescription">{{'GENERIC.DESCRIPTION' | translate}}</label>
<textarea style="height: 150px" [(ngModel)]="milestone.description"></textarea>
</div>
</div>
</div>
<div footer class="flex-container">
<button class="flex-item-row btn btn-a" [disabled]="!milestone.description" (click)="apply()">{{'GENERIC.APPLY'
| translate}}</button>
</div>
</app-modal>
Typescript:
import { Component, ViewChild, EventEmitter } from '#angular/core';
import { ModalComponent } from '../../widgets/modal/modal.component';
import { Milestone } from '../../entities/Milestone';
import { ConstantService } from '../../services/ConstantService';
import { AddUserOrEmailComponent } from '../../add-user-or-email/add-user-or-email.component';
#Component({
selector: 'app-add-milestone-modal',
templateUrl: './add-milestone-modal.component.html'
})
export class AddMilestoneModalComponent {
#ViewChild('addMilestoneModal')
modal: ModalComponent;
cs = ConstantService;
emitter: EventEmitter<Milestone> = new EventEmitter<Milestone>();
milestone: any = Milestone;
apply() {
console.log(this.milestone); // <---- HERE IT SHOULD BE ACCESSED
debugger;
this.emitter.next(this.milestone);
this.modal.close();
}
cancel() {
this.emitter.next(null);
this.modal.close();
}
}
I should get it in milestone object but it is empty. What am I missing?
I had problems with this a couple of weeks ago and made a StackBlitz with a good example.
Hopefully this can help you?
https://stackblitz.com/edit/mat-select-with-controlvalueaccessor

Categories