I have dataServiceEvent output from Component B to Component A , How do I get or access the dataServiceEvent data inside ngOnit on Component A ? I can access it outside ngOinit as function but I wanna access it inside ngOnit cause I wanna use the data from dataServiceEvent as params to getListOfDeals .
Thanks for any help or idea. Regards.
#Component A ts code
ngOnInit(): void {
//access the dataServiceEvent here
this.getListOfDeals()
}
// I can access it here but I want to access the data on ngOnInit
dataServiceEvent(item: any) {
this.tableElements = item;
// this.getListOfDeals();
}
private getListOfDeals() {
this.searchInput = '';
this.isLoading = true;
console.log("getting deals")
this.dealService
.getAllDeals(
this.accountId,
this.transaction.id,
this.tableElements.pageIndex + 1,
this.tableElements.pageSize,
this.searchInput,
this.tableElements.sortParams = ['name'],
this.tableElements.sortDirs = ['asc']
)
.pipe(finalize(() => (this.isLoading = false)))
.subscribe({
error: (err) => this.notificationService.showError(err),
next: (res) => {
// this.dealsListData[totalElements] = res.items.length;
this.dealsListData = res.totalItemCount;
this.dealsListData = res.lastItemOnPage;
this.dealsListData = res.items;
console.log('res', this.dealsListData);
},
complete: noop,
});
}
#Component A html code
<app-table-multi-sort (dataServiceEvent)="dataServiceEvent($event)" [tableOptions]="tableOptions" [tableData]="dealsListData" (tableActionsEvent)="tableActions($event)"></app-table-multi-sort>
#Component B code - dataServiceEvent is the Output from this Component To Component A
export class TableMultiSortComponent implements OnInit {
#Output() dataServiceEvent = new EventEmitter<any>() ;
#Input() tableOptions:any;
#Input() tableData:any = [];
#Input() isClientSide:boolean = false;
#Input() isLoading: boolean = false;
#Output() tableActionsEvent = new EventEmitter<any>();
#ViewChild(MatMultiSort, { static: false }) sort: MatMultiSort;
tableConfig: any = TABLE_MULTI_SORT_OPTIONS.DEFAULT;
table:TableData<any>;
displayedColumns: any;
constructor() { }
ngOnInit(): void {
this.initTableMultiSort();
}
initTableMultiSort(){
this.tableConfig = {
...this.tableConfig,
...this.tableOptions
}
this.table = new TableData<any>(this.tableConfig.columns,this.tableConfig.sortParams);
this.table.pageSize = this.tableConfig.pageSize;
this.table.pageIndex = this.tableConfig.pageIndex;
this.table.nextObservable.subscribe(() => { this.getData(); });
this.table.sortObservable.subscribe(() => { this.getData(); });
this.table.previousObservable.subscribe(() => { this.getData(); });
this.table.sizeObservable.subscribe(() => { this.getData(); });
setTimeout(()=>{
this.table.dataSource = new MatMultiSortTableDataSource(this.sort, this.isClientSide);
this.getData();
},0);
}
ngOnChanges(changes: SimpleChanges) {
if (changes.tableData && changes.tableData.currentValue){
this.initTableMultiSort()
}
}
getData(){
//Todo: get totalelement, pageindex, pagesize from api service response
this.table.totalElements = 1;
this.table.pageIndex = 0;
this.table.pageSize = 10;
this.table.data = this.tableData;
if(this.dataServiceEvent) {
this.dataServiceEvent.emit(this.table);
}
}
You can define a property in the typescript of component A decorated with #ViewChild to get a reference of the component B (NOTE: the 'static' property of ViewChild was introduced in Angular 8):
#ViewChild(TableMultiSortComponent, { static: true }) tableMultiSortComponent: TableMultiSortComponent;
Then, inside the ngOnInit method of component A, you can subscribe to the dataServiceEvent EventEmitter of component B (EventEmitter is a particular case of a RxJS Subject):
// This property is used as the notifier of the takeUntil operator to prevent memory leaks in your code:
private _destroyed$ = new Subject();
ngOnInit() {
this.tableMultiSortComponent.dataServiceEvent
.pipe(
// switchMap subscribes to a new call everytime dataServiceEvent emits an event, and cancels previous calls, if any - You can use another RxJS higher order mapping operator depending on your needs (check: https://blog.angular-university.io/rxjs-higher-order-mapping/):
switchMap((event) => this.getListOfDeals(event)),
// The takeUntil operator is useful to destroy the subscription to avoid memory leaks once the component gets destroyed, you can use it for other subscriptions as well
takeUntil(this._destroyed$)
)
// The subscribe was moved here from the 'getListOfDeals' method
.subscribe({
error: (err) => this.notificationService.showError(err),
next: (res) => {
// this.dealsListData[totalElements] = res.items.length;
this.dealsListData = res.totalItemCount;
this.dealsListData = res.lastItemOnPage;
this.dealsListData = res.items;
console.log('res', this.dealsListData);
},
complete: noop,
});
}
ngOnDestroy() {
this._destroyed$.next();
this._destroyed$.complete();
}
// In order to work with the subscription in the ngOnInit, here you must pass a parameter, which is the value emitted by the dataServiceEvent EventEmitter and change the method accordingly (I tried to understand it from your previous code, changing 'this.tableElements' with the 'item' parameter):
private getListOfDeals(item) {
this.searchInput = '';
this.isLoading = true;
console.log("getting deals")
return this.dealService
.getAllDeals(
this.accountId,
this.transaction.id,
item.pageIndex + 1,
item.pageSize,
this.searchInput,
item.sortParams = ['name'],
item.sortDirs = ['asc']
)
.pipe(finalize(() => (this.isLoading = false)));
}
You can handle the event and access to its data like this
Component A HTML code:
<app-table-multi-sort (dataServiceEvent)="dataServiceEvent($event)" [tableOptions]="tableOptions" [tableData]="dealsListData" (tableActionsEvent)="tableActions($event)"></app-table-multi-sort>
Component A TS file:
private _eventSubject$ = new Subject();
get eventData() {
return this.eventSubject.asObservable();
}
ngOnInit(){
this.eventData.subscribe((data) => {
console.log(data)
})
}
dataServiceEvent($event) {
this._eventSubject$.next($event);
}
Related
I am having an issue when I try to get a specified user from Firebase, Firestore.
export class TaskService {
tasksCollection: AngularFirestoreCollection<Task>;
taskDoc: AngularFirestoreDocument<Task>;
tasks: Observable<Task[]>;
task: Observable<Task>;
constructor(private afs: AngularFirestore) {
this.tasksCollection = this.afs.collection('tasks', ref => ref.orderBy('title', 'asc'));
}
getTask(id: string): Observable<Task> {
this.taskDoc = this.afs.doc<Task>(`clients/${id}`);
this.task = this.taskDoc.snapshotChanges().pipe(map(action => {
if (action.payload.exists === false) {
return null;
} else {
const data = action.payload.data() as Task;
data.id = action.payload.id;
return data;
}
}));
return this.task;
}
}
And this is my Component.ts file
export class TaskDetailsComponent implements OnInit {
id: string;
task: Task;
hasHours = false;
showHoursOnUpdate: false;
constructor(
private taskService: TaskService,
private router: Router,
private route: ActivatedRoute
) { }
ngOnInit() {
// Get id from url
this.id = this.route.snapshot.params.id;
// Get client
this.taskService.getTask(this.id).subscribe(task => {
if (task != null) {
if (task.hours > 0) {
this.hasHours = true;
}
}
this.task = task;
});
console.log(this.id);
console.log(this.task);
}
}
The result for id is good.
But the result for object (task) is undefined.
P.S
I also have functions for getting all the users and adding a new user, so if that's relevant please let me know in the comments
Your line of code
this.id = this.route.snapshot.params.id;
In this case id is not a table column but it's your document id by Firestore
Here an example of firestore
So your Id in this case is the red one and not the blue one.
EDIT: See Kurt Hamilton's answer for the solution.
I'm calling an API to return the values of some settings in settings.service.ts.
In settings.component.ts these need to be returned to fill a form - it displays loading when the API call isn't finished yet.
It's working with the 'return of(fakeData)'. However, I can't figure out how to return the 'realData'.
Instead of console.log(realData) I want to return that instead of the fakeData.
Some help would be nice, thanks in advance!
Beneath are the relevant parts of the code.
settings.service.ts:
export interface Settings {
setting1: boolean;
setting2: string;
}
const fakeData = {
setting1: true,
setting2: 'test'
};
#Injectable()
export class SettingsService {
defaultSettings: DefaultSettings[];
constructor(private apiService: ApiService) { }
loadSettings(): Observable<Settings> {
this.apiService.getDefaultSettings().subscribe( defaultSettings => {
// defaultSettings is needed for when value1 or value2 is 'null'
// not implemented yet, but therefore this nested subscription structure
this.defaultSettings = defaultSettings;
const value1 = this.apiService.getSpecificSetting('setting1');
const value2 = this.apiService.getSpecificSetting('setting2');
forkJoin([value1, value2]).subscribe( result => {
const realData = {
setting1: result[0],
setting2: result[1],
};
console.log(realData);
// return of(settingsFound); not possible here ...
});
});
return of(fakeData);
}
}
settings.component.ts
settings: Observable<Settings>;
ngOnInit() {
this.settings = this.settingsService.loadSettings().pipe(
tap(settings => {
this.settingsForm.patchValue(settings);
})
);
}
Use concatMap or switchMap to run a new observable (in your case a forkJoin) after another observable.
#Injectable()
export class SettingsService {
defaultSettings: DefaultSettings[];
constructor(private apiService: ApiService) { }
loadSettings(): Observable<Settings> {
return this.apiService.getDefaultSettings().pipe(
// save default settings
// this may not be required if you only need default settings for the forkJoin
tap(defaultSettings => this.defaultSettings = defaultSettings),
// now run the next observable
concatMap(defaultSettings => {
return forkJoin({
setting1: this.apiService.getSpecificSetting('setting1'),
setting2: this.apiService.getSpecificSetting('setting2')
});
}),
// now map the result of the forkJoin to the value to want to return
// map won't be required in this case,
// as the arg going into forkJoin matches the desired return structure
// I left it in for completeness
map(result => {
const realData = {
setting1: result.setting1,
setting2: result.setting2,
};
console.log(realData);
return realData;
})
);
}
}
Condensed version
Without my annotations and the redundant calls, the finished result looks like this:
#Injectable()
export class SettingsService {
constructor(private apiService: ApiService) { }
loadSettings(): Observable<Settings> {
return this.apiService.getDefaultSettings().pipe(
concatMap(defaultSettings => forkJoin({
setting1: this.apiService.getSpecificSetting('setting1'),
setting2: this.apiService.getSpecificSetting('setting2')
}))
);
}
}
I'm trying to send the selected data in my table row that I am selecting via a checkbox to the server but having questions about how it should be sent via a service. I have the basic skeleton but need help with getting the items to a delete REST API call. Using C# .Net Core JSON call as the server endpoint for this service call.
view.component.ts
#Component({
templateUrl: 'view.component.html'
})
export class ViewComponent implements OnInit, OnDestroy {
// User Fields
currentUser: User;
users: User[] = [];
currentUserSubscription: Subscription;
loading : boolean;
// Action Fields
viewData: any;
viewName: string;
refNumber: number;
currentActionSubscription: Subscription;
displayedColumns: string[] = [];
dataSource: any = new MatTableDataSource([]);
pageSizeOptions: number[] = [10, 20, 50];
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
selection = new SelectionModel<TableRow>(true, []);
defaultSort: MatSortable = {
id: 'defColumnName',
start: 'asc',
disableClear: true
};
defaultPaginator: MatPaginator;
constructor(
private iconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer,
private actionService: ActionService
) {
this.loading = false;
this.iconRegistry.addSvgIcon(
'thumbs-up',
this.sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/examples/thumbup-icon.svg'
)
);
}
loadAction(action: any) {
this.loading = true;
// If there is already data loaded into the View, cache it in the service.
if (this.viewData) {
this.cacheAction();
}
if (this.sort) {
// If there is sorting cached, load it into the View.
if (action.sortable) {
// If the action was cached, we should hit this block.
this.sort.sort(action.sortable);
} else {
// Else apply the defaultSort.
this.sort.sort(this.defaultSort);
}
}
if (this.paginator) {
// If we've stored a pageIndex and/or pageSize, retrieve accordingly.
if (action.pageIndex) {
this.paginator.pageIndex = action.pageIndex;
} else { // Apply default pageIndex.
this.paginator.pageIndex = 0;
}
if (action.pageSize) {
this.paginator.pageSize = action.pageSize;
} else { // Apply default pageSize.
this.paginator.pageSize = 10;
}
}
// Apply the sort & paginator to the View data.
setTimeout(() => this.dataSource.sort = this.sort, 4000);
setTimeout(() => this.dataSource.paginator = this.paginator, 4000);
// Load the new action's data into the View:
this.viewData = action.action;
this.viewName = action.action.ActionName;
this.refNumber = action.refNumber;
// TODO: add uniquifiers/ids and use these as the sort for table
const displayedColumns = this.viewData.Columns.map((c: { Name: any; }) => c.Name);
displayedColumns[2] = 'Folder1';
this.displayedColumns = ['select'].concat(displayedColumns);
// tslint:disable-next-line: max-line-length
const fetchedData = this.viewData.DataRows.map((r: { slice: (arg0: number, arg1: number) => { forEach: (arg0: (d: any, i: string | number) => any) => void; }; }) => {
const row = {};
r.slice(0, 9).forEach((d: any, i: string | number) => (row[this.displayedColumns[i]] = d));
return row;
});
this.dataSource = new MatTableDataSource(fetchedData);
this.loading = false;
}
// Stores the current Action, sort, and paginator in an ActionState object to be held in the action service's stateMap.
cacheAction() {
let actionState = new ActionState(this.viewData);
// Determine the sort direction to store.
let cachedStart: SortDirection;
if (this.sort.direction == "desc") {
cachedStart = 'desc';
} else {
cachedStart = 'asc';
}
// Create a Sortable so that we can re-apply this sort.
actionState.sortable = {
id: this.sort.active,
start: cachedStart,
disableClear: this.sort.disableClear
};
// Store the current pageIndex and pageSize.
actionState.pageIndex = this.paginator.pageIndex;
actionState.pageSize = this.paginator.pageSize;
// Store the refNumber in the actionState for later retrieval.
actionState.refNumber = this.refNumber;
this.actionService.cacheAction(actionState);
}
ngOnInit() {
// Subscribes to the action service's currentAction, populating this component with View data.
this.actionService.currentAction.subscribe(action => this.loadAction(action));
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected()
? this.selection.clear()
: this.dataSource.data.forEach((row: TableRow) => this.selection.select(row));
}
// Delete row functionality
deleteRow() {
console.log(this.selection);
this.selection.selected.forEach(item => {
const index: number = this.dataSource.data.findIndex((d: TableRow) => d === item);
console.log(this.dataSource.data.findIndex((d: TableRow) => d === item));
this.dataSource.data.splice(index, 1);
this.dataSource = new MatTableDataSource<Element>(this.dataSource.data);
});
this.selection = new SelectionModel<TableRow>(true, []);
this.actionService.deleteRow(this.selection).subscribe((response) => {
console.log('Success!');
});
}
ngOnDestroy() {
}
}
view.service.ts
deleteRow(selection: any): Observable<{}> {
console.log('testing service');
return this.http.delete<any>(`http://localhost:15217/actions/deleteRow`);
}
There are 2 things that your code as it currently stands needs to do:
Pass the ids of the selected rows back to the server in some way (generally via the url in a DELETE request)
Subscribe to the observable to materialise it. Currently the http request won't run, because it's an observable without any subscribers. At the very least the call to the service in the component should look a little like this:
this.actionService.deleteRow(this.selection).subscribe((response) => {
console.log('Success!');
});
Edit:
With number 1, it depends on what your server method looks like. If it accepts an array of numeric ids, then view.service.ts would look something like:
deleteRow(selection: SelectionModel<TableRow>): Observable<{}> {
console.log('testing service');
// create an array of query params using the property that you use to identify a table row
const queryParams = selection.selected.map(row => `id=${row.id}`);
// add the query params to the url
const url = `http://localhost:15217/actions/deleteRow?${queryParams.join('&')}`;
return this.http.delete<any>(url);
}
I'm guessing here at how you pass information about table rows to your server. If you're still struggling with this, you will need to provide a bit of information about the DELETE endpoint.
Edit 2:
Now we know a bit more about what the objects look like...
deleteRow(selection: SelectionModel<TableRow>): Observable<{}> {
console.log('testing service');
// create an array of query params using the property that you use to identify a table row
const queryParams = [...selection._selection].map(row => `id=${row.id}`);
// add the query params to the url
const url = `http://localhost:15217/actions/deleteRow?${queryParams.join('&')}`;
return this.http.delete<any>(url);
}
can any one please tell me why I can not loop through this array?
In ngOnInit, everything works fine. I got an array that I successfully display in the template.
But in ngAfterViewInit, console.log show the array but when looping through with "for of" or "forEach", nothing works.
import { JobsService } from '../jobs.service';
import {Job} from '../models/Job';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs: Job[] = [];
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs = this.jobsService.getJobs();
}
ngAfterViewInit() {
console.log(this.jobs); // Show the array
// Nothing happened when looping through the array
this.jobs.forEach((oneJob) => {
console.log(oneJob);
});
}
}
Screenshot of the console in Google Chrome
The content of the service:
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import {Job} from './models/Job';
interface IJob {
message: string;
jobs: any[];
}
#Injectable({
providedIn: 'root'
})
export class JobsService {
constructor(private httpClient: HttpClient) { }
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
private REST_API_SERVER_SEND = 'http://localhost:8080/myband/api/sendjob.php';
jobList: Job[] = [];
errorMessage: any;
message: string;
static handleError(err: HttpErrorResponse) {
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
public getJobs() {
this.requestJobs().subscribe(
iJob => {
this.message = iJob.message;
for (const job of iJob.jobs) {
const oneJob: Job = new Job(job);
this.jobList.push(oneJob);
}
},
error => this.errorMessage = error as any
);
return this.jobList;
}
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER).pipe(
catchError(JobsService.handleError)
);
}
}
The first thing I want to say to you is about isolation of responsibilities.
Your service must have just one job: provider one way to access your data; It means your logic inside getJobs() method could be done in your component.
export class JobsService {
constructor(
private httpClient: HttpClient,
) {}
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER);
}
}
Now, you can handler your data in your component.
import { JobsService } from '../jobs.service';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs$;
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs$ = this.jobsService.requestJobs();
}
ngAfterViewInit() {
this.jobs$
.pipe(
map(() => {}), // change your data here
catchError(() => {}) // handler your error here;
)
.subscribe(
() => {} // have access to your final data here.
);
}
}
Things to know:
You can remove the subscribe() execution and use the async pipe in your template;
The use of the operator map in pipe() is optional, you can handler your final data directly from your first callback subscribe().
You can convert your Observable to Promise using toPromise() method in one observable. Don't forgot async / await in your ngAfterViewInit.
Let me know if there is something I can help.
Try:
Object.keys(this.jobs).forEach(job => {
console.log(this.jobs[job]);
});
Try to assign an iterator function with below part replacement by this code:
// Nothing happened when looping through the array
this.jobs.forEach(oneJob, function(value, key) {
console.log(key + ': ' + value);
});
Usage of forEach in AngularJS:
For documentation try to check AngularJS forEach Docs
Syntax:
someIterable.forEach(object, iterator, [context])
Please check below example
class Job {
id: any;
status: any;
constructor(obj: any) {
this.id = obj.id;
this.status = obj.status;
}
}
let arr = [
{
id: 1,
status: "job"
}, {
id: 2,
status: "job2"
}
];
let newArr: any = [];
arr.forEach(a => {
let obj: Job = new Job(a);
newArr.push(obj);
})
console.log(newArr);
newArr.forEach((a: any) => {
console.log(a);
})
I'm learning Angular, so I'm building todo app. Todos are fetched from API, and every one of them has a project as parent. In a view I'm adding new project which triggers addNewProject method in service which in turn triggers POST request to the API in another service. Projects are listed in the sidebar, bind to input from parent component. ProjectsService holds array of projects in a private field, and has observable which is used by main component.
I'm struggling to understand why on earth appending private property _projects after API call in the service triggers change in MainComponent property even though _projects is private and change in Observable from array should not trigger functions passed from Observers.
Parent:
#Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
projects: Project[] = new Array<Project>();
constructor(private projectsService: ProjectsService) {
}
ngOnInit() {
this.projectsService.projects$.subscribe((projects) => {
this.projects = projects;
});
}
addNewProject(newProjectName: string) {
this.projectsService.addNewProject(newProjectName);
}
}
Sidebar:
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit {
#Input() projects: Project[];
#Output() projectEntered = new EventEmitter<string>();
constructor() {
}
ngOnInit() {
}
projectAdded(projectName: string) {
this.projectEntered.emit(projectName);
}
}
Service:
export class ProjectsService {
private _projects$: Observable<Array<Project>> = new Observable<Array<Project>>();
private _projects: Project[] = [];
private _loadedProject$: Observable<Project>;
private _projectsLoaded: boolean;
private _taskForProject: object;
constructor(private userService: UserService, private api: ApiService) {
}
loadAllProjects() {
this._projects$ = this.api.getAllProjectsByUserId(this.userService.userId).pipe(
map((projects) => {
this._projects = projects;
return this._projects;
})
);
}
get projects$() {
if (!this._projectsLoaded) {
this.loadAllProjects();
}
return this._projects$;
}
getAllTasks() {
return this.api.getAllTasksByUserId(this.userService.userId);
}
getProject(projectId: string) {
this._loadedProject$ = this.api.getProjectById(projectId);
return this._loadedProject$;
}
getTodayTasksForProject(tasks: Task[]) {
const todayTasks: Task[] = [];
const todayDate = new Date();
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() === todayDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
todayTasks.push(task);
}
});
return todayTasks;
}
getTomorrowTasksForProject(tasks: Task[]) {
const tomorrowTasks: Task[] = [];
const tomorrowDate = new Date(new Date().getDate() + 1);
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() === tomorrowDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
tomorrowTasks.push(task);
}
});
return tomorrowTasks;
}
getUpcomingTasks(tasks: Task[]) {
const upcomingTasks: Task[] = [];
const upcomingDate = new Date(new Date().getDate() + 2);
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() > upcomingDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
upcomingTasks.push(task);
}
});
return upcomingTasks;
}
addNewProject(projectName: string) {
this.api.postNewProject({
id: null,
userId: this.userService.userId,
title: projectName,
tasks: []
}).subscribe((project: Project) => {
this._projects.push(project);
});
}
}
Please see here:
Basically application state change can be caused by three things:
Events - click, submit
XHR - Fetching data from a remote server
Timers - setTimeout(), setInterval()
If you don't want change detection to fire try changing to ChangeDetectionStrategy.OnPush