From ec9789a13c9a22cdf93fcce6a61cd8e3724b763b Mon Sep 17 00:00:00 2001 From: pastorsj Date: Sun, 1 Oct 2017 15:41:14 -0400 Subject: [PATCH] Finished tagging feature --- src/app/_services/editor.service.ts | 38 ++++++++++++++- src/app/editor/editor.component.html | 19 +++++++- src/app/editor/editor.component.scss | 21 +++++++++ src/app/editor/editor.component.ts | 69 ++++++++++++++++++++++++++-- src/app/material.module.ts | 4 +- 5 files changed, 143 insertions(+), 8 deletions(-) diff --git a/src/app/_services/editor.service.ts b/src/app/_services/editor.service.ts index 2dc8baf..bcb2345 100644 --- a/src/app/_services/editor.service.ts +++ b/src/app/_services/editor.service.ts @@ -15,6 +15,7 @@ export class EditorService { private editorUrl = environment.URL + '/blog/'; private gistUrl = environment.URL + '/gist/'; + private tagUrl = environment.URL + '/tags/' private title = ''; private description = ''; private id: string; @@ -57,7 +58,7 @@ export class EditorService { .catch(this.handleError); } - saveArticle(edits: string): Observable { + saveArticle(edits: string, tags: string[]): Observable { const headers = new Headers(); headers.append('Content-Type', 'application/json'); headers.append('Authorization', 'Bearer ' + this.auth.token); @@ -68,6 +69,7 @@ export class EditorService { text: edits, title: this.title, description: this.description, + tags, author: author.username }; @@ -108,6 +110,40 @@ export class EditorService { .catch(this.handleError); } + getTags(text: string): Observable { + if (!text) { + return Observable.of([]); + } + const headers = new Headers(); + headers.append('Content-Type', 'application/json'); + headers.append('Authorization', 'Bearer ' + this.auth.token); + + const options = new RequestOptions({ headers }); + + const body = { + prefix: text, + count: 50 + } + + return this.http.put(this.tagUrl, body, options) + .map(this.extractData) + .catch(this.handleError); + } + + addTag(tag: string): Observable { + const headers = new Headers(); + headers.append('Content-Type', 'application/json'); + headers.append('Authorization', 'Bearer ' + this.auth.token); + + const options = new RequestOptions({ headers }); + + const body = { + tag + } + + return this.http.post(this.tagUrl, body, options); + } + private extractData(res: Response) { const body = res.json(); return body.data || { }; diff --git a/src/app/editor/editor.component.html b/src/app/editor/editor.component.html index 4a918b2..b768508 100644 --- a/src/app/editor/editor.component.html +++ b/src/app/editor/editor.component.html @@ -1,14 +1,31 @@
-
+ Article Title is required + Article Description is required + + + + {{ tag }} + cancel + + + + + + + + {{ tag }} + + +
diff --git a/src/app/editor/editor.component.scss b/src/app/editor/editor.component.scss index cc703fd..9c20c25 100644 --- a/src/app/editor/editor.component.scss +++ b/src/app/editor/editor.component.scss @@ -24,4 +24,25 @@ .editor-form { margin-left: 1vw; margin-right: 1vw; +} + +.vertical-spacer { + width: 100%; + display: block; + clear: all; + padding-top: 2em; +} + +.vertical-half-spacer { + width: 100%; + display: block; + clear: all; + padding-top: 1em; +} + +.vertical-quarter-spacer { + width: 100%; + display: block; + clear: all; + padding-top: 0.5em; } \ No newline at end of file diff --git a/src/app/editor/editor.component.ts b/src/app/editor/editor.component.ts index 1e123d9..fb01582 100644 --- a/src/app/editor/editor.component.ts +++ b/src/app/editor/editor.component.ts @@ -3,6 +3,8 @@ import { Router, ActivatedRoute } from '@angular/router'; import { FormBuilder, FormGroup, FormControl, Validators, FormsModule } from '@angular/forms'; import { MdSnackBar } from '@angular/material'; +import { Observable } from 'rxjs/Observable'; + import { EditorService } from '../_services/editor.service'; import { ArticleService } from '../_services/article.service'; import { ImagesService } from '../_services/images.service'; @@ -55,6 +57,10 @@ export class EditorComponent implements OnInit { public editing = false; public formGroup: FormGroup; public initControls: any; + public filteredTags: Observable; + public selectedTags: Set; + public tagInput: string; + public removable = true; constructor( private editorService: EditorService, @@ -67,7 +73,8 @@ export class EditorComponent implements OnInit { ) { this.formGroup = this.fb.group({ 'articleTitle': new FormControl('', Validators.required), - 'articleDescription': new FormControl('', Validators.required) + 'articleDescription': new FormControl('', Validators.required), + 'tags': new FormControl('') }); } @@ -83,6 +90,12 @@ export class EditorComponent implements OnInit { } ngOnInit() { + this.selectedTags = new Set(); + this.formGroup.get('tags').valueChanges + .subscribe((input) => { + this.filteredTags = this.filterTags(input); + }); + initializeFroalaGistPlugin(this.editorService); this.editing = true; this.route.params.subscribe(params => { @@ -94,10 +107,14 @@ export class EditorComponent implements OnInit { .subscribe(article => { this.formGroup.setValue({ 'articleTitle': article.title, - 'articleDescription': article.description + 'articleDescription': article.description, + 'tags': '' }); + if (article.tags instanceof Array) { + this.selectedTags = new Set(article.tags); + } this.editorContent = article.text; - }) + }); }); } @@ -109,11 +126,12 @@ export class EditorComponent implements OnInit { if (isFormValid) { const articleTitle = formValue['articleTitle']; const articleDescription = formValue['articleDescription']; + const tags = Array.from(this.selectedTags); this.editorService.setArticleTitle(articleTitle); this.editorService.setArticleDescription(articleDescription); - this.editorService.saveArticle(this.content) + this.editorService.saveArticle(this.content, tags) .subscribe(result => { if (result['text'] === this.content) { this.snackBar.open('Successfully saved article', '', { @@ -132,8 +150,49 @@ export class EditorComponent implements OnInit { } } - previewArticle() { + filterTags(text: string): Observable { + return this.editorService.getTags(text); + } + + removeTag(tag: string) { + this.selectedTags.delete(tag); + } + tagSelected(tag: string) { + if (this.selectedTags.has(this.tagInput)) { + this.snackBar.open('Tag already exists', '', { + duration: 2000 + }); + } else { + this.selectedTags.add(tag); + this.snackBar.open(`Added the tag: ${tag}`, '', { + duration: 2000 + }); + this.formGroup.get('tags').patchValue(''); + } } + onEnter(event: any) { + if (event.keyCode === 13) { + if (this.selectedTags.has(this.tagInput)) { + this.snackBar.open('Tag already exists', '', { + duration: 2000 + }); + } else { + const input = this.tagInput; + this.editorService.addTag(input) + .subscribe(result => { + this.selectedTags.add(input); + this.snackBar.open(`Added the tag: ${input}`, '', { + duration: 2000 + }); + this.formGroup.get('tags').patchValue(''); + }, error => { + this.snackBar.open('Error adding tag', '', { + duration: 2000 + }); + }); + } + } + } } diff --git a/src/app/material.module.ts b/src/app/material.module.ts index a1a536a..ab3f9fd 100644 --- a/src/app/material.module.ts +++ b/src/app/material.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { MatAutocompleteModule, MatGridListModule, MatDialogModule, MatInputModule, MatSelectModule, MatMenuModule, MatSidenavModule, MatToolbarModule, MatListModule, MatCardModule, MatButtonModule, MatIconModule, MatSnackBarModule, MatTableModule, - NoConflictStyleCompatibilityMode } from '@angular/material'; + NoConflictStyleCompatibilityMode, MatChipsModule } from '@angular/material'; @NgModule({ imports: [ @@ -20,6 +20,7 @@ import { MatAutocompleteModule, MatGridListModule, MatDialogModule, MatInputModu MatIconModule, MatSnackBarModule, MatTableModule, + MatChipsModule, NoConflictStyleCompatibilityMode ], exports: [ @@ -37,6 +38,7 @@ import { MatAutocompleteModule, MatGridListModule, MatDialogModule, MatInputModu MatIconModule, MatSnackBarModule, MatTableModule, + MatChipsModule, NoConflictStyleCompatibilityMode ] })