0

Basically, what I want is to have human-readable route names instead of the long mongooseid in the url.

eg: Instead of: https://localhost/campgrounds/50341373e894ad16347efe01

I want: https://localhost/campgrounds/mountain-view

where, "mountain-view" would be the name of the campground the user would enter in the form field.

This is my interface:

campgrounds.model.ts

export interface Campground {
_id: String;
name: String;
routeURL: String;
description: String;
cost: Number;
}

campground-create.component.html (I'm not posting the other form fields as it's not necessary here)

<div class="container">
 <form>
  <div class="form-group">
  <label for="name">Name</label>
  <input type="text" class="form-control" id="name" name="name"
  [(ngModel)]="campground.name" (blur)="generateURL()" required>
  </div>

  <button type="submit" class="btn btn-primary" 
  (click)="addCampground()">Submit</button>
 </form>
</div>

campground-create.component.ts

This just converts the name of the campground that the user has entered in the form field and adds a hyphen between the words when (blur) is called.

import { Component, OnInit } from '@angular/core';
import { CampgroundService } from 'src/app/campground.service';
import { Router } from '@angular/router';
import { Campground } from 'src/app/campground.model';

@Component({
selector: 'app-campground-create',
templateUrl: './campground-create.component.html',
styleUrls: ['./campground-create.component.sass']
})

export class CampgroundCreateComponent implements OnInit {

  campground: Campground = {} as Campground;

  constructor(private campgroundService: CampgroundService, private 
  router: Router) { }

  ngOnInit() {
  }

  generateURL() {
  this.campground.routeURL = this.campground.name.toLowerCase()
  .trim().split(/\s+/).join('-');
  // Retrieve campground name and add hyphens between words
  }

  addCampground() {
  this.campgroundService.addCampground(this.campground)
  .subscribe(()=> {
   this.generateURL();
   this.router.navigate(['/campground-list']);
  });
 }
}

campground-list.component.html

<div class="background">

 <header class="jumbotron jumbotron-fluid">
 <div class="container">
  <h1 class="display-4 title">Welcome to YelpCamp!</h1>
  <button type="button" [routerLink]="['/campground-create']" 
  class="btn btn-default btn-lg">Add New Campground</button>
 </div>
 </header>

 <div class="card-deck">
  <div class="card" *ngFor="let campground of campgrounds">
  <img class="card-img-top" src="{{campground?.imageURL}}" alt="Card 
  image cap">
   <div class="card-body">
    <h5 class="card-title">{{campground?.name}}</h5>
    <button type="button" routerLink="/campground- 
    details/{{campground.routeURL}}"
    class="btn btn-danger">More Info</button>
   </div>
  </div>
 </div>

</div>

However, the data from the service is not returned back for the campground details page, because I use "findById".

campground-routes.js

router.get('/api/campground/:id', (req, res) => {
  Campground.findById(req.params.id, (err, campground) => {
  if (err) {
  console.log(err);
  } else {
   res.json(campground);
  }
 });
});

if I had the mongoose id in the route, the below would've worked:

campground-details.component.ts

import { Component, OnInit } from '@angular/core';
import { CampgroundService } from 'src/app/campground.service';
import { ActivatedRoute } from '@angular/router';
import { Campground } from 'src/app/campground.model';

@Component({
selector: 'app-campground-details',
templateUrl: './campground-details.component.html',
styleUrls: ['./campground-details.component.sass']
})

export class CampgroundDetailsComponent implements OnInit {

  campground: Campground;

  constructor(private campgroundService: CampgroundService, private 
  router: ActivatedRoute) {
  }

  ngOnInit() {
   this.router.params.subscribe(params => {
   const id = params['id'];

   this.campgroundService.getCampground(id).subscribe(campground=> {
   this.campground = campground;
   console.log(this.campground);
   });
  });
 }
}

Is there a way to do this? If yes, is there an easy way to do it? Thanks.

Vishwanath B
  • 89
  • 2
  • 11
  • I've not seen any _easy_ way to do this. Are you sure that you couldn't find the Campground by it's name? I'm not too familiar with Mongoose but I know it will let you define methods -- I'd think a `findByName` method would be very useful here https://stackoverflow.com/questions/7419969/how-do-i-define-methods-in-a-mongoose-model/7448424#7448424 – Nathan Beck Jan 30 '19 at 18:58
  • Instead of that, I was wondering if I could pass the 'id' as a query param, and then hide the id, while keeping the name. For instance: https://localhost:4200/mountain-view?id=50341373e894ad16347efe01 (and the query param would be hidden). Not sure how I could hide only the id in the url. – Vishwanath B Jan 30 '19 at 19:23
  • Interesting idea. You'd need a way to read and then remove the queryParam without triggering a new navigation event though. The only other option I'd consider is saving an array of objects like `{ campground_name: campground_id }` in a shared service. That way you could route to `/api/campground/:name`, use the `name` to filter the right campground from the shared service and then use that campground's `id` in `getCampground()`. Lots of overhead, but it may work. – Nathan Beck Jan 30 '19 at 19:33

1 Answers1

1

Are you using the current version of Angular? As of Angular v7.2 there is a state property that you can send on the route.

You could then send the name in the URL and the id in the state property.

See this link for more information on using the state feature of the route: https://netbasal.com/set-state-object-when-navigating-in-angular-7-2-b87c5b977bb

Something like this:

<button type="button" 
        routerLink="/campground-details/{{campground.name }}"
        [state]="{ id: campground.id }"
        class="btn btn-danger">
  More Info
</button>
DeborahK
  • 57,520
  • 12
  • 104
  • 129