DWA2112 DWA2112 - 2 months ago 8
Javascript Question

ncaught (in promise): Error: Error in ./Search class Search - inline template:4:0 caused by: Maximum call stack size exceeded

I am new to Angular2 and working on a webapp, that should execute three different calls to three different REST API´s. I implemented mocked API´s. It is not working as it should. I am getting the following error message in the developer console:

ncaught (in promise): Error: Error in ./Search class Search - inline template:4:0 caused by: Maximum call stack size exceeded

Here´s my code:

search.template.html:

<search>
<h1>Angular2 HTTP Demo App</h1>
<h2>Foods</h2>
<ul>
<li *ngFor="let food of foods"><input type="text" name="food-name" [(ngModel)]="food.name"><button (click)="updateFood(food)">Save</button> <button (click)="deleteFood(food)">Delete</button></li>
</ul>
<p>Create a new food: <input type="text" name="food_name" [(ngModel)]="food_name"><button (click)="createFood(food_name)">Save</button></p>
<h2>Books and Movies</h2>
<h3>Books</h3>
<ul>
<li *ngFor="let book of books">{{book.title}}</li>
</ul>
<h3>Movies</h3>
<ul>
<li *ngFor="let movie of movies">{{movie.title}}</li>
</ul>
</search>


search.service.ts:

import { Injectable } from '@angular/core';
import 'rxjs/add/operator/map'
import { Observable } from 'rxjs/Observable';
import { Http, Response, Headers, RequestOptions } from "@angular/http";
import 'rxjs/add/observable/forkJoin';
import 'rxjs/add/observable/of';


@Injectable()
export class SearchService {

constructor(private http:Http) {
}

// Uses http.get() to load a single JSON file
getFoods() {
return this.http.get('./food.json').map((res:Response) => res.json());
}

// Uses Observable.forkJoin() to run multiple concurrent http.get() requests.
// The entire operation will result in an error state if any single request fails.
getBooksAndMovies() {
return Observable.forkJoin(
this.http.get('./books.json').map((res:Response) => res.json()),
this.http.get('./movies.json').map((res:Response) => res.json())
);
}

createFood(food) {
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
let body = JSON.stringify(food);
// Note: This is only an example. The following API call will fail because there is no actual API to talk to.
//return this.http.post('/api/food/', body, headers).map((res:Response) => res.json());
}

updateFood(food) {
let headers = new Headers({'Content-Type': 'application/json'});
let options = new RequestOptions({headers: headers});
let body = JSON.stringify(food);
// Note: This is only an example. The following API call will fail because there is no actual API to talk to.
//return this.http.put('/api/food/' + food.id, body, headers).map((res:Response) => res.json());
}

deleteFood(food) {
// Note: This is only an example. The following API call will fail because there is no actual API to talk to.
//return this.http.delete('/api/food/' + food.id);
}
}


search.component.ts:

import {Component, Output, EventEmitter, OnInit} from '@angular/core';
import { SearchService } from '../search/search.service';
import { AppState } from '../app.service';
import { HomeService } from '../home/home.service';
import { Injectable } from '@angular/core';
import { URLSearchParams, Jsonp } from '@angular/http';
import { Observable } from 'rxjs/Observable';


@Component({
// The selector is what angular internally uses
// for `document.querySelectorAll(selector)` in our index.html
// where, in this case, selector is the string 'home'
selector: 'search', // <home></home>
// We need to tell Angular's Dependency Injection which providers are in our app.
providers: [
HomeService,
SearchService
],
//

directives: [CORE_DIRECTIVES],
// Our list of styles in our component. We may add more to compose many styles together
//styleUrls: [ ],
// Every Angular template is first compiled by the browser before Angular runs it's compiler
templateUrl: 'search.template.html'
})


export class Search {

public foods;
public books;
public movies;

public food_name;

constructor(private _searchService: SearchService) { }

ngOnInit() {
this.getFoods();
this.getBooksAndMovies();
}

getFoods() {
this._searchService.getFoods().subscribe(
// the first argument is a function which runs on success
data => { this.foods = data},
// the second argument is a function which runs on error
err => console.error(err),
// the third argument is a function which runs on completion
() => console.log('done loading foods')
);
}

getBooksAndMovies() {
this._searchService.getBooksAndMovies().subscribe(
data => {
this.books = data[0]
this.movies = data[1]
}
// No error or completion callbacks here. They are optional, but
// you will get console errors if the Observable is in an error state.
);
}

/*createFood(name) {
let food = {name: name};
this._searchService.createFood(food).subscribe(
data => {
// refresh the list
this.getFoods();
return true;
},
error => {
console.error("Error saving food!");
return Observable.throw(error);
}
);
}

updateFood(food) {
this._searchService.updateFood(food).subscribe(
data => {
// refresh the list
this.getFoods();
return true;
},
error => {
console.error("Error saving food!");
return Observable.throw(error);
}
);
}

deleteFood(food) {
if (confirm("Are you sure you want to delete " + food.name + "?")) {
this._searchService.deleteFood(food).subscribe(
data => {
// refresh the list
this.getFoods();
return true;
},
error => {
console.error("Error deleting food!");
return Observable.throw(error);
}
);
}
}*/

/*
@Output() addVehicle = new EventEmitter();
// Set our default values
localState = { value: '' };
vehicles= [
{
id: 0,
name: 'TEST'
}
];
// TypeScript public modifiers
constructor(public appState: AppState, private homeService: HomeService) {

// this.appState.set('value', value);
}

add(_event) {
console.log('adding ', _event);
this.addVehicle.emit({
value: _event
});
}

constructor(private _dataService: DataService) { }

ngOnInit() {
this.getAllItems();
}

private getAllItems(): void {
this._dataService
.GetAll()
.subscribe((data:MyTypedItem[]) => this.myItems = data,
error => console.log(error),
() => console.log('Get all Items complete'));
}

ngOnInit() {
console.log('hello `Home` component');
}
*/
}


movies.json:

[
{ "title": "Ghostbusters" },
{ "title": "Star Wars" },
{ "title": "Batman Begins" },
{ "title": "Bourne Identity" },
{ "title": "Bourne Identity 2" }
]


food.json:

[
{ "id": 1, "name": "Donuts" },
{ "id": 2, "name": "Pizza" },
{ "id": 3, "name": "Sushi" }
]


books.json:

[
{ "title": "Hitchhiker's Guide to the Galaxy" },
{ "title": "The Fellowship of the Ring" },
{ "title": "Moby Dick" }
]


index.ts:

export * from './search.component';


I have absolutely no other guess why this is failing than a indefinite function, but I cannot figure out the mistake. Therefore, any hints and help would be very much appreciated! Thanks!

Answer

From your search.template.html, remove <search> tags. Because of your search tag in html template, it is going recursive. Hence, you are receiving Maximum call stack size exceeded error.

Keep template like this-

  <h1>Angular2 HTTP Demo App</h1>
  <h2>Foods</h2>
  <ul>
    <li *ngFor="let food of foods"><input type="text" name="food-name" [(ngModel)]="food.name"><button (click)="updateFood(food)">Save</button> <button (click)="deleteFood(food)">Delete</button></li>
  </ul>
  <p>Create a new food: <input type="text" name="food_name" [(ngModel)]="food_name"><button (click)="createFood(food_name)">Save</button></p>
  <h2>Books and Movies</h2>
  <h3>Books</h3>
  <ul>
    <li *ngFor="let book of books">{{book.title}}</li>
  </ul>
  <h3>Movies</h3>
  <ul>
    <li *ngFor="let movie of movies">{{movie.title}}</li>
  </ul>

See if this helps.

Comments