2

My angular SPA doesn't refresh when my EventEmitter emits an event.

Here is my EventEmitter in the child component:

import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Todo } from '../todo';

@Component({
  selector: 'app-add-todo',
  templateUrl: './add-todo.component.html',
  styleUrls: ['./add-todo.component.css']
})
export class AddTodoComponent implements OnInit {
  newTodo: Todo = new Todo();

  @Output() addTodoEvent: EventEmitter<Todo> = new EventEmitter();

  constructor() { }

  addTodo() {
    this.addTodoEvent.emit(this.newTodo);
    console.log('event emitted');
    this.newTodo = new Todo();
    console.log('new todo created');
  }

  ngOnInit() {
  }

}

Here is the template that consumes it:

<div class="container">
  <app-add-todo (addTodoEvent)='onAddTodo($event)'></app-add-todo>
   // other template stuff
</div>

Here is the declaration of onAddTodo:

  onAddTodo(todo: Todo) {
    console.log('onAddTodo called');
    this.todoDataService.addTodo(todo);
  }

Basically, the application works, but I have to hit F5 to refresh the page after any change is made.

One thing to note is that this used to work when I was using an array as a data source, but when I went to json-server and Observables, the page stopped refreshing.

I'm happy to supply any further information that may be needed.

UPDATE:

Here's addTodo:

  addTodo(todo: Todo): void {
    this.aHttpService.post<Todo>(`http://localhost:3000/todos`, todo).subscribe(
      val => {
        console.log('POST call successful value returned in body', val);
      },
      response => {
        console.log('POST call in error', response);
      },
      () => {
        console.log('The POST observable is now completed.');
      }
    );
  }

The full code to list the todos is as follows:

import { Component, OnInit } from '@angular/core';
import { Todo } from '../todo';
import { TodoDataService } from '../todo-data.service';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-todo-list',
  templateUrl: './todo-list.component.html',
  styleUrls: ['./todo-list.component.css']
})
export class TodoListComponent implements OnInit {
  constructor(private todoDataService: TodoDataService) {}

  completetodos: Observable<Array<Todo>>;
  incompletetodos: Observable<Array<Todo>>;

  onAddTodo(todo: Todo) {
    console.log('onAddTodo called');
    this.todoDataService.addTodo(todo);
  }

  toggleTodoComplete(todo) {
    this.todoDataService.toggleTodoComplete(todo);
  }

  removeTodo(todo) {
    this.todoDataService.deleteTodoById(todo.id);
  }

  editTodo(todo: Todo) {
    todo.editMode = true;
  }

  updateTodo(todo: Todo, editInput) {
    todo.title = editInput.value;
    todo.editMode = false;
    this.todoDataService.updateTodoById(todo.id, todo);
  }

  allTasks(): Observable<Array<Todo>> {
    return this.todoDataService.getAllTodos();
  }

  completedTodos(): Observable<Array<Todo>> {
    return this.todoDataService.completedTodos();
  }

  incompletedToDos(): Observable<Array<Todo>> {
    return this.todoDataService.incompletedTodos();
  }

  ngOnInit() {
    this.completetodos = this.completedTodos();
    this.incompletetodos = this.incompletedToDos();
  }
}
11
  • addTodo is executing an Http call? Commented Oct 15, 2018 at 2:53
  • @AlexanderStaroselsky -- Added my addToDo call. Commented Oct 15, 2018 at 2:54
  • It's a bit difficult to figure out with piecemeal snippets of code like this, and apparent typos (you have an EventEmitter named 'addTodoEvent', but a call to 'add.emit'). A complete running example, perhaps on stackblitz.com would help a lot. Commented Oct 15, 2018 at 2:58
  • You need to please clarify what you are expecting to have updated on the screen after the post(). Are you pushing the new Todo to an array after post success or anything for display in a view or anything? Commented Oct 15, 2018 at 3:00
  • @NickHodges can u pls post your code where list the todos? Commented Oct 15, 2018 at 3:10

2 Answers 2

1

Once you are done with the successful save of the todo , You have to update the observables

 onAddTodo(todo: Todo) {
this.todoDataService.addTodo(todo).subscribe(
      val => {
         // here you have to update the observables //incompletetodos.next(newTodo)
      },
      response => {
        console.log('POST call in error', response);
      },
      () => {
        console.log('The POST observable is now completed.');
      }
    );
}

Also you don't need to subscribe here

 addTodo(todo: Todo): void {
    return this.aHttpService.post<Todo>(`http://localhost:3000/todos`, todo);
  }

I think push won't work for observables , You can refer the below link to append a new item in to the incompletetodos observable array

How can I append items to Observable

If you would use BehaviorSubject(another type of observable) instead of Observable , you can easily add a new item. Something like below

incompletetodos: BehaviorSubject<Array<Todo>> = BehaviorSubject.empty<Array<Todo>>();

//you can push a new item like below

 incompletetodos.next(newTodo)
0
1

Your service should return the observable like this:

 addTodo(todo: Todo): void {
    return this.aHttpService.post<Todo>(`http://localhost:3000/todos`, todo);
 }

And using it in the component like this:

 onAddTodo(todo: Todo) {
    this.todoDataService.addTodo(todo).subscribe(val => {
       this.incompletetodos.push(val);
    });;
 }

(I minimized the code)


Reference: https://angular.io/tutorial/toh-pt6#add-a-new-hero

src/app/heroes/heroes.component.ts (add)

add(name: string): void {
  name = name.trim();
  if (!name) { return; }
  this.heroService.addHero({ name } as Hero)
    .subscribe(hero => {
      this.heroes.push(hero);
    });
}

src/app/hero.service.ts (addHero)

/** POST: add a new hero to the server */
addHero (hero: Hero): Observable<Hero> {
  return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
    tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
    catchError(this.handleError<Hero>('addHero'))
  );
}

Why doesn't my Angular page refresh when using an EventEmitter?

You might think this way, we're using Angular to build a single page application which doesn't cause any full page refresh, we just update a part of the website.

Basically, the application works, but I have to hit F5 to refresh the page after any change is made.

Because whenever you hit F5, your Angular application well be re-initialized which fires this lifecycle hook:

ngOnInit() {
    this.completetodos = this.completedTodos();
    this.incompletetodos = this.incompletedToDos();
}

Therefore, your application reflects the up-to-date data of the database.

One thing to note is that this used to work when I was using an array as a data source, but when I went to json-server and Observables, the page stopped refreshing.

In fact, when you're using an array as a data source, our application doesn't refresh at all. That's something call data-binding, the data which we see on the UI is bound to the data which the Angular application holding (on client-side, in client's memory).

When communicating with the server, when we make some changes in the server's database, we have to update our client's data based on the response from the server. This is how we'll do in this case, in your Todo App.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.