I'm creating a small app which keeps track of what activity I've done in a certain timespan once I write down said activity inside of an input field, once I click "OK" it should appear in a list.
I'm stuck on how I should proceed to reset the timer once I click the "OK" button, so basically what I should add inside the "resetTimer()" function which is supposed to trigger each time I add a task in a list.
HTML:
<div class="modalbox" [class.active]="modalboxActive">
<div class="modal">
<p>What did you do?</p>
<input type="text" [(ngModel)]="activity.name" />
<button (click)="addTask()" [disabled]="activity.name === ''">OK</button>
</div>
</div>
<div class="boxSuper">
<div class="boxLeft">
<div class="containerUp">
<button id="start" (click)="startTimer()">START</button>
<button id="pause" (click)="pauseTimer()">PAUSE</button>
<button id="stop" (click)="stopTimer()">STOP</button>
</div>
<div class="containerDown">
<p>{{ display }}</p>
</div>
</div>
<div class="boxRight">
<div class="containerLeft"></div>
<div class="containerRight">
<ul class="listElement" *ngFor="let item of tasks">
<li>
Activity:
<span>{{ item.name }}</span>
Length:
<span>{{ item.length }}</span>
</li>
</ul>
</div>
</div>
</div>
TS:
import { importExpr } from '#angular/compiler/src/output/output_ast';
import { Component, OnInit } from '#angular/core';
import { timer } from 'rxjs';
import { Activity } from '../activity';
import { Result } from '../result';
#Component({
selector: 'app-timer',
templateUrl: './timer.component.html',
styleUrls: ['./timer.component.css'],
})
export class TimerComponent implements OnInit {
ngOnInit() {}
time: number = 0;
display: string | undefined;
interval: any;
modalboxActive = false;
activity: Activity = {
name: '',
};
tasks: Result[] = [];
addTask() {
var el: Result = {
name: this.activity.name,
end: '',
start: '',
length: this.display,
};
this.tasks.push(el);
this.activity.name = '';
this.modalboxActive = false;
this.resetTimer();
}
resetTimer() {
console.log('reset');
}
startTimer() {
console.log('go');
this.interval = setInterval(() => {
if (this.time === 0) {
this.time++;
} else {
this.time++;
}
this.display = this.transform(this.time);
}, 1000);
}
transform(value: number): string {
var sec_num = value;
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - hours * 3600) / 60);
var seconds = sec_num - hours * 3600 - minutes * 60;
return hours + ':' + minutes + ':' + seconds;
}
pauseTimer() {
clearInterval(this.interval);
}
stopTimer() {
this.modalboxActive = true;
clearInterval(this.interval);
console.log('show');
}
}
Result interface:
export interface Result {
start: string | undefined;
end: string | undefined;
length: string | undefined;
name: string | undefined;
}
If you are trying to just reset it back to 0, all you would need to do is:
resetTimer(){
this.time = 0;
}
Related
I'm creating a scheduler in which I can add tasks I've done in a certain amount of time. The tasks get added in a to-do list-like manner.
I'm trying to remove individual items from a list both from the front-end and from LocalStorage but I haven't found a solution that works yet. The function I'm trying to make work is "RemoveTask()".
Here's my code:
HTML:
<div class="modalbox" [class.active]="modalboxActive">
<div class="modal">
<p>What did you do?</p>
<input type="text" [(ngModel)]="activity.name" maxlength="22" />
<button (click)="addTask()" [disabled]="activity.name === ''">OK</button>
</div>
</div>
<div class="boxSuper">
<div class="boxLeft">
<div class="containerUp">
<button id="start" (click)="startTimer()">START</button>
<button id="pause" (click)="pauseTimer()">PAUSE</button>
<button id="stop" (click)="stopTimer()">STOP</button>
<button (click)="removeAll()">REMOVE ALL</button>
</div>
<div class="containerDown">
<p>{{ display }}</p>
</div>
</div>
<div class="boxRight">
<div class="containerLeft">
<ul class="listElementLeft" *ngFor="let item of tasks">
<div class="card">
<ul class="order">
<li class="list-group-item"><span>START:</span>{{ item.start }}</li>
<li class="list-group-item"><span>END:</span>{{ item.end }}</li>
<li class="list-group-item">
<span>LENGTH:</span>{{ item.length }}
</li>
</ul>
<div class="subcard">
<li>
{{ item.name }}
</li>
</div>
<div class="buttonCard">
<button (click)="removeTask(item.id)">REMOVE</button>
</div>
</div>
</ul>
</div>
</div>
</div>
TS:
import { importExpr } from '#angular/compiler/src/output/output_ast';
import { Component, OnInit } from '#angular/core';
import { timer } from 'rxjs';
import { Activity } from '../activity';
import { Result } from '../result';
#Component({
selector: 'app-timer',
templateUrl: './timer.component.html',
styleUrls: ['./timer.component.scss'],
})
export class TimerComponent implements OnInit {
ngOnInit() {
this.tasks = JSON.parse(localStorage.getItem('token') ?? '');
}
time: number = 0;
display: string | undefined;
interval: any;
modalboxActive = false;
startTime: string | undefined;
endTime: string | undefined;
id: number | undefined;
activity: Activity = {
name: '',
};
tasks: Result[] = [];
startFunc() {
this.startTime = new Date().toString().split(' ')[4];
}
endFunc() {
this.endTime = new Date().toString().split(' ')[4];
}
//task actions
addTask() {
var el: Result = {
id: this.id!,
name: this.activity.name,
end: this.endTime,
start: this.startTime,
length: this.display,
};
this.tasks.push(el);
localStorage.setItem('token', JSON.stringify(this.tasks));
this.activity.name = '';
this.modalboxActive = false;
this.resetTimer();
}
removeAll() {
localStorage.clear();
}
removeTask(id: number) {
// this.tasks.splice(id, 1);
this.tasks = this.tasks.filter((item) => item.id !== id);
}
//timer actions
startTimer() {
console.log('go');
this.interval = setInterval(() => {
if (this.time === 0) {
this.time++;
} else {
this.time++;
}
this.display = this.transform(this.time);
}, 1000);
this.startFunc();
}
transform(value: number): string {
var sec_num = value;
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - hours * 3600) / 60);
var seconds = sec_num - hours * 3600 - minutes * 60;
return hours + ':' + minutes + ':' + seconds;
}
pauseTimer() {
clearInterval(this.interval);
}
stopTimer() {
console.log('show');
this.modalboxActive = true;
clearInterval(this.interval);
this.endFunc();
}
resetTimer() {
console.log('reset');
this.time = 0;
}
}
Result interface:
export interface Result {
id: number;
start: string | undefined;
end: string | undefined;
length: string | undefined;
name: string | undefined;
}
It looks like when you add an item in task list, the task.id is always undefined. You need to give it an unique id in order to identify it on removal.
Try to change the following code:
from:
ngOnInit() {
this.tasks = JSON.parse(localStorage.getItem('token') ?? '');
}
// task actions
addTask() {
var el: Result = {
id: this.id!, // always is undefined
name: this.activity.name,
end: this.endTime,
start: this.startTime,
length: this.display,
};
this.tasks.push(el);
localStorage.setItem('token', JSON.stringify(this.tasks));
this.activity.name = '';
this.modalboxActive = false;
this.resetTimer();
}
removeAll() {
localStorage.clear();
}
removeTask(id: number) {
// this.tasks.splice(id, 1);
this.tasks = this.tasks.filter((item) => item.id !== id);
}
to:
ngOnInit() {
this.tasks = JSON.parse(localStorage.getItem('token')) ?? [];
}
// task actions
addTask() {
var el: Result = {
id: this.tasks.length + 1,
name: this.activity.name,
end: this.endTime,
start: this.startTime,
length: this.display,
};
this.tasks.push(el);
localStorage.setItem('token', JSON.stringify(this.tasks));
this.activity.name = '';
this.modalboxActive = false;
this.resetTimer();
}
removeAll() {
this.tasks = [];
localStorage.removeItem('token');
}
removeTask(id: number) {
this.tasks = this.tasks.filter((item) => item.id !== id);
localStorage.setItem('token', JSON.stringify(this.tasks)); // also, update localStorage
}
You can check a working example here.
I'm trying to create a timer in Angular in which I should be able to save the various values of the time (start, finish and length, right now just length) and "print" them in HTML.
I tried inserting this.display in the length property of the interface "result" but it says "Property 'display' is used before its initialization". I think I should be using the ngOnInit or the constructor in some way but I'm unsure on how to proceed.
I've created the function clearTimer() which should both work as a function for clearing the current displayed timer and saving the value in a list.
Feel free to also correct my current code if you don't find it optimal for its purpose.
HTML:
<div class="modalbox" [class.active]="modalboxActive">
<div class="modal">
<p>What did you do?</p>
<input type="text" [(ngModel)]="activity.name" #task />
<button
(click)="addTask(task.value)"
(click)="task.value = ''"
[disabled]="activity.name === ''"
(click)="clearTimer()"
>
OK
</button>
</div>
</div>
<div class="boxSuper">
<div class="boxLeft">
<div class="containerUp">
<button id="start" (click)="startTimer()">START</button>
<button id="pause" (click)="pauseTimer()">PAUSE</button>
<button id="stop" (click)="stopTimer()">STOP</button>
</div>
<div class="containerDown">
<p>{{ display }}</p>
</div>
</div>
<div class="boxRight">
<div class="containerLeft">
<div class="leftList">
<ul class="listElement" *ngFor="let item of duration">
<li>
Length:
<span>{{ item.length }}</span>
</li>
</ul>
</div>
</div>
<div class="containerRight">
<div class="rightList">
<ul class="listElement" *ngFor="let item of list">
<li>
Activity:
<span>{{ item.name }}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
TS:
import { importExpr } from '#angular/compiler/src/output/output_ast';
import { Component, OnInit } from '#angular/core';
import { timer } from 'rxjs';
import { Activity } from '../activity';
import { Result } from '../result';
#Component({
selector: 'app-timer',
templateUrl: './timer.component.html',
styleUrls: ['./timer.component.css'],
})
export class TimerComponent implements OnInit {
ngOnInit() {}
time: number = 0;
display: string | undefined;
interval: any;
modalboxActive = false;
activity: Activity = {
name: '',
};
list: any[] = [];
result: Result = {
start: undefined,
end: undefined,
length: this.display, // here display gets marked in red and it says "Property 'display' is used before its initialization."
};
duration: any[] = [];
addTask(item: string) {
this.list.push({ name: this.activity.name });
this.activity.name = '';
this.modalboxActive = false;
}
clearTimer() {
this.duration.push({ length: this.result.length });
}
startTimer() {
console.log('go');
this.interval = setInterval(() => {
if (this.time === 0) {
this.time++;
} else {
this.time++;
}
this.display = this.transform(this.time);
}, 1000);
}
transform(value: number): string {
var sec_num = value;
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - hours * 3600) / 60);
var seconds = sec_num - hours * 3600 - minutes * 60;
return hours + ':' + minutes + ':' + seconds;
}
pauseTimer() {
clearInterval(this.interval);
}
stopTimer() {
this.modalboxActive = true;
clearInterval(this.interval);
console.log('show');
}
}
"Result" interface:
export interface Result {
start: string | undefined;
end: string | undefined;
length: string | undefined;
}
I am trying to calculate total duration from the entered values. But when I start to type, it triggers the calculateTotal function. And gives me this error:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'null: 273'. Current value: 'null: 294'.
Why this function triggers and changes the values randomly? Shouldn't it be triggered after clicking the button?
app.component.html
<div>
<label>Duration</label>
<input [(ngModel)]="durationModel.Duration" type="text" name="Duration" value="" #inputValue />
<button (click)="addDuration()">Submit</button>
<div [hidden]="durationList.length === 0">
<ul>
<li *ngFor="let duration of durationList">
{{calculateDuration(duration)}}
</li>
</ul>
<div>Total Duration : {{calculateTotal(21)}}</div>
</div>
</div>
app.component.ts
export class DurationModel {
Duration: number;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
durationModel: DurationModel = new DurationModel();
durationList = [];
totalDuration = 0;
addDuration() {
this.durationList.push(this.durationModel.Duration);
this.durationModel = new DurationModel();
}
calculateDuration(duration) {
if (duration > 60) {
const hours = Math.floor(duration / 60);
const minutes = Math.floor(duration % 60);
return `${hours} hours ${minutes} min.`;
}
return `${duration} min.`;
}
calculateTotal = (duration) => {
console.log(duration);
return this.totalDuration += duration;
}
}
Stackblitz Demo
The reason for this is because your html invokes the method calculateTotal(...) when the page loads. Make the following changes:
.ts
export class AppComponent {
calTotal;
...
calculateTotal = (duration) => {
console.log(duration);
this.calTotal = this.totalDuration += duration;
}
}
.html
<div>Total Duration : {{ calTotal }}</div>
Thereby, on click of the button the appropriate value is stored in calTotal and it can be retrieved in the template accordingly.
EDIT:
Your approach to sum up the time entered by the user is way to complicated IMO. You can simplify it to:
.ts
export class AppComponent {
durationList = [];
totalDuration = 0;
displayTotal: string;
addDuration(time: number) {
this.durationList.push(time);
this.totalDuration = this.totalDuration + Number(time);
this.displayTotal = this.calculateDuration(this.totalDuration);
}
calculateDuration(duration) {
if (duration > 60) {
const hours = Math.floor(duration / 60);
const minutes = Math.floor(duration % 60);
return `${hours} hours ${minutes} min.`;
}
return `${duration} min.`;
}
}
.html
<div>
<label>Duration</label>
<input type="number" #inputValue />
<button (click)="addDuration(inputValue.value)">Submit</button>
<div [hidden]="durationList.length === 0">
<ul>
<li *ngFor="let duration of durationList">
{{calculateDuration(duration)}}
</li>
</ul>
<div>Total Duration : {{displayTotal}}</div>
</div>
</div>
Stackblitz Demo
I have a series of questions which have radio answer choices. I can't figure out how to use angular validation to require the user to select all answer before clicking save & next.
NOTE: Please note that click save & next button gets the next 5 question from the controller depending on what choice was made. It's basically a dynamic questionnaire. Below is my code.
<ion-content id="testmodeCont" >
<div class="midCont mrgn0" >
<form #f="ngForm" class="login">
<div class="formCont">
<div *ngIf = "questionCategory == 'Diabetes'">
<div class="boxInCont" *ngFor="let question of AllQuestions | slice:numberOfQuestionsToDisplay:numberOfQuestionsToleft; let i = index">
<div *ngIf="mLanguageFlag === 'English'" >
<div class="heading" *ngIf="question.QuestionType=='dropdown'||question.QuestionType=='radio'||question.QuestionType=='toggle'|| question.QuestionType=='multiselect' ">
Q. {{question.QuestionText}}
</div>
</div>
<div *ngIf="mLanguageFlag === 'English'">
<div class="row mrgnB20" *ngIf="question.QuestionType=='radio'">
<div>
<div class="quetxt">
<ion-list radio-group [ngModelOptions]="{standalone: true}" [(ngModel)]="AllGroupAnswers[i].ExpectedAnswerId" >
<ion-item class="opteach" *ngFor="let answer of question.ExpectedAnswers">
<ion-label class="radioBox fr">{{answer.Answer}}
</ion-label>
<ion-radio item-left [value]="answer.ExpectedAnswerId" (ionSelect)="ValueChanged(answer)">
</ion-radio>
</ion-item>
</ion-list>
</div>
</div>
</div>
</div>
<div *ngIf="mLanguageFlag === 'English'">
<ion-item class="row" *ngIf="question.QuestionType=='dropdown'">
<ion-select formControlName="option" okText="Okay" cancelText="Nah" placeholder= "{{'personal.select'|translate}}" interface = "alert">
<ion-option *ngFor="let answer of question.ExpectedAnswers" [value]="answer.Answer" (ionSelect)="ValueChanged(answer)">{{answer.Answer}}
</ion-option>
</ion-select>
</ion-item>
</div>
</div>
</div>
</div>
</form>
</div>
<div class ="testbtn">
<ion-grid>
<ion-row>
<ion-col align-self-center>
<button ion-button block color="button-color" class="prevBtn" type="submit" (click) = "previous()" *ngIf="previousState" >{{"Previous" | translate}}</button>
</ion-col>
<ion-col align-self-end>
<button ion-button block color="button-color" class="saveBtn" type="submit" (click) = "next()" *ngIf="isDataLoad" >{{"Save & Next" | translate}}</button>
</ion-col>
</ion-row>
</ion-grid>
</div>
ts file
import { Component } from '#angular/core';
import { NavController, NavParams, AlertController, ToastController } from 'ionic-angular';
import { UserService } from '../../providers/service/share';
import { Service } from '../../providers/service/service';
import { Functions } from '../../providers/service/functions';
import { Values } from '../../providers/service/values';
import { UtilService } from '../../providers/service/util';
import { FeedsPage } from '../feeds/feeds';
import { TestPage } from '../test/test';
#Component({
templateUrl: 'testmode.html',
})
export class TestmodePage {
questionCategory: any
ckeckedArray: any;
answersID: any;
userPrefernce: any;
AllQuestions: any;
edittest: any;
testQuestionList: any;
TestQuestions: any;
lifestyleAnswers: any;
AllGroupAnswers: any;
AllTestAnswer: any;
ShowlifeStyleSave: boolean = false;
ExpectedAnswers: any;
mLanguageSelect: any;
mLanguageClass: any;
mLanguageFlag: any;
mLanguageClassdisc: any;
mLanguageClassWhite: any;
mLanguageClassGreen: any;
success: any;
numberOfQuestionsToDisplay: any;
numberOfQuestionsToleft: any
previousState: boolean = false;
isDataLoad: boolean = false;
pageIndex: any;
testform: FormGroup;
constructor(public navCtrl: NavController,public alertCtrl: AlertController,private toastCtrl: ToastController, public navParams: NavParams, public functions: Functions, public values: Values, public utilServices: UtilService, public userService: UserService, public service: Service) {
this.numberOfQuestionsToDisplay = 0;
this.numberOfQuestionsToleft = 5;
this.questionCategory = this.navParams.get('questionCategory')
this.mLanguageFlag = 'English';
this.testQuestionList = [];
this.TestQuestions = [];
this.lifestyleAnswers = [];
this.AllGroupAnswers = [];
this.AllTestAnswer = [];
this.ExpectedAnswers = [];
this.AllQuestions = [];
this.pageIndex = 0 ;
console.log(this.questionCategory);
this.setTestInfo();
}
ionViewDidLoad(){
this.mLanguageSelect = this.userService.getLocaleDisplayName();
console.log(this.mLanguageSelect)
if(this.mLanguageSelect == 'english'){
this.mLanguageClass = ' ';
this.mLanguageFlag='English';
this.mLanguageClassWhite = ' ';
this.mLanguageClassGreen = ' ';
}else if(this.mLanguageSelect == 'hindi'){
this.mLanguageClass = 'heading2';
this.mLanguageClassWhite = 'heading3';
this.mLanguageFlag='Hindi';
this.mLanguageClassGreen = 'heading4';
}
}
setTestInfo(){
var url = '/api/JM_TestQuestions?userId=1002&questionCategory='+ this.questionCategory ;
this.service.jivaGetMethod('1234567899','mobile',url).then((results) => {
console.log(results);
this.userService.setLifeStyleInfo(results);
this.edittest = this.userService.getLifeStyleInfo();
console.log("Your question",this.edittest);
this.AllQuestions = this.edittest.TestQuestions;
console.log("Your answer", this.AllQuestions);
for (var i = 0; i < this.AllQuestions.length; i++) {
// console.log("hello", this.AllQuestions[i]);
var obj= this.AllQuestions[i]
this.AllTestAnswer[i] = obj.ExpectedAnswers;
console.log(this.AllTestAnswer[i]+ i);
this.TestQuestions.push(obj);
console.log(this.TestQuestions.push(obj));
if (obj.ExpectedAnswers[0].UserAnswer==true){
this.AllGroupAnswers.push(obj.ExpectedAnswers[0]);
}else if(obj.ExpectedAnswers[1].UserAnswer==true){
this.AllGroupAnswers.push(obj.ExpectedAnswers[1]);
}else {
this.AllGroupAnswers.push({});
}
console.log("working");
this.testQuestionList.push(obj);
this.isDataLoad = true;
}
})
.catch((error)=>{
console.log(error);
})
}
ValueChanged(answer){
console.log(answer);
console.log(this.AllQuestions) ;
for (var i = 0; i < this.AllQuestions.length; i++) {
if (this.AllQuestions[i].QuestionId==answer.QuestionId) {
for (var j = 0; j < this.AllQuestions[i].ExpectedAnswers.length; j++) {
console.log(this.AllQuestions[i].ExpectedAnswers[j].Answer);
if (this.AllQuestions[i].ExpectedAnswers[j].ExpectedAnswerId==answer.ExpectedAnswerId) {
console.log('match-->',this.AllQuestions[i].ExpectedAnswers[j].ExpectedAnswerId,'-------',answer.ExpectedAnswerId);
this.AllQuestions[i].ExpectedAnswers[j].UserAnswer=true;
console.log(this.AllQuestions[i].ExpectedAnswers[j]);
}else {
console.log('not match-->',this.AllQuestions[i].ExpectedAnswers[j].ExpectedAnswerId,'-------',answer.ExpectedAnswerId);
this.AllQuestions[i].ExpectedAnswers[j].UserAnswer=false;
}
}
break;
}
}
}
submitTest(){
console.log(this.testQuestionList);
var TestQuestions = this.testQuestionList ;
var testData = {
"User": {
"UserId": 79163,
"FirstName": null,
"LastName": null,
"MobileNo": null,
"EmailId": null,
"UserSfdcId": null,
"FacebookId": null,
"GooglePlusId": null,
"AccessToken": null,
"AuthType": null,
"UserLanguage": null,
"UserDeviceId": null,
"UserPersonalDetails": null,
"CountryCode": 0
},
"TestQuestions": TestQuestions
};
this.functions.presentLoading(this.values.lan.WaitTitle);
// var identity = this.userService.getUserIdentity();
// var authtype = this.userService.getUserData().AuthType;
var url = 'http:///api/JM_TestQuestions';
this.service.submitSetUser1('1234567899','mobile',url,JSON.stringify(testData)).then((results) => {
console.log(results);
this.success = results
console.log(this.success);
this.functions.dismissLoading();
//this.navCtrl.setRoot(TestresultsPage);
}) .catch((error)=> {
alert("Please try after sometime..");
this.functions.dismissLoading();
})
}
next(){
this.previousState = true;
console.log(this.AllQuestions.length)
console.log(this.numberOfQuestionsToleft)
if(this.AllQuestions.length > this.numberOfQuestionsToleft) {
this.pageIndex++;
console.log(this.pageIndex)
this.numberOfQuestionsToDisplay = this.numberOfQuestionsToDisplay +5;
this.numberOfQuestionsToleft = this.numberOfQuestionsToleft + 5;
console.log(this.numberOfQuestionsToDisplay);
console.log(this.numberOfQuestionsToleft);
this.submitTest();
} else {
this.submitTest();
let toast = this.toastCtrl.create({
message: 'Your answer for ' + this.questionCategory + ' test was saved successfully',
duration: 2000,
position: 'top'
});
toast.onDidDismiss(() => {
console.log('Dismissed toast');
});
toast.present();
this.navCtrl.setRoot(FeedsPage);
}
}
previous() {
this.pageIndex--;
console.log(this.pageIndex)
this.numberOfQuestionsToDisplay = this.numberOfQuestionsToDisplay - 5;
this.numberOfQuestionsToleft = this.numberOfQuestionsToleft - 5;
console.log(this.numberOfQuestionsToDisplay);
console.log(this.numberOfQuestionsToleft);
this.submitTest();
if(this.pageIndex == 0){
console.log(this.pageIndex)
this.previousState = false;
}
}
ionViewWillLeave(){
let alert = this.alertCtrl.create({
title: "Jiva Ayurveda",
message: "Do you want to close the test ?",
buttons:[
{
text:"Yes",
handler: () => {
this.navCtrl.setRoot(TestPage);
}
},
{
text:"No",
handler: () => {
}
}
]
});
alert.present();
}
}
You can use a (click)=function(arg1, arg2) in order to create a function to check the specific validation you need to see happening.
Edit:
In the function you call with (click) add a logic at the beginning:
function(arg1) {
if(!arg1){
return; // eventually output a string in an error message to indicate the user needs to input or tick a box
} else {
// next page
}
}
use formgroup and initialize form like
this.loginForm = this.fb.group({
username: ['', Validators.required],
password: ['', Validators.required]
})
It throws me this error in console, however the form is working right whenever I clic.
Seriously, I don't see the mistake, I have another template with the same syntax and it doesn't throw me this error
<div class="container-fluid">
<div class="row">
<div class="col-md-5">
<div class="card">
<div class="header">
<h4 class="title">Comparativa mensual</h4>
<p class="category">Año actual</p>
</div>
<div class="content">
<app-pie-graph #graphmonths></app-pie-graph>
</div>
</div>
</div>
<div class="col-md-7">
<div class="card ">
<div class="header">
<div class="formGroup">
<form [formGroup]="dataForm" (ngSubmit)="submit($event)">
<h4 class="title inline">Comparativa diaria</h4>
<my-date-range-picker name="mydaterange" [options]="myOptions" formControlName="myDateRange"></my-date-range-picker>
<button type="submit" class="btn btn-primary">Consultar</button>
</form>
</div>
<p class="category">Vista de una semana</p>
</div>
<div class="content">
<div class="row">
<div class="col-sm-8">
<app-pie-graph #graphdays></app-pie-graph>
</div>
</div>
</div>
</div>
</div>
</div>
Error
ERROR Error: formControlName must be used with a parent formGroup directive. You'll want to add a formGroup
directive and pass it an existing FormGroup instance (you can create one in your class).
Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
UPDATE: ts file------------------------------------------------------------------------------------------------------------------------------------------------
import { Component, ViewChild, OnInit, AfterViewInit, QueryList } from '#angular/core';
import { LinearGraphComponent } from '../../shared/graph/linear-graph/linear-graph.component';
import { PieGraphComponent } from '../../shared/graph/pie-graph/pie-graph.component';
import { Validators, FormGroup, FormControl } from '#angular/forms'
import { Data } from '../data';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-overview',
templateUrl: './overview.component.html',
styleUrls: ['./overview.component.css']
})
export class OverviewComponent implements AfterViewInit, OnInit{
#ViewChild('graphdays') private pieGraphDays: PieGraphComponent;
#ViewChild('graphmonths') private pieGraphMonths: PieGraphComponent;
#ViewChild('generaloverview') private linearGraph: LinearGraphComponent;
//COMMON
public getDataRetriever(): Data { return this.dataRetriever; }
//COMMON
//private disableButton(){ this.blocked = true; }
//COMMON
//private activateButton(){ this.blocked = false; }
//COMMON VARIABLE
private userid = parseInt(this.route.parent.snapshot.params.id);
private dataForm = new FormGroup({
myDateRange: new FormControl(null, Validators.required)
});
constructor(
private dataRetriever: Data,
private route: ActivatedRoute
){
}
ngOnInit(){
}
private getMonthAgo(since: Date): Date{
var monthAgo = new Date(since.getTime());
monthAgo.setMonth(monthAgo.getMonth() - 3);
monthAgo.setHours(8, 0, 0, 0);
return monthAgo;
}
private displayLastMonthDaysLinear(){
var that = this;
var yesterday = this.getYesterday();
var monthAgo = this.getMonthAgo(yesterday);
this.getDataRetriever().getRangeDays(this.userid, monthAgo, yesterday, function(){
let data = that.getDataRetriever().getData();
let labels = that.getDataRetriever().getXLabels();
console.log(labels);
that.linearGraph.setChart(data, labels);
});
}
private displayLastWeekPie(){
var that = this;
var monday = this.getMondayCurrentWeek();
var yesterday = this.getYesterday();
if(monday.getDate() === new Date().getDate()) //If today is monday
monday.setDate(monday.getDate()-7); //Get monday from previous week
this.getDataRetriever().getRangeDays(this.userid, monday, yesterday, function(){
let data = that.getDataRetriever().getData();
let labels = that.getDataRetriever().getXLabels();
console.log(labels);
that.pieGraphDays.setChart(data[0].data, labels);
});
}
private displayLastMonthsPie(){
var now = new Date();
var newYear = new Date(now.getFullYear(), 0, 1, 8, 0, 0, 0);
var last = new Date(new Date().setMonth(now.getMonth()-1));
var that = this;
if(newYear.getMonth() === now.getMonth()) //If we are in January (spetial case)
newYear.setFullYear(newYear.getFullYear() - 1); //Get January from past year
this.getDataRetriever().getCountingPerMonth(this.userid, newYear, last, function(){
let data = that.getDataRetriever().getData();
let las = that.getDataRetriever().getXLabels();
console.log(data);
that.pieGraphMonths.setChart(data, las);
});
}
private getDaysToMonth(month, year): number[] { //month not included
var date = new Date(year, month, 1);
var days = [];
while (date.getMonth() < month) {
days.push(new Date(date).setHours(8,0,0,0));
date.setDate(date.getDate() + 1);
}
return days;
}
private getYesterday(): Date{
var today = new Date();
today.setDate(today.getDate() - 1);
today.setHours(8,0,0,0);
return today
}
private getMondayCurrentWeek(): Date{
var d = new Date();
var day = d.getDay(),
diff = d.getDate() - day + (day == 0 ? -6:1); // adjust when day is sunday
d.setDate(diff);
d.setHours(8,0,0,0);
return d;
}
ngAfterViewInit(){
this.displayLastMonthsPie();
this.displayLastWeekPie();
this.displayLastMonthDaysLinear();
console.log(this.linearGraph);
}
submit(){
let range = this.getPickedDayRange();
var that = this;
this.getDataRetriever().getRangeDays(this.userid, range[0], range[1], function(){
let data = that.getDataRetriever().getData();
let labels = that.getDataRetriever().getXLabels();
that.pieGraphDays.setChart(data[0].data, labels);
});
}
//COMMON CLASS
private getPickedDayRange(): Date[]{
var begDate = new Date(this.dataForm.value.myDateRange.beginJsDate);
var endDate = new Date(this.dataForm.value.myDateRange.endJsDate);
return [begDate, endDate];
}
}
I pieced together the answers from another post.
The short answer is that you have to declare the formGroup in the parent component, then pass in the formGroup to the child component.
app.component.ts
feedbackValues = [1,2,3,4,5];
constructor(private formBuilder: FormBuilder) {
this.surveyForm = this.formBuilder.group({
rowForm: this.formBuilder.group({
rating: ['']
})
});
app.component.html
<form [formGroup]="surveyForm" (ngSubmit)="onSubmit()" >
<app-survey-row [feedbackValues]=feedbackValues [parentGroup]="surveyForm"></app-survey-row>
<button type="submit">Submit</button>
</form>
survey-row.component.ts
export class SurveyRowComponent implements OnInit {
#Input() feedbackValues;
#Input() parentGroup: FormGroup;
constructor( private formBuilder: FormBuilder) {
}
ngOnInit() {
}
}
survey-row.component.html
<div [formGroup]="parentGroup">
<div formGroupName="rowForm">
<div *ngFor="let val of feedbackValues; let i = index">
<input id="{{i+1}}" type="radio" value="{{i+1}}" formControlName="rating">
<label for="{{i+1}}">{{i+1}}</label>
</div>
</div>
</div>
Notice how the [formGroup] is passed in by the parent component, and the formGroupName is not declared in survey-row, but in app.component.ts
dataForm is declared as private in your component. What if you try to declare it as public (in order to make it reachable from the view)?
I didn't realize which was the solution but finally I didn't need those formularies because the widget triggers already a function