1
votes

I have an angular application that has a page with cabins and when you click on a cabin its supposed to take you to the cabin detail page. This works fine when I tested with a json-server database but when I created and hooked it up to my express server I get an error when trying to navigate to my cabindetail page.

I am new to angular and nodejs so I am a bit lost with this.

this is the error.

ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'cabindetail' Error: Cannot match any routes. URL Segment: 'cabindetail' at ApplyRedirects.push../node_modules/@angular/router/fesm5/router.js.ApplyRedirects.noMatchError (router.js:2469) at CatchSubscriber.selector (router.js:2450) at CatchSubscriber.push../node_modules/rxjs/_esm5/internal/operators/catchError.js.CatchSubscriber.error (catchError.js:34) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._error (Subscriber.js:79) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.error (Subscriber.js:59) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._error (Subscriber.js:79) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.error (Subscriber.js:59) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._error (Subscriber.js:79) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.error (Subscriber.js:59) at TapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/tap.js.TapSubscriber._error (tap.js:61) at resolvePromise (zone.js:831) at resolvePromise (zone.js:788) at zone.js:892 at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423) at Object.onInvokeTask (core.js:17290) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195) at drainMicroTaskQueue (zone.js:601) at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:502) at invokeTask (zone.js:1744) defaultErrorLogger @ core.js:15724

this is my cabinRouter.js

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const authenticate = require('../authenticate');
const cors = require('./cors');
const Cabins = require('../models/cabins');

const cabinRouter = express.Router();

cabinRouter.use(bodyParser.json());


cabinRouter.route('/')
.options(cors.corsWithOptions, (req,res) => {res.sendStatus(200); })
.get(cors.cors, (req, res, next) => {
    Cabins.find(req.query)
    .populate('comments.author')
    .then((cabin) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.json(cabin);
    }, (err) => next(err))
    .catch((err) => next(err));
})
.post(cors.corsWithOptions, /*authenticate.verifyUser, authenticate.verifyAdmin,*/ (req, res, next) => {
    Cabins.create(req.body)
    .then((cabin) => {
        console.log('Cabin Created', cabin);
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.json(cabin);
    }, (err) => next(err))
    .catch((err) => next(err));
})
.put(cors.corsWithOptions, authenticate.verifyUser,authenticate.verifyAdmin, (req, res, next) => {
    res.statusCode = 403;
    res.end('PUT operation not supported on /cabins');
})
.delete(cors.corsWithOptions, /*authenticate.verifyUser, authenticate.verifyAdmin,*/ (req, res, next) => {
    Cabins.remove({})
    .then((resp) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.json(resp);
    }, (err) => next(err))
    .catch((err) => next(err));
});

cabinRouter.route('/:cabinId')
.options(cors.corsWithOptions, (req,res) => {res.sendStatus(200); })
.get(cors.cors, (req, res, next) => {
    Cabins.findById(req.params.cabinId)
    .populate('comments.author')
    .then((cabin) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.json(cabin);
    }, (err) => next(err))
    .catch((err) => next(err));
})
.post(cors.corsWithOptions,/*authenticate.verifyUser, authenticate.verifyAdmin,*/ (req, res, next) => {
    res.statusCode = 403;
    res.end('POST operation not supported on /cabins/' + req.params.cabinId);
})
.put(cors.corsWithOptions, /*authenticate.verifyUser, authenticate.verifyAdmin,*/ (req, res, next) => {
    Cabins.findByIdAndUpdate(req.params.cabinId, {
        $set: req.body
    }, {new: true})
    .then((cabin) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.json(cabin);
    }, (err) => next(err))
    .catch((err) => next(err));
})
.delete(cors.corsWithOptions, /*authenticate.verifyUser, authenticate.verifyAdmin,*/ (req, res, next) => {
    Cabins.findByIdAndRemove(req.params.cabinId)
    .then((resp) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'application/json');
        res.json(resp);
    }, (err) => next(err))
    .catch((err) => next(err));
});


module.exports = cabinRouter;

this is my app-routing.module.ts

import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from '../home/home.component';
import { CabinsComponent } from '../cabins/cabins.component';
import { HousesComponent } from '../houses/houses.component';
import { EcoactivitiesComponent } from '../ecoactivities/ecoactivities.component';
import { ContactComponent } from '../contact/contact.component';
import { CabinDetailComponent } from '../cabin-detail/cabin-detail.component';
import { HouseDetailComponent } from '../house-detail/house-detail.component';


const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  { path: 'cabin', component: CabinsComponent },
  { path: 'house', component: HousesComponent }, 
  { path: 'cabindetail/:id', component: CabinDetailComponent },
  { path: 'housedetail/:id', component: HouseDetailComponent },
  { path: 'ecoactivity', component: EcoactivitiesComponent },
  { path: 'contact', component: ContactComponent },
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}

these are the bitbucket links to my full projects. Angular

https://bitbucket.org/natashanodine/vilcabambahotel-angular/src/master/

Node express server

https://bitbucket.org/natashanodine/vilcabamba-hotel-server/src/master/

this is my cabin service

import { Injectable } from '@angular/core';

import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Observable, of } from 'rxjs';
import { catchError, map, tap, flatMap } from 'rxjs/operators';

import { Cabin } from '../shared/cabin';
import { Comment } from '../shared/comment';
import { MessageService } from './message.service';



const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({
  providedIn: 'root'
})
export class CabinService {

  private cabinsUrl = 'http://localhost:3000/cabins';  // URL to web api

  constructor(
    private http: HttpClient,
    private messageService: MessageService) { }

  /** GET cabins from the server */
  getCabins(): Observable<Cabin[]> {
    return this.http.get<Cabin[]>(this.cabinsUrl)
      .pipe(
        tap(cabins => this.log('fetched cabins')),
        catchError(this.handleError('getCabins', []))
      );
  }

  getFeaturedCabin(): Observable<Cabin[]> {
    const url = 'http://localhost:3000/cabins?featured=true';
    return this.http.get<Cabin[]>(url).pipe(
      tap(_ => this.log('o')),
      catchError(this.handleError<Cabin[]>(`getFeaturedCabin`))
    );
  }
  /** GET cabin by id. Return `undefined` when id not found */
  getCabinNo404<Data>(id: string): Observable<Cabin> {
    const url = `${this.cabinsUrl}/?id=${id}`;
    return this.http.get<Cabin[]>(url)
      .pipe(
        map(cabins => cabins[0]), // returns a {0|1} element array
        tap(h => {
          const outcome = h ? `fetched` : `did not find`;
          this.log(`${outcome} cabin id=${id}`);
        }),
        catchError(this.handleError<Cabin>(`getCabin id=${id}`))
      );
  }

  /** GET cabin by id. Will 404 if id not found */
  getCabin(id: string): Observable<Cabin> {
    const url = `${this.cabinsUrl}/${id}`;
    return this.http.get<Cabin>(url).pipe(
      tap(_ => this.log(`fetched cabin id=${id}`)),
      catchError(this.handleError<Cabin>(`getCabin id=${id}`))
    );
  }



updatePosts(id, newcomment) {
    const comment: Comment = newcomment;
    return this.http.get<Cabin>('http://localhost:3000/cabins/' + id).pipe(
      map(cabin => {


        return {
          id: cabin._id,
          name: cabin.name,
          image: cabin.image,
          description: cabin.description,
          priceweek: cabin.priceweek,
          pricemonth: cabin.pricemonth,
          featured: cabin.featured,
          comments: cabin.comments


        };


      }),
      flatMap((updatedCabin) => {
        updatedCabin.comments.push(comment);
        return this.http.put(this.cabinsUrl + '/' + id, updatedCabin);
      })
    );

  }





   /**
    * Handle Http operation that failed.
    * Let the app continue.
    * @param operation - name of the operation that failed
    * @param result - optional value to return as the observable result
    */
  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }

  /** Log a CabinService message with the MessageService */
  private log(message: string) {
    this.messageService.add(`CabinService: ${message}`);
  }

}

My cabin-detail.component

import { Location } from '@angular/common';
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Params, ActivatedRoute } from '@angular/router';


import { Comment } from '../shared/comment';
import { Cabin } from '../shared/cabin';
import { CabinService } from '../services/cabin.service';

@Component({
  selector: 'app-cabin-detail',
  templateUrl: './cabin-detail.component.html',
  styleUrls: ['./cabin-detail.component.css']
})
export class CabinDetailComponent implements OnInit {
   cabin: Cabin;
   cabins: Cabin[];
  comment: Comment;
  commentForm: FormGroup;
  errMess: string;


  formErrors = {
    'author' : '',
    'rating' : '',
    'comment' : ''
  };

  validationMessages = {
    'author' : {
      'required' : 'Name is required',
      'minlength' : 'Name must be at least 2 characters long',
      'maxlength' : 'Name cannot be more that 25 characters long'
    }
  };


  constructor(
    private cabinService: CabinService,
     private fb: FormBuilder,
    private location: Location,
    private route: ActivatedRoute,
    @Inject("BaseURL") private BaseURL
  ) {
    this.createForm();
  }

  ngOnInit(): void {
    this.getCabin();
    this.getCabins();


  }

  getCabin(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    this.cabinService.getCabin(id)
      .subscribe(cabin => this.cabin = cabin);
  }


  getCabins(): void {
    this.cabinService.getCabins()
    .subscribe(cabins => this.cabins = cabins);
  }

/* addComment(description: string): void {
    description = description.trim();
    if (!description) { return; }
    this.cabinService.addCabin({ description } as Cabin)
      .subscribe(cabin => {
        this.cabins.push(cabin);
      });
  }
 */
 /* delete(cabin: Cabin): void {
    this.cabins = this.cabins.filter(h => h !== cabin);
    this.cabinService.deleteCabin(cabin).subscribe();
  }
  */

    createForm() {
    this.commentForm = this.fb.group({
      author: ['', [ Validators.required, Validators.minLength(2) ] ],
      rating: 5,
      comment: ['', [ Validators.required ] ],
    });

    this.commentForm.valueChanges
      .subscribe(data => this.onValueChanged(data));

    this.onValueChanged(); // (re)set form validation messages
  }

    onValueChanged(commentFormData?: any) {
    if (!this.commentForm) {
      return;
    }
    const form = this.commentForm;
    for (const field in this.formErrors) {
      this.formErrors[field] = '';
      const control = form.get(field);
      if (control && control.dirty && !control.valid) {
        const messages = this.validationMessages[field];
        for (const key in control.errors) {
          this.formErrors[field] += messages[key] + ' ';
        }
      }
    }

    if (this.commentForm.valid) {
      this.comment = this.commentForm.value;
    } else {
      this.comment = undefined;
    }
  }

  onSubmit() {
      const id = +this.route.snapshot.paramMap.get('id');
          this.comment['date'] = new Date().toISOString();

    this.cabin.comments.push(this.comment);
    this.cabinService.updatePosts(this.cabin._id, this.comment).subscribe(() => {
    console.log("PUT is done");
})

    this.commentForm.reset({
        author: '',
        rating: 5,
        comment: ''
    });
  }


}
1
Make sure you are passing id with cabindetail in the url. Because this error occurs when you try to go to the route which is not set in the routing moduleJohar Zaman
The router link I have that takes you from cabins to cabindetail is this: routerLink="/cabindetail/{{cabin.id}} witch would pass the id to the urlNaNodine
I don't know anything about routes. But, I wonder if it would help to remove that training comma inside your Routes array.rickz
I meant to type trailing comma.rickz

1 Answers

0
votes

As i can see, the Angular threw a router error, that cabindetail is not found in the routing table.

When i checked the code you've provided i found out that the router table expects a parameter :id in the route, so when you were connected to the JSON server (and the data was perfectly fine, the Id in the model was populated and the route occurred as cabindetail/5 for example.

What seems to have happened, that when you connected your express server, the id property is not being populated in the model, which makes the route /cabindetail/ and it is invalid as per the routing table (because the Id would be undefined and not 0).

What you need to do, is to check the JSON coming from the server and ensure that the Id is properly populated.