#Input or service not working in Component - javascript

I am trying to make import a service inside a component but when I call the Input coming from this service, in my template, it does not render anything.
Here is my entity:
export interface PageState {
step: string;
}
export class SimplePageState implements PageState {
step: string;
}
Here is my service:
import { Injectable } from '#angular/core';
import { PageState } from './state.entity';
#Injectable()
export class PageStateService {
getPageState(): Promise<PageState[]> {
const step = [{ 'step': '1' }];
return Promise.resolve(step);
// return this.http.get('/api/status');
}
}
I am importing and instantiating these in my main component:
import { Component, OnInit } from '#angular/core';
import { Module } from '../modules/core/module.entity';
import { ModuleService } from '../modules/core/module.service';
import { PageState } from './state.entity';
import { PageStateService } from './state.service';
#Component({
selector: 'df-esign',
templateUrl: './esign-page.html',
styleUrls: ['./esign-page.scss'],
providers: [ ModuleService, PageStateService ]
})
export class EsignComponent implements OnInit {
modules: Module[];
pageState: PageState[];
constructor(private moduleService: ModuleService, private pageStateService: PageStateService) { }
getModules() {
this.moduleService.getModules().then(modules => this.modules = modules);
}
getPageState() {
this.pageStateService.getPageState().then(pageState => this.pageState = pageState);
}
ngOnInit() {
this.getModules();
this.getPageState();
}
}
And finally, I am using SimplePageState inside of a particular component, this way:
import { Component, Input } from '#angular/core';
import { SimpleModule } from '../core/module.entity';
import { SimplePageState } from '../../core/state.entity';
#Component({
selector: 'df-module-page',
templateUrl: './module-page.html',
styleUrls: ['./module-page.scss'],
})
export class ModulePageComponent {
#Input() module: SimpleModule;
#Input() pageState: SimplePageState;
}
But trying to do {{pageState}} in my template gives me a blank result with no error.. Anybody can help? I've spent hours looking on the internet and trying to make it work.
Edit:
I am trying to use it inside a bread-crumbs component.
Here is the beginning of my module-view.html, which contains df-module-page as well as df-module-bread-crumbs:
<ng-container [ngSwitch]="module.type">
<template [ngSwitchCase]="'PageModule'"><df-module-page [module]="module" [pageState]="pageState"></df-module-page></template>
<template [ngSwitchCase]="'TextModule'"><df-module-text [module]="module"></df-module-text></template>
<template [ngSwitchCase]="'BreadCrumbModule'"><df-module-bread-crumb [module]="module" [pageState]="pageState" class="{{module.class}}"></df-module-bread-crumb></template>
I am calling SimplePageState in the bread-crumb-component too:
import { Component, Input, HostBinding } from '#angular/core';
import { SimpleModule } from '../core/module.entity';
import { SimplePageState } from '../../core/state.entity';
#Component({
selector: 'df-module-bread-crumb',
templateUrl: './module-bread-crumbs.html',
styleUrls: ['./module-bread-crumbs.scss']
})
export class ModuleBreadCrumbsComponent {
#Input() module: SimpleModule;
#Input() pageState: SimplePageState;
}
And I am trying to do an ngIf inside of module-breads-crumbs.html with a pageState condition which does not have any effect:
<div class="dfbreadcrumbs">
<ol *ngIf="module">
<li *ngFor="let crumb of module.slots.crumbs; let i = index" class="step_{{i + 1}}">{{crumb.text}}</li>
</ol>
</div>
<div *ngIf="pageState">ohohoh</div>

To pass data to an input you would need something like
<df-module-page [pageState]="pageState">
in the template of EsignComponent

Related

Angular component comuniaction

I need help about angular component comunication.
I have Parent component and children.
In parent component i have list. I would like to click of item list and move data {name, text} to children component and set it to children component where is froala editor.
In Angular, there are two ways you can do implement component intercommunication.
Pass data from parent to child with input binding
#Component({
selector: 'app-parent',
template: `
<app-child [childMessage]="parentMessage"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent{
parentMessage = "message from parent"
constructor() { }
}
#Component({
selector: 'app-child',
template: `
Say {{ message }}
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
#Input() childMessage: string;
constructor() { }
}
Using Angular Services(This method is used when the components communicating are directly related ). But from you are describing you can do it better with angular service. When you click the item list, you can call the function of services that pushes the data to the BehaviourSubject. After that, all the component that are subscribed to that Behaviour subject will get the data you passed.
#Injectable()
export class DataService {
private messageSource = new BehaviorSubject('default message');
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-parent',
template: `
{{message}}
`,
styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
}
import { Component, OnInit } from '#angular/core';
import { DataService } from "../data.service";
#Component({
selector: 'app-sibling',
template: `
{{message}}
<button (click)="newMessage()">New Message</button>
`,
styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {
message:string;
constructor(private data: DataService) { }
ngOnInit() {
this.data.currentMessage.subscribe(message => this.message = message)
}
newMessage() {
this.data.changeMessage("Hello from Sibling")
}
}

Angular 5 BehaviorSubject not working for boolean value

I am trying to practice behaviorsubject in angular 5. I am written a small app with two components and want to change the value in both of them at once but the value is not changing. BehaviorSubject should change the value in all the components. Please help me understand.
Service
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class TestserviceService {
public isAdmin = new BehaviorSubject<boolean>(false);
cast = this.isAdmin.asObservable();
constructor() { }
changeAdmin(){
this.isAdmin.next(!this.isAdmin);
}
}
Component One
import { Component, OnInit } from '#angular/core';
import{ TestserviceService } from '../../testservice.service';
#Component({
selector: 'app-one',
templateUrl: './one.component.html',
styleUrls: ['./one.component.css']
})
export class OneComponent implements OnInit {
isAdmin: boolean;
constructor(private testservice: TestserviceService) { }
ngOnInit() {
this.testservice.cast.subscribe(data => this.isAdmin = data);
}
changeValue(){
this.testservice.changeAdmin();
console.log(this.isAdmin);
}
}
Component One html
<button (click)="changeValue()">Click Me</button>
<p>
one {{isAdmin}}
</p>
Component Two
import { Component, OnInit } from '#angular/core';
import { TestserviceService } from '../../testservice.service';
#Component({
selector: 'app-two',
templateUrl: './two.component.html',
styleUrls: ['./two.component.css']
})
export class TwoComponent implements OnInit {
isAdmin: boolean;
constructor(private testservice: TestserviceService) { }
ngOnInit() {
this.testservice.cast.subscribe(data => this.isAdmin = data);
console.log("two "+this.isAdmin);
}
}
changeAdmin(){
this.isAdmin.next(!this.isAdmin);
}
Should be
changeAdmin(){
this.isAdmin.next(!this.isAdmin.value);
}
this.isAdmin is a BehaviorSubject and you were trying to set !thisAdmin which evaluates to false
Stackblitz
Change your service to :
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class SharedServiceService {
constructor() { }
public isAdmin = new BehaviorSubject<boolean>(false);
cast = this.isAdmin.asObservable();
changeAdmin(){
this.isAdmin.next(!this.isAdmin.value);
}
}
It should be this.isAdmin.value because this.admin will only be behaviourSubject's object
Live Demo

Angular: Getter/Setter - Getter is returning undefined

I'm trying to pass a variable that is set on a component, to the parent component via a getter/setter in a service. The setter is applied correctly, but the getter returns undefined.
The below code was pulled out of another project I work on that does just fine with this code so I'm not sure why it isn't working here.
I simply just need to pass the pageTitle that is set on the child component, pass it to the parent component to display in its HTML.
Parent Component
TS: styleguide.component.ts
import { Component } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { StyleguideService } from './styleguide.service';
#Component({
selector: 'styleguide',
templateUrl: './styleguide.component.html',
host: {'class': 'route'},
})
export class StyleguideComponent {
constructor( private ss: StyleguideService ) {}
}
Relevant HTML: styleguide.component.html
<a [routerLink]="[]" aria-current="page" class="crumbs__link crumbs__link--active" [title]="ss.pageTitle">{{ss.pageTitle}}</a>
Parent Module: styleguide.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { StyleguideService } from './styleguide.service';
import { StyleguideComponent } from './styleguide.component';
import { TemplatesComponent } from './templates/templates.component';
...
#NgModule({
imports: [
CommonModule,
FormsModule,
...
],
declarations: [
StyleguideComponent,
TemplatesComponent,
...
],
exports: [
...
],
providers: [
StyleguideService
]
})
export class StyleguideModule {}
Service: styleguide.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class StyleguideService {
pageTitleS: string;
get pageTitle(): string {
console.log('get title: ', this.pageTitleS); // <-- Returns undefined
return this.pageTitleS;
}
set pageTitle(s: string) {
console.log('set title: ', s);
this.pageTitleS= s;
}
}
Child Component: templates.component.ts
import { Component } from '#angular/core';
import { StyleguideService } from '../styleguide.service';
#Component({
selector: 'templates',
templateUrl: './templates.component.html',
host: {'class': 'route__content'}
})
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle = "Templates";
}
}
You should implement the Service with Observables. A quick example would be something like this:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Injectable} from '#angular/core'
#Injectable()
export class Service {
private value: BehaviorSubject<string>;
constructor() {
this.value = <BehaviorSubject<string>>new BehaviorSubject();
}
setValue(value=""){
this.value.next(value);
}
getValue() {
return this.value.asObservable();
}
}
The Parent Component would subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'parent-component',
template: `
<div>
<h2>Value {{value}}</h2>
<child-component></child-component>
</div>
`,
})
export class ParentComponent implements OnInit {
value:string;
constructor(private service: Service) {
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
And the Child Component would set the value and also subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'child-component',
template: `
<div>
<h2>Child Value {{value}}</h2>
</div>
`,
})
export class ChildComponent implements OnInit {
value:string;
constructor(private service: Service) {
this.service.setValue('New Value');
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
Your setter is never called. You instantiate the service using StyleguideComponent, not the TemplatesComponent which does call the setter, and the constructor of StyleguideComponent does not call the setter on the service which is why the value remains undefined.
The TemplatesComponent has an element selector templates which I do not see in the styleguide.component.html you have in the question which is why I believe TemplatesComponent is never being created.
You are not calling the setter function in your child.component.ts instead you are setting the value of variable but I think you are accessing it wrongly as you are missing the last S in the variable name. You should be doing
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle("Templates");
// Now to get it you should call
this.ss.pageTitle(); // Should console.log the value
}
}
Okay so it was related to my routing setup, I didn't have my child routes setup correctly so this really had nothing to do with the getter/setter after all.

ExpressionChangedAfterItHasBeenCheckedError . Angular 2(5) *ngIf

I'm using a service to pass data between child and parent with the purpose being to update the parent ui with child specific info depending on what child is loaded.
Service:
import { Subject } from "rxjs/Subject";
export class ChildDataService {
private childDetails = new Subject<{}>();
childLoaded$ = this.childDetails.asObservable();
changedComponentName(option: {}){
this.childDetails.next(option);
}
}
Parent Component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { ChildDataService } from "../core/helpers/child-data.service";
import { Subscription } from "rxjs/Subscription";
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ParentComponent implements OnInit {
childDetails: {title?: string};
private subscription:Subscription;
constructor( private childDataService: ChildDataService) {
this.childDataService.childLoaded$.subscribe(
newChildDetails => {
console.log(newChildDetails);
this.childDetails = newChildDetails
});
}
}
Example Child Component:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { ChildDataService } from "../../core/helpers/child-data.service";
#Component({
selector: 'app-child-dashboard',
templateUrl: './child-dashboard.component.html',
styleUrls: ['./child-dashboard.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ChildDashboardComponent implements OnInit {
constructor(private childDataService: ChildDataService) { }
public ngOnInit() {
this.childDataService.changedComponentName({
title: 'Dashboard'
});
}
}
Parent HTML:
<div class="subheader">
<h1>{{ childDetails?.title }}</h1>
<button *ngIf="childDetails?.title == 'Dashboard'">dashboard</button>
<button *ngIf="childDetails?.title == 'SecondChild'">Second Child</button>
</div>
With this setup I am getting the error "ExpressionChangedAfterItHasBeenCheckedError" when i click on a routerLink, now if i click on the same link a second time, the error persists but the correct button becomes visible. Spent all weekend getting nowhere with this, so any help would be greatly appreciated.
public ngOnInit() {
setTimeout(() => {
this.childDataService.changedComponentName({
title: 'Dashboard'
}), {});
}
it should work.
async ngOnInit() {
await this.childDataService.changedComponentName({
title: 'Dashboard'
});
}

implement angular2-virtual-scroll with observable in angular4

I am trying to implement angular2-virtual-scroll in an angular4 project. Firstly, can anyone confirm if I would have a problem implementing it in NG4. I don't think I should and I haven't seen any notices to the contrary. Secondly I am using an observable instead of a promise shown in the NPM angular2-virtual-scroll docs. However, when I applied the virtual-scroll tag there is no change in my output...no scroll bar ..the data from the observable is displayed but no scrolling. The following are the relevant code segments:
Home.component.html
<h1 style="color: #76323f">
{{title}}
</h1>
<h4>
{{description}}
</h4>
<h2>Coming Soon....</h2>
<app-events-list [upcomingEvents]="upcomingEvents"></app-events-list>
home.component.ts
import {Component, OnInit,OnDestroy } from '#angular/core';
import {UpcomingEvent} from './../../interface/upcomingevent';
import {EventService} from './../../services/event.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, OnDestroy {
upcomingEvents: Array<UpcomingEvent>;
title = 'Welcome....';
description='Events promotions....';
eventServiceSub: Subscription;
constructor(private eventService: EventService){
this.upcomingEvents = [];
}
ngOnInit() {
this.eventServiceSub=this.eventService.getEvents().subscribe(upcomingEvents=>{this.upcomingEvents=upcomingEvents.slice(0,25);
});
}
ngOnDestroy(){
if (this.eventServiceSub){
this.eventServiceSub.unsubscribe();
}
}
}
event-list-component.html
<virtual-scroll [items]="items" (update)="upcomingEvents = $event"
(change)="onlistChange($event)">
<app-upcomingevent *ngFor="let upcomingEvent of upcomingEvents"[upcomingEvent]="upcomingEvent" [eventItemCss]="'event-item'"></app-upcomingevent>
<div *ngIf="loading" class="loader">Loading.....</div>
</virtual-scroll>
event-list-component.ts
import { Component, Input, OnDestroy, OnInit } from '#angular/core';
import { UpcomingEvent } from './../../interface/upcomingevent';
import { trigger,state,style,transition,animate,keyframes } from '#angular/animations';
import { ChangeEvent } from 'angular2-virtual-scroll';
import {EventService} from './../../services/event.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-events-list',
templateUrl: './events-list.component.html',
styleUrls: ['./events-list.component.css'],
animations: []
})
export class EventsListComponent implements OnInit, OnDestroy {
#Input()
upcomingEvents: Array<UpcomingEvent>;
items=this.upcomingEvents;
protected buffer: Array<UpcomingEvent> =[];
protected loading: boolean;
eventServiceSub: Subscription;
constructor(private eventService: EventService) {
this.upcomingEvents=[];
}
ngOnInit() {
}
ngOnDestroy(){
if (this.eventServiceSub){
this.eventServiceSub.unsubscribe();
}
}
protected onlistChange(event: ChangeEvent){
if (event.end !==this.buffer.length) return;
this.loading=true;
this.eventServiceSub=this.eventService.getEvents().subscribe(upcomingEvents=>{
this.buffer=upcomingEvents.slice(this.buffer.length,25);
this.loading=false;
}, ()=> this.loading=false);
}
}
eventService.ts
import { Injectable } from '#angular/core';
import {UpcomingEvent} from './../interface/upcomingevent'
import {Http} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class EventService {
constructor(private http: Http) { }
getEvents(): Observable<UpcomingEvent[]>
{
return this.http
.get(`https://jsonplaceholder.typicode.com/posts`)
.map(response => response.json() as UpcomingEvent[]);
}
}
upcomingEvents.html
<md-card [ngClass]="'event-item'">
<h4 [ngClass]="'event-title'" [ngStyle]="{'color':'purple'}"> {{upcomingEvent.title}}</h4>
<md-card-actions>
<button md-button>LIKE</button>
<button md-button>SHARE</button>
</md-card-actions>
</md-card>
upcomingEvents.component.ts
import { Component, Input,OnInit } from '#angular/core';
import { UpcomingEvent } from './../../interface/upcomingevent';
#Component({
selector: 'app-upcomingevent',
templateUrl: './upcomingevent.component.html',
styleUrls: ['./upcomingevent.component.css']
})
export class UpcomingeventComponent implements OnInit {
#Input()
upcomingEvent: UpcomingEvent;
#Input()
eventItemCss: string;
constructor() { }
ngOnInit() {
if (!this.upcomingEvent) {
this.upcomingEvent=<UpcomingEvent> {};
}
}
}
The way it suppose to work is that the HomeComponent request data from the eventService via an observable..the data is then passed to the eventList component where it is iterated on and pass to the upcomingEvents component to present via HTML. 25 events are requested at first and if the user scroll to the end another 25 is requested from the eventService...this time by the upcomingEvents component. I am not sure this is the most efficient way to do it but in either case it doesn't work. The virtual-scroll seems to have no effect on the output....I would really appreciate someone showing me what I am doing wrong....Thanks
I think I see a problem in your code. When using virtual-scroll you need to keep two arrays - one for all loaded data, and one for currently rendered items. Looks like you kinda mixed them up.
Let's call all-items-array as items and render-array - upcomingEvents
First chunk of items will be passed from the parent component, hence:
Home.component.html
...
<app-events-list [items]="upcomingEvents"></app-events-list>
event-list-component.html looks good
event-list-component.ts
import { Component, Input, OnDestroy, OnInit } from '#angular/core';
import { UpcomingEvent } from './../../interface/upcomingevent';
import { trigger,state,style,transition,animate,keyframes } from '#angular/animations';
import { ChangeEvent } from 'angular2-virtual-scroll';
import {EventService} from './../../services/event.service';
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-events-list',
templateUrl: './events-list.component.html',
styleUrls: ['./events-list.component.css'],
animations: []
})
export class EventsListComponent implements OnInit, OnDestroy {
#Input()
items: Array<UpcomingEvent> = [];
upcomingEvents: Array<UpcomingEvent>;
protected loading: boolean;
eventServiceSub: Subscription;
constructor(private eventService: EventService) {
this.upcomingEvents=[];
}
ngOnInit() {
}
ngOnDestroy(){
if (this.eventServiceSub){
this.eventServiceSub.unsubscribe();
}
}
protected onlistChange(event: ChangeEvent){
if (event.end !==this.items.length) return;
this.loading=true;
this.eventServiceSub=this.eventService.getEvents().subscribe(upcomingEvents=>{
this.items=upcomingEvents.slice(0, this.items.length + 25);
this.loading=false;
}, ()=> this.loading=false);
}
}
warning! code not tested, I simply edited your code.

Categories