I cant add the Input Decorator in Angular - html

I'm getting data from father component with Input in Angular, but it doen't work
My child:
export class ForumComponent implements OnInit {
#Input() id_foro: number = 3;
nombre: string = '';
desc: string = ''
forum = this.forumService.getById(this.id_foro).subscribe((data: Forum[]) => {
this.nombre = data[0].name
this.desc = data[0].description
});
constructor(private forumService: ForumService) { }
where I call it:
<div *ngIf="forum.id_category_fk === category.id">
<app-forum [id_foro]="forum.id"></app-forum>
</div>

That is not the correct way to call the method and assign the value.
For your scenario, you should call the service method in ngOnInit().
export class ForumComponent implements OnInit {
#Input() id_foro: number = 3;
...
forum: Forum;
...
ngOnInit() {
this.forumService.getById(this.id_foro).subscribe((data: Forum[]) => {
this.nombre = data[0].name;
this.desc = data[0].description;
this.forum = data[0];
});
}
}

Related

Angular Trouble Emptying Array

Hell, I have an e-commerce application in which I am trying to empty the shopping cart after payment is successful, no matter what I have tried the array will not empty. I have tried cartItems.length = 0,
cartItems = [] as well as splice. I must be missing something. Code snippet below I will walk you through it.
This is my Model
import { Product } from './product';
export class CartItem {
id: number;
productId: number;
productName: string;
qty: number;
price: number;
size:string;
imageUrl:string;
constructor(id:number, size:string,
product:Product, qty= 1) {
this.id = id;
this.productId = product.id;
this.price = product.price;
this.size = size;
this.productName = product.name;
this.qty = qty;
this.imageUrl = product.imageUrl;
}
}
This is my car service as you can see I remove and add to cart as well as get cart items, there is no problem here. The remove and add are button clicks on html
export class CartService {
product: any;
cartItem: CartItem[] = [];
cartUrl = 'http://localhost:4000/cart';
constructor(private http: HttpClient, private router: Router,
private route: ActivatedRoute) {
}
getCartItems(): Observable<CartItem[]> {
return this.http.get<CartItem[]>(cartUrl).pipe(
map((result: any[]) => {
let cartItems: CartItem[] =[];
for(let item of result) {
cartItems.push( new CartItem(item.id, item.size,
item.product, item.imageUrl ));
}
return cartItems;
})
);
}
addProductToCart(product:Product):Observable<any>{
return this.http.post(cartUrl, {product});
}
RemoveProductFromCart(id:number):Observable<void>{
//this.cartItems.splice(index,1)
alert("show deleted item");
return this.http.delete<CartItem[]>(`${this.cartUrl}/${id}`)
.pipe(catchError(_err => of (null))
);
}
buttonClick() {
const currentUrl = this.router.url;
this.router.navigateByUrl('/', {skipLocationChange:
true}).then(() => {
this.router.navigate([currentUrl]);
});
//alert("Should Reload");
}
addPurseToCart(product:Product):Observable<any>{
return this.http.post(cartUrl, {product})
}
}
Here is the checkout component, I injected the cart component so I could call the the empty cart function which resides in the cart component. I did import the CartComponent. Check out is based on cart. When the cart is emptied so should checkout
#Component({
providers:[CartComponent], //injected CartComponent
selector: 'app-checkout',
templateUrl: './checkout.component.html',
styleUrls: ['./checkout.component.scss']
})
export class CheckoutComponent implements OnInit {
// #ViewChild(CartComponent) // #ViewChild(CartItemComponent) cartitemComponent: CartItemComponent
cartComponent: CartComponent
#Input() product: CartItem;
#Input() cartItem: CartItem;
cartUrl = 'http://localhost:4000/cart';
size;
cartItems = [];
cartTotal = 0;
itemTotal = 0;
shipping = 8.00;
estimatedTax = 0;
myValue: any;
constructor(private msg: MessengerService, private route:
ActivatedRoute,
private router: Router, private
cartService:CartService,
private productService: ProductService, private
comp:CartComponent) {}
ngOnInit() {
this.loadCartItems();
}
}
loadCartItems(){
this.cartService.getCartItems().subscribe((items:
CartItem[]) => {
this.cartItems = items;
this.calcCartTotal();
this.calNumberOfItems();
})
}
calcCartTotal() {
this.cartTotal = 0;
this.cartItems.forEach(item => {
this.cartTotal += (item.qty * item.price);
})
this.cartTotal += this.shipping;
this.myValue = this.cartTotal
render(
{
id:"#paypal-button-container",
currency: "USD",
value: this.myValue,
onApprove: (details) =>{
alert("Transaction Suceessfull")
console.log(this.myValue);
this.comp.handleEmptyCart();
}
}
);
}
calNumberOfItems(){
console.log("Trying to get tolal items")
this.itemTotal = 0;
this.cartItems.forEach(item => {
this.itemTotal += item.qty;
})
}
}
cart component
export class CartComponent implements OnInit {
#Input() product: CartItem;
#Input() cartItem: CartItem;
//items: CartItem [] =[];
cartUrl = 'http://localhost:4000/cart';
val;
size;
cartItems = [];
cartTotal = 0
itemTotal = 0
constructor(private msg: MessengerService, private
cartService:CartService, private productService:
ProductService, private formBuilder:FormBuilder, private
_data:AppserviceService, private router:Router) { }
ngOnInit(): void {
this.handleSubscription();
this.loadCartItems();
}
handleSubscription(){
this.msg.getMsg().subscribe((product: Product) => {
})
}
loadCartItems(){
this.cartService.getCartItems().subscribe((items:
CartItem[]) => {
this.cartItems = items;
console.log("what is in cartItems" + this.cartItems)
console.log("What does this property hold" +
this.cartItem)
this.calcCartTotal();
this.calNumberOfItems();
})
}
calcCartTotal() {
this.cartTotal = 0
this.cartItems.forEach(item => {
this.cartTotal += (item.qty * item.price)
})
}
calNumberOfItems(){
console.log("Trying to get tolal items")
this.itemTotal = 0
this.cartItems.forEach(item => {
this.itemTotal += item.qty
})
}
handleEmptyCart(){
alert("Hit Empty Cart");
/*here I get the cart items to see what is in the array
and try to empty, it does show tow objects in the
array*/
this.cartService.getCartItems().subscribe((items:
CartItem[]) => {
this.cartItems = items;
this.cartItems.length=0
// this.cartItems = [];
console.log("what is in cartItems" + this.cartItems)
})
}
}
I have used different approaches trying to empty the cart nothing works. It makes me think I'm stepping on something or somehow creating events calling the loadCartItems to many times not sure but according to my research one of these approaches should work. If someone can please help me out I'm stuck. I would greatly appreciate it.
Short answer: run change detection ChangeDetectorRef.detectChanges()
Long answer:
You need to understand how angular works.
Lets assume simple example:
<div id="val">{{val}}</div><button (click)="val++">Increase<div>
So when u click button variable changes, but who changes actual #val div content? Answer is that Angular uses zone.js to patch/change a lot of functions to run Angular change detection after most of JS events (you can control type of this events to include/exclude some of them). Also Promise is patched same way, thats why after some Promise is resolve change detection is run.
However, here you run some render method with onApprove callback which is probably some 3rd party library (?) and zone.js is not aware of it (https://angular.io/guide/zone#ngzone-run-and-runoutsideofangular). And though running detectChanges gonna help u, u better re-write your code, so onApprove is always in Angular zone and u never gonna face similar bugs in future when using this method.

How to get a directive to react to an EventEmitter in a component

I have a CustomComponent which emits a value (let's just call it "error") if a http request to the back end api returns an error. How can I get a directive (call it Form Directive), applied to this form, to recognize when the "error" value is emitted by CustomComponent?
Code for CustomComponent:
export class CustomComponent extends FormComponent<Custom> {
constructor(
protected fb: FormBuilder,
private httpService: HttpService) {
super(fb);
}
currentVal: string = '';
inputType: string = 'password';
showPasswordTitle: string = 'Show Password';
showPasswordStatus: boolean = false;
form: FormGroup;
#Output() invalidOnError = new EventEmitter<string>();
protected buildForm(): FormGroup {
return this.form = this.fb.group({
fieldA: ['', Validators.required],
fieldB: ['', Validators.required],
fieldC: [''],
fieldD: ['', [Validators.required, Validators.pattern('[0-9]{10}')]]
}
protected doSubmit(): Observable<Custom> {
return this.httpService.callDatabase<Custom>('post', '/api/users/custom', this.value);
};
protected get value(): Registration {
return {
fieldA: this.fieldA.value,
fieldB: this.fieldB.value,
fieldC: this.fieldC.value,
fieldD: this.fieldD.value
};
}
get fieldA() { return this.form.get('fieldA'); }
get fieldB() { return this.form.get('fieldB'); }
get fieldC() { return this.form.get('fieldC'); }
get fieldD() { return this.form.get('fieldD'); }
protected onError() {
if (this.error.length) {//error.length indicates some of the fields in the form are already registered in the database
Object.keys(this.error).forEach(element => {
let formControl = this.form.get(this.error[element])
this.currentVal = formControl.value;
formControl.setValidators(formControl.validator ? [formControl.validator, unique(this.currentVal)] : unique(this.currentVal))
formControl.updateValueAndValidity()
this.invalidOnError.emit('error');
})
}
}
Code for FormComponent:
export abstract class FormComponent<T> implements OnInit {
protected form: FormGroup = null;
submitted = false;
completed = false;
error: string = null;
constructor(protected fb: FormBuilder) {}
ngOnInit() {
this.form = this.buildForm();
}
onSubmit() {
this.submitted = true;
if (this.form.valid) {
this.doSubmit().subscribe(
() => {
this.error = null;
this.onSuccess();
},
err => {
this.error = err
this.onError();
},
() => {
this.submitted = false;
this.completed = true;
}
)
}
}
protected abstract get value(): T;
protected abstract buildForm(): FormGroup;
protected abstract doSubmit(): Observable<T>;
protected onSuccess() {}
protected onError() {}
}
Code for Form Directive (works well when user clicks Submit button, which triggers onSubmit event in CustomComponent):
#Directive({
selector: 'form'
})
export class FormSubmitDirective {
submit$ = fromEvent(this.element, 'submit').pipe(shareReplay(1));
constructor(private host: ElementRef<HTMLFormElement>) {}
get element() {
return this.host.nativeElement;
}
}
I was hoping something like this could be the solution to my question, but this for sure doesn't work.
invalidOnError$ = fromEvent(this.element, 'error').pipe(shareReplay(1));
The idea is to use submit$ or invalidOnError$ from the directive to focus on the first invalid field in the form. Works fine for submit$, but not invalidOnError$. Appreciate some help - still fairly new to Angular.
I got this to work in a round about manner, by using the #Input decorator in another form directive which also imports submit$ from Form Directive.
No changes to code for FormComponent and Form Directive vs. what's shown in the question.
Relevant code from Custom component:
export class CustomComponent extends FormComponent<Custom> {
invalidOnError: string = '';
form: FormGroup;
protected buildForm(): FormGroup {
return this.form = this.fb.group({
fieldA: ['', Validators.required],
fieldB: ['', Validators.required],
fieldC: [''],
fieldD: ['', [Validators.required, Validators.pattern('[0-9]{10}')]]
}
protected doSubmit(): Observable<Custom> {
invalidOnError = '';
return this.httpService.callDatabase<Custom>('post', '/api/users/custom', this.value);
};
protected get value(): Registration {
return {
fieldA: this.fieldA.value,
fieldB: this.fieldB.value,
fieldC: this.fieldC.value,
fieldD: this.fieldD.value
};
}
get fieldA() { return this.form.get('fieldA'); }
get fieldB() { return this.form.get('fieldB'); }
get fieldC() { return this.form.get('fieldC'); }
get fieldD() { return this.form.get('fieldD'); }
protected onError() {
if (this.error.length) {//error.length indicates some of the fields in the form are already registered in the database
invalidOnError = 'invalid'
Object.keys(this.error).forEach(element => {
let formControl = this.form.get(this.error[element])
this.currentVal = formControl.value;
formControl.setValidators(formControl.validator ? [formControl.validator, unique(this.currentVal)] : unique(this.currentVal))
formControl.updateValueAndValidity()
this.invalidOnError.emit('error');
})
}
}
Relevant code from CustomComponentTemplate:
<form class="bg-light border" appFocus="FieldA" [formGroup]="CustomForm"
[invalidOnError]="invalidOnError" (ngSubmit)="onSubmit()">
Relevant code from invalidFormControlDirective (imports submit$ from Form Directive):
#Directive({
selector: 'form[formGroup]'
})
export class FormInvalidControlDirective {
private form: FormGroup;
private submit$: Observable<Event>;
#Input() invalidOnError: string = ''; //this is the #Input variable invalidOnError
constructor(
#Host() private formSubmit: FormDirective,
#Host() private formGroup: FormGroupDirective,
#Self() private el: ElementRef<HTMLFormElement>
) {
this.submit$ = this.formSubmit.submit$;
}
ngOnInit() {
this.form = this.formGroup.form;
this.submit$.pipe(untilDestroyed(this)).subscribe(() => {
if (this.form.invalid) {
const invalidName = this.findInvalidControlsRecursive(this.form)[0];
this.getFormElementByControlName(invalidName).focus();
}
});
}
ngOnChanges(){
of(this.invalidOnError).pipe(filter(val => val == 'invalid')).subscribe(() => {
if (this.form.invalid) {
const invalidName = this.findInvalidControlsRecursive(this.form)[0];
this.getFormElementByControlName(invalidName).focus();
}
});
}
ngOnDestroy() { }
// findInvalidControlsRecursive and getFormElementByControlName defined functions to get invalid controls references
}
That said, I'd be interested in 1) somehow bringing code under onChanges lifecyle into ngOnInit lifecyle in invalidFormControlDirective (couldn't get that to work), and 2) find out if there is some way to emitting an event and processing it with Rxjs fromEventPattern as opposed to passing the #Input variable invalidOnError into invalidFormControlDirective.

Angular 6 : Not able to fetch individual data from different component's JSON object

Scenario:
In Component1, I have a table, I am sending single row's data as a JSON object to Component2's object
Expected result:
I should be able to fetch data using object2, eg. object2.id = id1, object2.title = title1
Actual result: I am getting undefined for values in object2, object2.id= undefined , object2.title = undefined
What I tried:
In Component1 I used JSON.stringify(obj) and in Component2 I was using JSON.parse(obj) to get the object values, but I was getting [obj obj] on alert the JSON object.
I got confused as to how did JSON automatically got converted to Obj without using any JSON.parse.
Good news is, data is being passed to object2, when I alert object2 I get the whole object string with all values.
but when I try to populate single value it gives me undefined msg inspite of the values being present
Any idea how else i can check why it is not working ?
Not sure what exactly I am missing, I am searching since past couple of days, did not find any solution on this or any other site.
Any help is appreciated. Thanks.
Here is my code:
Component1
#Component({
selector: 'myjds',
templateUrl: './myjds.component.html',
styleUrls: ['./myjds.component.scss'],
providers: [DatePipe]
})
#NgModule({
imports: [
ThemeModule,
NgxEchartsModule, Ng2SmartTableModule,
NgxChartsModule, ChartModule, NgxSpinnerModule
],
declarations: [
DashboardComponent,
StatusCardComponent,
ContactsComponent,
EchartsPieComponent,
EchartsBarComponent,
],
entryComponents: []
})
export class MyjdsComponent implements OnInit {
config: ToasterConfig;
private message = null;
position = 'toast-top-right';
animationType = 'flyLeft';
title = 'Result';
content = `I'm cool toaster!`;
timeout = 5000;
toastsLimit = 5;
type = 'info';
isNewestOnTop = true;
isHideOnClick = true;
isDuplicatesPrevented = false;
isCloseButton = true;
EntityID;
LoginUserId;
jdData: JobDescription[] = [];
indJobDescription = {} as JobDescription;
source: LocalDataSource = new LocalDataSource();
serachResults = [];
public nijobmobile;
public nijobcontactemail;
constructor(
private ServiceObj: ApiService,
private spinner: NgxSpinnerService,
private modalService: NgbModal,
private toasterService: ToasterService,
private activeModal: NgbActiveModal,
private datePipe: DatePipe) {
this.EntityID = localStorage.getItem("Entity");
this.LoginUserId = localStorage.getItem("LoginID");
}
private showToast(type: string, title: string, body: string) {
this.config = new ToasterConfig({
positionClass: this.position,
timeout: this.timeout,
newestOnTop: this.isNewestOnTop,
tapToDismiss: this.isHideOnClick,
preventDuplicates: this.isDuplicatesPrevented,
animation: this.animationType,
limit: this.toastsLimit,
});
const toast: Toast = {
type: type,
title: title,
body: body,
timeout: this.timeout,
showCloseButton: this.isCloseButton,
bodyOutputType: BodyOutputType.TrustedHtml,
};
this.toasterService.popAsync(toast);
}
ngOnInit() {
this.loadJobDescription();
}
loadJobDescription(jdData?) {
if (jdData == null || jdData == undefined || jdData == 0) {
alert("data null e ");
this.spinner.show();
let body = JSON.stringify({
nispname: "nijobdescriptionsearch_sp",
ptype: "alljobdescription",
pnijobdescriptionid: 0,
pniuserid: Number(this.LoginUserId),
pnicompid: this.EntityID
});
alert("body string value : " + body);
this.ServiceObj.apicall(body).subscribe(
res => {
this.spinner.hide();
let data: any = res;
if (data.results.Table.length > 0) {
alert("table returns values:" + data.results.Table.length);
this.jdData = data.results.Table;
localStorage.setItem('Message', JSON.stringify(this.jdData));
this.source.load(this.jdData);
}
},
(err) => {
this.spinner.hide();
}
);
}
else {
alert("data ahe baba");
let loginUserId = localStorage.getItem("LoginID");
alert("loginUserId: " + loginUserId);
this.spinner.show();
let body = JSON.stringify({
nispname: "nijobdescriptionsearch_sp",
ptype: "individualJD",
pnijobdescriptionid: jdData.nijobdescriptionid,
pniuserid: Number(this.LoginUserId),
pnicompid: this.EntityID
});
alert("body stringify:" + body);
this.ServiceObj.apicall(body).subscribe(
res => {
this.spinner.hide();
let data: any = res;
if (data.results.Table.length > 0) {
alert("data length" + data.results.Table.length);
this.indJobDescription = data.results.Table;
localStorage.setItem('Message1', JSON.stringify(this.indJobDescription));
// alert("result of indjobdescription: " + JSON.stringify(this.indJobDescription));
const activeModal = this.modalService.open(IndJobDescriptionComponent, {
size: 'lg',
backdrop: 'static',
container: 'nb-layout',
});
}
},
(err) => {
this.spinner.hide();
}
);
}
}
}
Component2
selector: 'commentresult',
templateUrl: './indjobdescription.component.html',
styleUrls: ['./indjobdescription.component.scss']
})
export class IndJobDescriptionComponent implements OnInit {
private msg: string = '';
private msg1: string = "";
public dialog: any;
public dialog1 :any;
public existingstaffid = [];
errorMsgRolename: string = '';
errorMsgRoledescription: string = '';
isValidRolename: boolean = true;
isValidRoledescription: boolean = true;
public ShlocationAutoComplete;
public ShroleAutoComplete;
public ShskillAutoComplete;
public ShdomainAutoComplete;
public ShcertAutocomplete;
public alldata;
public nijobmobile;
public nijobcontactemail;
pager: any = {};
pagedItems: any[];
jdData: JobDescription[] = [];
indJobDescription = {} as JobDescription;
LoginUserId = localStorage.getItem("LoginID");
source: LocalDataSource = new LocalDataSource();
constructor(private modalService: NgbModal,
private spinner: NgxSpinnerService,
private _sanitizer: DomSanitizer,
private data: DataService,
private activeModal: NgbActiveModal,
private ServiceObj: ApiService,
private pagerService: PagerService,
private toasterService: ToasterService) {
this.EntityID = localStorage.getItem("Entity");
}
profile: any;
private EntityID: string;
private message = null;
config: ToasterConfig;
position = 'toast-top-right';
animationType = 'flyLeft';
title = 'Result';
content = `I'm cool toaster!`;
timeout = 5000;
toastsLimit = 5;
type = 'info';
isNewestOnTop = true;
isHideOnClick = true;
isDuplicatesPrevented = false;
isCloseButton = true;
ngOnInit() {
this.msg1 = localStorage.getItem("Message1");
//this.indJobDescription = JSON.parse(this.msg1); //on doing alert, this line is returning [obj obj]
alert("user id: " + this.indJobDescription.nijobcreateuserid);
}
closeModal() {
this.activeModal.close();
}
private showToast(type: string, title: string, body: string) {
this.config = new ToasterConfig({
positionClass: this.position,
timeout: this.timeout,
newestOnTop: this.isNewestOnTop,
tapToDismiss: this.isHideOnClick,
preventDuplicates: this.isDuplicatesPrevented,
animation: this.animationType,
limit: this.toastsLimit,
});
const toast: Toast = {
type: type,
title: title,
body: body,
timeout: this.timeout,
showCloseButton: this.isCloseButton,
bodyOutputType: BodyOutputType.TrustedHtml,
};
this.toasterService.popAsync(toast);
}
SaveData() {
let t = window.location.host;
let tvpselectiondtl: tvp_selectiondtl[] = [];
let LoginUserId = localStorage.getItem("LoginID");
}
}
PFB screenshot of the JSON string, sorry console.log is not working so had to take screen shot of the alert,
As I can see in json you are storing an array of objects .
this.msg1 = localStorage.getItem("Message1");
this.indJobDescription = JSON.parse(this.msg1); //on doing alert, this line is
returning [obj obj]
alert("user id: " + this.indJobDescription[0].nijobcreateuserid); //It is array
If there is multiple entry of indJobDescription then use *ngFor to read each object.
for (let i = 0; i < this.indJobDescription.length ; i++) {
var jobCreateUserId= this.indJobDescription[i].nijobcreateuserid;
..........
}
and it will solve your issue.
keep this simple
var product=[{"sno":"1","description":"test"}] =Array of object ==>product[0].sno
var product={"sno":"1","description":"test"} = it presents object. ==> product.sno
There are two main ways to share data between components, input and output properties and shared services.
If component 2 is a child of component 1 then use inputs
<component2 [data]="propertyOfComponent1"></component2>
and in component 2 decorate the data property as an input
#Input() data;
After the component has initalised data will be the data passed from component 1. You can access it in the ngOnInit method but not the constructor as it wont be wired up just yet.
If they are not in a parent child relationship use a shared service that is dependency injected into both components constructor.
constructor(private sharedService: SharedService) {
}
and both components will get the same instance of the service.
#Injectable()
export class SharedService {
data = new SomeObject();
}

Using service.ts variables on multiple components

I've set up next.service.ts with 3 variables (user, action, rest) and made setters(updateNext()) and getters (getUser, getAction, getRest). I've got to use the setter to change the variables in one component (stock-management component) and retrieved these variables in another component (inventory-record component) but I can't seem to retrieve them from another component (inventory-record-filled component).
I've tried returning a string ("TEST") in the getter and it worked, but when I tried returning a variable, it just returned nothing/empty string.
export class NextService {
private action: string;
private user: string;
private restraunt: string;
constructor() { }
updateNext(actions, users, restraunts) {
this.action = actions;
this.user = users;
this.restraunt = restraunts;
}
getAction(): string {
return this.action;
}
getUser(): string {
return this.user;
}
getRest(): string {
return this.restraunt;
}
export class InventoryRecordComponent implements OnInit {
name = '';
rest = '';
action = '';
constructor(private next: NextService) {
this.name = this.next.getUser();
this.action = this.next.getAction();
this.rest = this.next.getRest();
}
ngOnInit() {
document.getElementById('dne').style.display = 'none';
}
onSubmit(f: NgForm) {
const x = document.getElementById('dne');
if (!this.next.updateCode(this.code)) {
x.style.display = 'block';
f.resetForm();
} else {
this.next.updateCode(this.code);
location.replace('inventory-record/qty');
}
}
}
export class InventoryRecordFilledComponent implements OnInit {
name: string;
action: string;
rest: string;
constructor(private next: NextService) {
this.name = this.next.getUser();
this.action = this.next.getAction();
this.rest = this.next.getRest();
}
ngOnInit() {
}
}
Each component have its respective html files with {{ name }} {{ action }} {{ rest }}
If you need your component to behave as a Simpleton (where it contains the same values regardless of where in the application it is used) you must set its providedIn value to "root", like so:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class NextService {
// The rest of the code stays the same
}
The description for that can be found here: https://angular.io/guide/singleton-services#providing-a-singleton-service
If you don't do that, each component that imports NextService will have it's own instance of NextService, with its own isolated values. If you want the values of a service to be available everywhere that the service is used in, then you want the service to be a Simpleton, so you must follow the steps.
Following the steps above is not the only way to make your component a Simpleton, but as the link mentions, it is the preferred way to do that.
Hope that helps!

Cannot read property of undefined Angular 4 Typescript

I have been trying to sort this out for a while now and could definitely use some help.
I am trying to display the question I got from the server and stored locally. But When i try to display the question that i got it gives me the following error:
Cannot read property 'question' of undefined
Here's my HTML
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Questionnaire</title>
</head>
<body>
<div class="container" id="Question" align="center">
<h2 [innerHTML] = "q.question"></h2>
</div>
</body>
</html>
Here's typescript that is for sure returning values because i can log them and so I checked:
#Component({
selector: 'app-new-questionnaire',
templateUrl: './new-questionnaire.component.html',
styleUrls: ['./new-questionnaire.component.css'],
providers: [NewServerRequestsService]
})
export class NewQuestionnaireComponent implements OnInit {
q: any;
sub: any;
id: number;
cQuestion: number;
score: number;
qNumber: number;
constructor(private serverService: NewServerRequestsService, private
route: ActivatedRoute, private router: Router){}
static questionTime = performance.now();
onload()
{
console.log(NewQuestionnaireComponent.questionTime);
// this.ButtonClicks();
}
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
// (+) converts string 'id' to a number
// fetch the file and get next Question
this.id = +params['id'];
if (localStorage.getItem('q') !== null) {
var data = JSON.parse(localStorage.getItem('q'))
this.qNumber = parseInt(localStorage.getItem('qNumber'))
this.q = data.results[this.id - 1]
} else {
this.serverService.getQuestion()
var data = JSON.parse(localStorage.getItem('q'))
this.qNumber = parseInt(localStorage.getItem('qNumber'))
this.q = data.results[this.id - 1]
}
});
}
}
}
Here's the service:
#Injectable()
export class NewServerRequestsService {
constructor(private http: Http) { }
getQuestion()
{
return this.http.get("https://opentdb.com/api.php?amount=30")
.map(res => res.json()).subscribe(
data => {
// shuffle questions
for (var i = 0; i < data.results.length - 1; i++) {
var j = i + Math.floor(Math.random() * (data.results.length -
i));
var temp = data.results[j];
data[j] = data.results[i];
data[j].incorrect_answers.push(data[j].correct_answer)
data[i] = temp;
}
localStorage.setItem("q", JSON.stringify(data))
localStorage.setItem("qNumber", JSON.stringify(data.length))
},
err => console.error(err)
)
}
}
Here's where the data gets fetched:
#Component({
selector: 'home',
templateUrl: 'home.component.html',
providers: [NewServerRequestsService]
})
export class HomeComponent implements OnInit, OnDestroy {
constructor(private QuizService: NewServerRequestsService, private route:
ActivatedRoute, private router: Router) {
}
ngOnInit() {
this.QuizService.getQuestion();
}
ngOnDestroy() { }
}
this error occurs because q is undefined when rendering the view , try using safe navigation by using ? :
<h2 [innerHTML] = "q?.question"></h2>
initialize q: any = {} if it's an object, [] if array ;)
tell me now what console.log(q) show
Check q in your ts or on your html <h2 *ngIf="q" ...