Skip to content

Commit

Permalink
feat: blog posts listing and post viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonroberts committed Mar 9, 2019
1 parent b4037eb commit 1d0dc63
Show file tree
Hide file tree
Showing 19 changed files with 497 additions and 83 deletions.
13 changes: 9 additions & 4 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,18 @@
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
"src/assets",
"src/content"
],
"styles": [

"src/styles.scss"
"src/styles.scss",
"node_modules/prismjs/themes/prism-okaidia.css"
],
"scripts": [
"node_modules/marked/lib/marked.js",
"node_modules/prismjs/prism.js",
"node_modules/prismjs/components/prism-typescript.min.js"
],
"scripts": [],
"es5BrowserSupport": true
},
"configurations": {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@angular/router": "~7.2.0-rc.0",
"core-js": "^2.5.4",
"hammerjs": "^2.0.8",
"ngx-markdown": "^7.1.4",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
Expand All @@ -37,4 +38,4 @@
"tslint": "~5.11.0",
"typescript": "~3.2.2"
}
}
}
25 changes: 12 additions & 13 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { MarkdownModule } from 'ngx-markdown';

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { LayoutComponent } from './core/layout/layout.component';
import { LayoutModule } from '@angular/cdk/layout';
import { MatToolbarModule, MatButtonModule, MatSidenavModule, MatIconModule, MatListModule } from '@angular/material';
import { CoreModule } from './core/core.module';

@NgModule({
declarations: [
AppComponent,
LayoutComponent
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
RouterModule.forRoot([

{ path: '', redirectTo: '/blog', pathMatch: 'full' },
{ path: 'blog', loadChildren: './blog/blog.module#BlogModule' }
]),
LayoutModule,
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatIconModule,
MatListModule,
MarkdownModule.forRoot({
loader: HttpClient
}),
CoreModule
],
bootstrap: [AppComponent]
})
Expand Down
25 changes: 25 additions & 0 deletions src/app/blog/blog.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { BlogComponent } from './blog/blog.component';
import { PostComponent } from './post/post.component';
import { MarkdownModule } from 'ngx-markdown';
import { MatListModule } from '@angular/material';

@NgModule({
declarations: [
BlogComponent,
PostComponent
],
imports: [
CommonModule,
RouterModule.forChild([
{ path: 'posts/:postId', component: PostComponent },
{ path: '', component: BlogComponent }
]),
MarkdownModule.forChild(),
MatListModule
]
})
export class BlogModule { }
32 changes: 32 additions & 0 deletions src/app/blog/blog/blog.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Component, OnInit } from '@angular/core';
import { PostService } from 'src/app/core/services';
import { Observable } from 'rxjs';
import { Post } from 'src/app/core/models';
import { tap } from 'rxjs/operators';

@Component({
selector: 'app-blog',
template: `
<mat-list>
<mat-list-item *ngFor="let post of posts$ | async">
<h3 mat-line>
<a [routerLink]="['/blog/posts', post.id]">{{post.title}}</a>
</h3>
<p mat-line> {{post.dateCreated | date}} </p>
</mat-list-item>
</mat-list>
`,
styles: []
})
export class BlogComponent implements OnInit {
posts$: Observable<Post[]>;

constructor(private postServce: PostService) {
this.posts$ = this.postServce.getPosts();
}

ngOnInit() {
}

}
29 changes: 29 additions & 0 deletions src/app/blog/post/post.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { catchError, tap, concatMap, switchMap } from 'rxjs/operators';
import { EMPTY } from 'rxjs';

import { PostService } from 'src/app/core/services';

@Component({
selector: 'app-post',
template: `
<markdown [data]="post$ | async"></markdown>
`,
styles: []
})
export class PostComponent implements OnInit {
post$ = this.route.paramMap.pipe(
switchMap(params =>
this.postService.getPost(params.get('postId')))
);

constructor(
private route: ActivatedRoute,
private postService: PostService
) { }

ngOnInit() {
}

}
1 change: 1 addition & 0 deletions src/app/core/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './layout/layout.component';
63 changes: 63 additions & 0 deletions src/app/core/components/layout/layout.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Component } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Component({
selector: 'app-layout',
template: `
<mat-sidenav-container class="sidenav-container">
<mat-sidenav #drawer class="sidenav" fixedInViewport="true"
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
[mode]="(isHandset$ | async) ? 'over' : 'over'">
<mat-toolbar>Menu</mat-toolbar>
<mat-nav-list>
<a mat-list-item routerLink="/">Posts</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<button
type="button"
aria-label="Toggle sidenav"
mat-icon-button
(click)="drawer.toggle()"
*ngIf="isHandset$ | async">
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
</button>
<a routerLink="/">Brandon Roberts</a>
</mat-toolbar>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
`,
styles: [`
.sidenav-container {
height: 100%;
}
.sidenav {
width: 200px;
}
.sidenav .mat-toolbar {
background: inherit;
}
.mat-toolbar.mat-primary {
position: sticky;
top: 0;
z-index: 1;
}
`]
})
export class LayoutComponent {

isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches)
);

constructor(private breakpointObserver: BreakpointObserver) {}

}
27 changes: 27 additions & 0 deletions src/app/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LayoutModule } from '@angular/cdk/layout';
import { MatToolbarModule, MatButtonModule, MatSidenavModule, MatIconModule, MatListModule } from '@angular/material';
import { RouterModule } from '@angular/router';

import { LayoutComponent } from './components';

@NgModule({
declarations: [
LayoutComponent
],
imports: [
CommonModule,
RouterModule,
LayoutModule,
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatIconModule,
MatListModule,
],
exports: [
LayoutComponent
]
})
export class CoreModule { }
17 changes: 0 additions & 17 deletions src/app/core/layout/layout.component.css

This file was deleted.

28 changes: 0 additions & 28 deletions src/app/core/layout/layout.component.html

This file was deleted.

20 changes: 0 additions & 20 deletions src/app/core/layout/layout.component.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/app/core/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './post.model';
6 changes: 6 additions & 0 deletions src/app/core/models/post.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Post {
id: string;
title: string;
dateCreated: string;
lastUpdated: string;
}
1 change: 1 addition & 0 deletions src/app/core/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './post.service';
22 changes: 22 additions & 0 deletions src/app/core/services/post.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

import { Post } from '../models';

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

constructor(private http: HttpClient) { }

getPost(postId: string) {
return this.http.get(`/content/posts/${postId}.md`, { responseType: 'text' });
}

getPosts() {
return this.http.get<{ posts: Post[] }>(`/content/posts.json`)
.pipe(map(data => data.posts));
}
}
14 changes: 14 additions & 0 deletions src/content/posts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"posts": [
{
"id": "2019-03-04-handling-error-states-with-ngrx",
"title": "Handling Error States with NgRx",
"excerpt": "When building applications with NgRx, one thing you have to be aware of is how to handle error states.",
"dateCreated": "1551657600000",
"lastUpdated": "1551657600000",
"tags": [
"my first blog post"
]
}
]
}

0 comments on commit 1d0dc63

Please sign in to comment.