I am building a timer app/component and I cannot find my error. It sounds like this: Cannot set property startAt of undefined. Don't know where is an error because I defined it in my dashboard component. Any ideas where is an error and how can I fix it? I post most of my code, maybe error is somewhere else.
This is my code:
My dashboard.ts file
export class DashboardComponent implements OnInit {
appUser: User;
clients: Client[] = [];
today: Date = new Date(2019, 9, 25, 12, 23); // Defualt todays time;
@ViewChild('counter', {read: CounterComponent, static: false})
private counter: CounterComponent;
counterState = 'counter is ticking';
ngOnInit() {
this.appUser = this.userS.currentUser;
this.clients = this.clientService.getUserClients()
.sort((a, b) => {
return (new Date(a.registrationDate) as any) - (new Date(b.registrationDate) as any);
});
this.counter.startAt = 120; // Here I am defining it
this.counter.counterState.subscribe((msg)=>{
if(msg==='COMPLETE') {
this.counterState = 'counter has stopped';
}
});
this.counter.start();
}
}
My dashboard.html file
<counter #counter></counter>
My Mycounter.ts file
@Component({
selector: 'counter',
templateUrl: './counter.component.html',
styleUrls: ['./counter.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent {
@Input()
startAt:number = 1;
@Input()
showTimeRemaining = true;
@Output()
counterState = new EventEmitter();
currentValue = '';
private currentSubscription: Subscription;
constructor(private changeDetector: ChangeDetectorRef) { }
public start() {
this.currentValue = this.formatValue(this.startAt);
this.changeDetector.detectChanges();
const t: Observable<number> = interval(1000);
this.currentSubscription = t.pipe(take(this.startAt))
.pipe( // not sure about this place but using only map but else it says
// Property 'map' does not exist on type 'Observable<number>'
map(v => this.startAt - (v + 1))).subscribe(v => {
this.currentValue = this.formatValue(v);
this.changeDetector.detectChanges();
}, err => {
this.counterState.error(err);
}, () => {
this.currentSubscription.unsubscribe();
this.currentValue = '00:00';
this.counterState.emit('COMPLETE');
this.changeDetector.detectChanges();
});
}
private formatValue(v) {
const minutes = Math.floor(v / 60);
const formattedMinutes = '' + (minutes > 9 ? minutes : '0' + minutes);
const seconds = v % 60;
const formattedSeconds = '' + (seconds > 9 ? seconds : '0' + seconds);
return `${formattedMinutes}:${formattedSeconds}`;
}
}
You can solve this in 2 ways: First, you can use static: true
to make counter
available in ngOnInit
:
@ViewChild('counter', {read: CounterComponent, static: true})
private counter: CounterComponent;
This way you will be able to access counter
variable in ngOnInit
unless there is a structural directive preventing it (like *ngIf
for example, more on that here and here).
The second way is to move counter
code to ngAfterViewInit
(there, the counter
variable will be resolved and you won't get the error):
@ViewChild('counter', {read: CounterComponent, static: false})
private counter: CounterComponent;
ngAfterViewInit() {
this.counter.startAt = 120;
this.counter.counterState.subscribe((msg)=>{
if(msg==='COMPLETE') {
this.counterState = 'counter has stopped';
}
});
this.counter.start();
}
Hope this helps...