Search code examples
node.jsmongodbmongooseangular8mongoose-schema

Populating a tabbed table


Okay, so I'm new to Angular, just under a week or so and I managed to get most of this app built thanks to dev docs.

I am trying to populate a tab table for job openings where clicking the title changes the description accordingly (such as here https://www.w3schools.com/w3css/tryit.asp?filename=tryw3css_tabulators_animate). I got the tabs to function just fine with static code, but now trying to pull from the mongodb, it only shows one of the entries in the "posts" collection, though the entire array of objects does come up in the console.

I can't seem to figure out what I'm doing wrong.

my home.component.html:

    <div class="content" *ngFor="let post of posts">
      <div class="row">
        <div class="container">
          <div class="col-md-12 open">
            <h3>Open Positions:</h3>
          </div>
        </div>
      </div>
      <div class="example-loading-shade" *ngIf="isLoadingResults">
        <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
      </div>
      <div class="row" [routerLink]="['/post']" >
        <div class="col-md-3" id="jobNames" data="this.posts">
          <div class="w3-bar w3-black">
            <button class="w3-bar-item w3-button tablink" onclick='openLink(event, post._id)'>{{post.postTitle}}</button>
          </div>
        </div>
        <div class="col-md-9" id="jobContainer">
          <div class="job w3-container w3-animate-opacity" id="post._id">
            <h3 class="jobSections">Date Posted: {{post.updated | date: 'dd MMM yyyy'}}</h3>
            <p [innerHTML]="post.postContent"></p>
          </div>
        </div>
      </div>

Post.js:

    var mongoose = require('mongoose'), Schema = mongoose.Schema;

var PostSchema = new mongoose.Schema({
  category : { type: Schema.Types.ObjectId, ref: 'Category' },
  id: String,
  postTitle: String,
  postAuthor: String,
  postContent: String,
  postReference: String,
  created: { type: Date },
  updated: { type: Date, default: Date.now },
});

module.exports = mongoose.model('Post', PostSchema);

home.component.ts

import { Component, OnInit } from '@angular/core';
import { Post } from '../post/post';
import { PostService } from '../post.service';
import { HomeService } from '../home.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  post: Post = {
    category: '',
    id: '',
    postTitle: '',
    postAuthor: '',
    postContent: '',
    postReference: '',
    created: null,
    updated: null
  };
  posts: Post[] = [];
  isLoadingResults = true;

  constructor(private api: HomeService) { }

  ngOnInit() {
    this.api.getPosts()
      .subscribe((res: any) => {
        this.posts = res;
        console.log(this.posts);
        this.isLoadingResults = false;
      }, err => {
        console.log(err);
        this.isLoadingResults = false;
      });
  }

}

jobs.js

function openLink(evt, animName) {
  var i, x, tablinks;
  x = document.getElementsByClassName("job");
  for (i = 0; i < x.length; i++) {
    x[i].style.display = "none";
  }
  tablinks = document.getElementsByClassName("tablink");
  for (i = 0; i < x.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" w3-red", "");
  }
  document.getElementById(animName).style.display = "block";
  evt.currentTarget.className += " w3-red";
}

home.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { Category } from './category/category';
import { Post } from './post/post';

const apiUrl = 'http://localhost:3000/api/public/';

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

  constructor(private http: HttpClient) { }

  getCategories(): Observable<Category[]> {
    return this.http.get<Category[]>(apiUrl + 'category')
      .pipe(
        tap(_ => this.log('fetched Categories')),
        catchError(this.handleError('getCategories', []))
      );
  }

  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>(apiUrl + 'post')
      .pipe(
        tap(_ => this.log('fetched Posts')),
        catchError(this.handleError('getPosts', []))
      );
  }

  getPostsByCategory(id: any): Observable<Post[]> {
    return this.http.get<Post[]>(apiUrl + 'bycategory/' + id)
      .pipe(
        tap(_ => this.log('fetched Posts')),
        catchError(this.handleError('getPosts', []))
      );
  }

  getPost(id: any): Observable<Post> {
    return this.http.get<Post>(apiUrl + 'post/' + id).pipe(
      tap(_ => console.log(`fetched post by id=${id}`)),
      catchError(this.handleError<Post>(`getPost id=${id}`))
    );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      console.error(error); // log to console instead
      this.log(`${operation} failed: ${error.message}`);

      return of(result as T);
    };
  }

  private log(message: string) {
    console.log(message);
  }
}

Solution

  • I got it, and in case anyone else is looking for something similar

    home.component.ts :

    import { Component, OnInit } from '@angular/core';
    import { Post } from '../post/post';
    import { PostService } from '../post.service';
    import { HomeService } from '../home.service';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.scss']
    })
    export class HomeComponent implements OnInit {
      post: Post = {
        category: '',
        id: '',
        postTitle: '',
        postAuthor: '',
        postContent: '',
        postReference: '',
        created: null,
        updated: null
      };
      posts: Post[] = [];
      isLoadingResults = true;
      selectedPost: Post = null;
    
      constructor(private api: HomeService) { }
    
      openLink(evt, animName) {
        var i, x, tablinks;
        x = document.getElementsByClassName("job");
        for (i = 0; i < x.length; i++) {
          x[i].style.display = "none";
        }
        tablinks = document.getElementsByClassName("tablink");
        for (i = 0; i < x.length; i++) {
          tablinks[i].className = tablinks[i].className.replace(" w3-red", "");
        }
        document.getElementById(animName).style.display = "block";
        evt.currentTarget.className += " w3-red";
      }
    
      selectPost(post) {
        this.selectedPost = post;
        console.log(this.selectedPost);
      }
    
      ngOnInit() {
        this.api.getPosts()
          .subscribe((res: any) => {
            this.posts = res;
            console.log(this.posts);
            this.isLoadingResults = false;
          }, err => {
            console.log(err);
            this.isLoadingResults = false;
          }
        );
      }
    
    }
    

    home.component.html

    <div class="col-md-12 open">
            <h3>Open Positions:</h3>
          </div>
          <div class="example-loading-shade" *ngIf="isLoadingResults">
            <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
          </div>
          <div class="row">
            <div class="col-md-2" id="jobNames">
              <div class="w3-bar w3-black">
                <button class="w3-bar-item w3-button tablink" *ngFor="let post of posts"
                  (click)="selectPost(post)">{{post.postTitle}}</button>
              </div>
            </div>
            <div class="col-md-10" id="jobContainer">
              <div class="job w3-container w3-animate-opacity" id="{{selectedPost.postTitle}}">
                <h3 class="jobSections">Date Posted: {{selectedPost.updated | date: 'dd MMM yyyy'}}</h3>
                <p innerHTML={{selectedPost.postContent}}></p>
              </div>
            </div>
          </div>