Angular passing data to child component - javascript

Hey I am trying to display a simple error message on my login page if the login fails. Following is my login.component.html:
<div class="container shadow login-container">
<div class="row">
<div class="col-sm-12 text-center">
<div class="error-message">
<app-server-error [errorMessage]="error" ></app-server-error> -----> not displaying on screen
</div>
<div class="login-form-container">
<div class="login-input-container">
<input [(ngModel)]="user.email" type="email" placeholder="Enter your email" class="buddha-input"/>
</div>
<div class="login-input-container">
<input [(ngModel)]="user.password" type="password" placeholder="Enter password" class="buddha-input"/>
</div>
<div class="login-input-container">
<button (click)="tryLogin()" class="login-form-button">Login</button>
</div>
</div>
</div>
</div>
</div>
Following is my server-error.component.html:
<p>
{{errorMessage}}
</p>
Following is my server-error.component.ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-server-error',
templateUrl: './server-error.component.html',
styleUrls: ['./server-error.component.css']
})
export class ServerErrorComponent implements OnInit {
#Input() public errorMessage: string;
constructor() { }
ngOnInit() {
}
}
"Error" is not showing up on the screen and I am not getting errors on console either. Please do let me know how I can fix this? Thank you

#Input() public errorMessage: string; expects error to be a string.
Define error like this in .ts
error = 'error message'
Working Demo

With [errorMessage]="error", error should be a variable.
So you need to define it in your component.
But if you want to display the string "error",
then pass it like this [errorMessage]="'error'" or errorMessage="error"

The other posted answer may solve your problem but I would go with Service which will be responsible for showing an error from everywhere in the Application and also you can have a full control on the error component as it is centralized at a one place:
error.service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class ErrorService {
constructor() { }
private errorMessages = new BehaviorSubject('');
errorMessage = this.errorMessages.asObservable();
showError(message: string) {
this.errorMessages.next(message)
}
}
Error.Component.ts:
import { Component, OnInit, Input } from '#angular/core';
import {ErrorService } from "../error.service";
#Component({
selector: 'app-server-error',
templateUrl: './server-error.component.html',
styleUrls: ['./server-error.component.css']
})
export class ServerErrorComponent implements OnInit {
private errorMessage : any;
constructor(public errorService : ErrorService) { }
ngOnInit() {
this.errorService.errorMessage.subscribe(value => {
this.errorMessage = value;
})
}
}
App.Component.html:
// show it in app.component
<div class="error-message">
<app-server-error></app-server-error>
</div>
Use (in Login Component):
import { Component, OnInit } from "#angular/core";
import {ErrorService } from "../error.service";
#Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.css"]
})
export class LoginComponent implements OnInit {
constructor(public errorService: ErrorService) {}
user = {};
ngOnInit() {}
tryLogin(){
this.errorService.showError('An error');
}
}

Related

Error message : can't bind to xxx since it isn't a known property of yyy when calling angular component

I created in my Angular project a shared component named useraccount-type. I use it into a form but I have this error message :
Can't bind to 'selectedType' since it isn't a known property of 'app-useraccount-type'.
If 'app-useraccount-type' is an Angular component and it has 'selectedType' input, then verify that it is part of this module.
If 'app-useraccount-type' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '#NgModule.schemas' of this component to suppress this message.
To allow any property add 'NO_ERRORS_SCHEMA' to the '#NgModule.schemas' of this component.
The html part looks this:
<div [formGroup]="myForm">
<select class="form-select form-control" id="useraccountType" formControlName="useraccountType" [(ngModel)]="selectedType">
<option *ngFor="let t of types" [ngValue]="t.description" >{{t.description}}</option>
</select>
</div>
The ts part looks this :
import { Component, Input, OnInit } from '#angular/core';
import { ControlContainer, FormGroup } from '#angular/forms';
import { ErrorHandlerService } from 'src/app/services/error-handler.service';
import { RepositoryService } from 'src/app/services/repository.service';
import { environment } from 'src/environments/environment';
// Classes
import { Useraccounttype } from './../../../model/useraccounttype.model';
#Component({
selector: 'app-useraccount-type',
templateUrl: './useraccount-type.component.html',
styleUrls: ['./useraccount-type.component.css']
})
export class UseraccountTypeComponent implements OnInit {
public errorMessage: string = '';
#Input() public types: Useraccounttype[];
#Input() public selectedType?: string ="";
#Input() public myForm: FormGroup;
constructor(private repository: RepositoryService, private controlContainer: ControlContainer, private errorHandler: ErrorHandlerService) { }
ngOnInit(): void {
this.myForm = <FormGroup>this.controlContainer.control;
this.getUserAccountTypes();
console.log('Selected type' + this.selectedType);
}
getUserAccountTypes = () => {
let apiType: string = environment.apiAddress + 'UserAccountType';
this.repository.getData(apiType)
.subscribe(response => {
this.types = response as Useraccounttype[];
},
(error) => {
this.errorHandler.handleError(error);
this.errorMessage = this.errorHandler.errorMessage;
})
}
}
The compent is used in a form like this:
<form [formGroup]="myForm" autocomplete="off" novalidate>
...
<div class="col-md-12">
<app-useraccount-type name="useraccountType" id="useraccountType" [formGroup]="myForm" [selectedType]="user?.userAccountType?.shortDescription"></app-useraccount-type>
</div>
...
</form>
The error message is on this part :
[selectedType]="user?.userAccountType?.shortDescription"
Is there anybody has an idee of the problem?
Thanks in advance for your help.
PS: for informations I have imported FormModule and ReactiveFormsModule in the app.module.ts
the error means that the component <app-useraccount-type> has no #Input() selectedType
Ok problem is finaly solved.
I forgot to declare the useraccounttypecomponent in the useraccount.module.ts (in the Declaration part and export part).
Thanks

Is there a way to send a Bootstrap Datepicker value from one component to another?

I'm using Bootrap on my Angular Application and it is divided like this
This is my Datepicker.html file
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input class="form-control" placeholder="yyyy-mm-dd"
name="dp" [(ngModel)]="model" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary calendar" (click)="d.toggle()" type="button"></button>
</div>
</div>
</div>
</form>
This is my Datepicker.ts file
import { Component, OnInit } from '#angular/core';
import {NgbDateStruct, NgbCalendar} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'ngbd-datepicker-basic',
templateUrl: './datepicker.component.html',
styleUrls: ['./datepicker.component.css']
})
export class DatepickerComponent implements OnInit {
model: NgbDateStruct;
date: {year: number, month: number};
constructor(private calendar: NgbCalendar) {
}
selectToday() {
this.model = this.calendar.getToday();
}
ngOnInit(): void {
}
}
And my goal is to get the selected date from my datepicker component and send it of to my dayoverview component
Dayoverview.ts
import { Component, OnInit } from '#angular/core';
import { ɵangular_packages_core_testing_testing_a } from '#angular/core/testing';
import { PATIENTS } from './dummypatientdata';
#Component({
selector: 'app-dayoverview',
templateUrl: './dayoverview.component.html',
styleUrls: ['./dayoverview.component.css']
})
export class DayoverviewComponent implements OnInit {
patients = PATIENTS;
constructor() { }
ngOnInit(): void {
}
}
The Datepicker is an child component of the day overview.
If Datepicker is a child component of Dayoverview component you should use an EventEmitter in the child component and bind to that in the parent component.
An example can be found in the Angular documentation:
https://angular.io/guide/component-interaction#parent-listens-for-child-event

Pass boolean to another Angular component

I have two Angular components
results.table and results.query
I want to hide the table inside the results.table.component when the user clicks on the reset button within the results.query.component
Maybe I am doing this wrong with event emitter, or maybe there is a better way to do this
results.table HTML
<div *ngIf='results?.length>0'>
<table *ngIf="showResults" class='table'>
<tr>
<th>Result Name</th>
<th>Location</th>
</tr>
<tbody>
<ng-template ngFor let-results [ngForOf]='items' let-i="index">
<tr>
<td>
<span>{{result?.description}}</span>
</td>
<td>
<span>{{result?.location}}</span>
</td>
</tr>
</ng-template>
</tbody>
</table>
</div>
results.table TS
showResults: boolean = true;
showResults(event) {
console.log('this is not getting called')
if (event) {
this.showResults = false;
}
}
results.query HTML
<div class="panel-body">
<form (submit)="onSubmitClicked()">
<div class="row">
<div class="form-group col-md-12 col-xs-12">
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3">
<label class="col-md-12 col-xs-12 control-label no-margin no-padding">Location: </label>
<pg-radio-toggle-select class="col-md-12 col-xs-12 no-margin no-padding" name="locationChangeInput" [(ngModel)]="Location"
(selectedChanged)="onFilteringLocation($event)" [options]='locationOptions'>
</pg-radio-toggle-select>
</div>
<pg-inputfield name="description" class="col-xs-12 col-sm-3 col-md-3 col-lg-3" [(ngModel)]="paramsModel.description"
displaytext="Name:"></pg-inputfield>
</div>
</div>
<div>
<button type="reset" class="btnReset" (click)="reset()">Reset</button>
<button type="submit" name="btnSearch">Search</button>
</div>
</form>
</div>
results.query TS
import {Component, OnInit, EventEmitter, Output} from '#angular/core';
import * as _ from 'lodash';
import { LocationService } from '../location-service.service';
#Component({
selector: 'result-query',
templateUrl: './result-query.component.html',
styleUrls: ['./result-query.component.less'],
})
export class ResultQueryComponent implements OnInit {
#Output() showResults: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor(
private LocationService: LocationService,
) {
this.reset();
}
ngOnInit() {
this.reset();
}
onSubmitClicked() {
console.log('test')
}
reset(): void {
console.log('I am the reset king');
this.showResults = false;
this.showResults.emit(true);
this.onSubmitClicked();
}
}
If two components does have a parent child relationship, you can use #Input() #Output() decorators.
4 Ways to share data between angular components
Component Interaction
Input Output Example
Parent Component
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { Stephen } from '../stephen.model';
#Component({
selector: 'app-parent',
template: `
Hello, Mr. (or Ms.): {{ selectedName }}
`,
styleUrls: ['./parent.component.css'],
encapsulation: ViewEncapsulation.None
})
export class ParentComponent implements OnInit {
stephen: Stephen;
selectedName: string;
constructor() {
this.stephen = new Stephen();
this.selectedName = this.stephen.firstName;
}
ngOnInit() {
}
updateName(selectedName: string): void {
console.log('in parent');
this.selectedName = selectedName;
}
}
Child Component
import { Component, OnInit, ViewEncapsulation, Input, Output, EventEmitter } from '#angular/core';
import { Stephen } from '../../stephen.model';
#Component({
selector: 'app-child',
template: `
{{ stephen.firstName }}
{{ stephen.lastName }}
{{ stephen.fullName }}
`,
styleUrls: ['./child.component.css'],
encapsulation: ViewEncapsulation.None
})
export class ChildComponent implements OnInit {
#Input() stephen: Stephen;
#Output() onNameSelected: EventEmitter;
constructor() {
this.onNameSelected = new EventEmitter();
}
ngOnInit() {
}
clicked(name: string): void {
this.onNameSelected.emit(name);
}
}
Important - second solution
But in your case these 2 components don't seem to have a parent child relationship. If you want to share data between two components, you can create a share-able service. This service will contain and EventEmitter to which a component that needs latest change will subscribe in ngOnInit method and the component which will have latest data will call a function from this share-able service to emit that event.
share-able service
import { Injectable, Output, EventEmitter } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class MessengerService {
#Output() change: EventEmitter<any> = new EventEmitter();
sendData(data: any): any {
this.change.emit(data);
}
}
The component that want to know about this change will subscribe to this event in it the ngOnInit like this.
messengerService.change.subscribe(emitedValue => {
this.value = emitedValue;
});
The component that has the new change will call the sendData method is messenge / share-able service to post new data to the event subscribers whenever it is required.
I dont know if you forgot to write it in your question, but you should have a results.query Tag in your results.Table HTML, and call the output through it. Considering your selector is app-results-query, it would be like this:
results.table HTML
<app-results-query (showResults)="changeShowResults($event)"></app-results-query>
<table *ngIf="showResults">
//table stuff
</table>
results.table TS
showResults: boolean = true;
changeShowResults(event: boolean) {
console.log('this is not getting called')
if (event) {
this.showResults = false;
}
}

Angular form returning ERROR TypeError: "_co.service.formData is undefined"?

I'm trying to make an form in angular 7 and get inout in that but the form is giving the following error : ERROR TypeError: "_co.service.formData is undefined"
now here is my code for the html part of the component :-
<form (sumbit)="signUp(form)" autocomplete="off" #form="ngForm">
<div class="form-group">
<input name="Name" class="form-control" #Name="ngModel" [(ngModel)]="service.formData.Name" required>
</div>
</form>
while this is the type script code
import { Component, OnInit } from '#angular/core';
import {UserService} from '../../shared/user.service';
import {NgForm} from '#angular/forms';
#Component({
selector: 'app-agent-signup',
templateUrl: './agent-signup.component.html',
styleUrls: ['./agent-signup.component.css']
})
export class AgentSignupComponent implements OnInit {
constructor(private service:UserService) { }
ngOnInit() {
}
signUp(form:NgForm)
{
}
}
and this is the code for user.service :-
import { Injectable } from '#angular/core';
import {UserData} from './user.model';
import {HttpClient} from '#angular/common/http';
import {environment} from '../../environments/environment';
const API_URL=environment.apiUrl;
#Injectable({
providedIn: 'root'
})
export class UserService {
formData : UserData;
constructor(private http:HttpClient) {}
createUser(formData:UserData)
{
return this.http.post(API_URL+'/user/signup',formData);
}
}
and this is the the user.model class :-
export class UserData{
public Email:string;
public Password:string;
public Rera_no:string;
public Name:string;
public Company_name:string;
}
and i'm getting the following error :-
ERROR TypeError: "_co.service.formData is undefined"
can anybody tell me where i'm going wrong and how can i correct it ?
you are initializing with the Class object but not creating it's object though. So
do changes like this in your component
from
formData : UserData;
to
formData = new UserData();
in your template
<form (sumbit)="signUp(form)" autocomplete="off" #form="ngForm">
<div class="form-group">
<input name="Name" class="form-control" #Name="ngModel" [(ngModel)]="service.formData?.Name" required>
</div>
</form>
I hope this wil solve your issue
EDIT
you are using Object variable as ngModel which is not created in the Service yet. so try to initialize it first with empty string.
export class UserData{
public Email:string;
public Password:string;
public Rera_no:string;
public Name: string = '';
public Company_name:string;
}
I think it is because you need to call method from service UserService in component with form AgentSignupComponent . So you need to call createUser method in AgentSignupComponent;
import { Component, OnInit } from '#angular/core';
import {UserService} from '../../shared/user.service';
import {NgForm} from '#angular/forms';
#Component({
selector: 'app-agent-signup',
templateUrl: './agent-signup.component.html',
styleUrls: ['./agent-signup.component.css']
})
export class AgentSignupComponent implements OnInit {
constructor(private service:UserService) { } //You use DI this but dont call method;
ngOnInit() {
}
signUp(form:NgForm) {
this.service.createUser(form);
}
}
Write a console.log(service); in your InOnInit() function of your AgentSignupComponent class. Observe what properties you can see in your console. This error means it cannot access the UserData object in your formData variable. So you need to initiate a UserData object and assign a value to the variable. Change formData : UserData; to formData = new UserData();

ngIf Global Service ExpressionChangedAfterItHasBeenCheckedError

Developing a dashboard I find the following error, I understand that it is because I mishandle the Angular cycles.
I found workarounds but not a real solution.
I have a service that acts as a global scope then on the login screen when you pressed the button you updated the authenticated property to true.
The moment it becomes true I have a ngif that shows the header with the title which is stored in the global scope
From the home screen I updated the value of the title in global scope.
At that moment I get the error
ERROR Error: "ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'FormTitle:'. Current value: 'FormTitle: Home'."
global scope:
import { Injectable } from '#angular/core';
#Injectable()
export class ScopeService {
scope: any = {
FormTitle:"",
showMenu:false,
loading:false,
authenticated:false
};
}
app.component.html
<div class="container-flow">
<app-header *ngIf="ScopeService.scope.authenticated [FormTitle]="ScopeService.scope.FormTitle"></app-header>
<app-navigation *ngIf="ScopeService.scope.authenticated"></app-navigation>
<div class="main-container">
<router-outlet></router-outlet>
</div>
</div>
header.component.html
<div class="main-header">
<h1 class="title"> {{ FormTitle }} </h1>
</div>
home.commonent:
import { Component, OnInit } from '#angular/core';
import { ScopeService } from '../../services/scope.service';
import { ApiService } from '../../services/api.service';
import { Router , ActivatedRoute } from "#angular/router";
import { FormBuilder, FormGroup , Validators } from '#angular/forms';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
viewForm: FormGroup;
constructor(private formBuilder: FormBuilder,
private ScopeService:ScopeService,
private ApiService: ApiService,
private route: ActivatedRoute,
private router:Router) { }
ngOnInit() {
this.ScopeService.scope.FormTitle = this.route.snapshot.data['title'];
}
}
login click event:
continuar() {
this.ScopeService.scope.authenticated = true;
this.router.navigate(['home'],{ skipLocationChange : true });
}

Categories

Resources