Commit 64b348aab7aefb9b7a076e2c8f44215e929d70c6
1 parent
77280cd9
Closes #55 - EventEmitter by object structures using Typescript class Mixins
Showing
11 changed files
with
192 additions
and
29 deletions
Show diff stats
src/app/article/comment/comments.component.spec.ts
| ... | ... | @@ -11,8 +11,7 @@ describe("Components", () => { |
| 11 | 11 | |
| 12 | 12 | beforeEach(angular.mock.module("templates")); |
| 13 | 13 | |
| 14 | - let commentService = jasmine.createSpyObj("commentService", ["getByArticle"]); | |
| 15 | - commentService.onSave = helpers.mocks.commentService.onSave; | |
| 14 | + let commentService = helpers.mocks.commentService; | |
| 16 | 15 | |
| 17 | 16 | let comments = [{ id: 2 }, { id: 3 }]; |
| 18 | 17 | commentService.getByArticle = jasmine.createSpy("getByArticle") | ... | ... |
src/app/article/comment/comments.component.ts
| ... | ... | @@ -8,7 +8,7 @@ import { CommentComponent } from "./comment.component"; |
| 8 | 8 | templateUrl: 'app/article/comment/comments.html', |
| 9 | 9 | directives: [PostCommentComponent, CommentComponent] |
| 10 | 10 | }) |
| 11 | -@Inject(CommentService, "$element") | |
| 11 | +@Inject(CommentService, "$scope") | |
| 12 | 12 | export class CommentsComponent { |
| 13 | 13 | |
| 14 | 14 | comments: noosfero.CommentViewModel[] = []; |
| ... | ... | @@ -30,17 +30,28 @@ export class CommentsComponent { |
| 30 | 30 | this.loadNextPage(); |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | - this.commentService.onSave.subscribe((comment: noosfero.Comment) => { | |
| 34 | - this.commentAdded(comment); | |
| 33 | + this.commentService.addEvent<noosfero.Comment>({ | |
| 34 | + event: 'onSave', | |
| 35 | + component: 'CommentsComponent', | |
| 36 | + callback: (comment: noosfero.Comment) => { | |
| 37 | + | |
| 38 | + if (this.commentService.called !== <string>comment.id.toString()) { | |
| 39 | + | |
| 40 | + this.commentAdded(comment); | |
| 41 | + this.commentService.called = comment.id.toString(); | |
| 42 | + } | |
| 43 | + } | |
| 35 | 44 | }); |
| 45 | + | |
| 36 | 46 | } |
| 37 | 47 | |
| 38 | - commentAdded(comment: noosfero.Comment): void { | |
| 48 | + commentAdded(comment: noosfero.CommentViewModel): void { | |
| 49 | + comment.__show_reply = false; | |
| 39 | 50 | this.comments.push(comment); |
| 40 | 51 | this.resetShowReply(); |
| 52 | + this.$scope.$apply(); | |
| 41 | 53 | } |
| 42 | 54 | |
| 43 | - | |
| 44 | 55 | loadComments() { |
| 45 | 56 | return this.commentService.getByArticle(this.article, { page: this.page, per_page: this.perPage }); |
| 46 | 57 | } | ... | ... |
src/app/article/comment/post-comment/post-comment.component.spec.ts
| ... | ... | @@ -11,8 +11,8 @@ describe("Components", () => { |
| 11 | 11 | |
| 12 | 12 | beforeEach(angular.mock.module("templates")); |
| 13 | 13 | |
| 14 | - let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]); | |
| 15 | - commentService.onSave = jasmine.createSpyObj("onSave", ["subscribe", "next"]); | |
| 14 | + let commentService = helpers.mocks.commentService; | |
| 15 | + commentService.emitEvent = jasmine.createSpy("emitEvent"); | |
| 16 | 16 | let user = {}; |
| 17 | 17 | let providers = [ |
| 18 | 18 | new Provider('CommentService', { useValue: commentService }), |
| ... | ... | @@ -46,7 +46,7 @@ describe("Components", () => { |
| 46 | 46 | let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance; |
| 47 | 47 | commentService.createInArticle = jasmine.createSpy("createInArticle").and.returnValue(helpers.mocks.promiseResultTemplate({ data: {} })); |
| 48 | 48 | component.save(); |
| 49 | - expect(component.commentService.onSave.next).toHaveBeenCalled(); | |
| 49 | + expect(component.commentService.emitEvent).toHaveBeenCalled(); | |
| 50 | 50 | done(); |
| 51 | 51 | }); |
| 52 | 52 | }); | ... | ... |
src/app/article/comment/post-comment/post-comment.component.ts
| 1 | +import {ngClass} from 'ng-forward/cjs/testing/test-component-builder'; | |
| 1 | 2 | import { Inject, Input, Output, EventEmitter, Component } from 'ng-forward'; |
| 2 | 3 | import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service"; |
| 3 | 4 | import { NotificationService } from "../../../shared/services/notification.service"; |
| ... | ... | @@ -18,6 +19,7 @@ export class PostCommentComponent { |
| 18 | 19 | @Input() article: noosfero.Article; |
| 19 | 20 | @Input() parent: noosfero.Comment; |
| 20 | 21 | @Input() comment = <noosfero.Comment>{}; |
| 22 | + @Input() identifier: any; | |
| 21 | 23 | private currentUser: noosfero.User; |
| 22 | 24 | |
| 23 | 25 | constructor( |
| ... | ... | @@ -33,7 +35,21 @@ export class PostCommentComponent { |
| 33 | 35 | } |
| 34 | 36 | this.commentService.createInArticle(this.article, this.comment).then((result: noosfero.RestResult<noosfero.Comment>) => { |
| 35 | 37 | |
| 36 | - this.commentService.onSave.next(result.data); | |
| 38 | + if (this.identifier) { | |
| 39 | + this.commentService.emitEvent({ | |
| 40 | + id: this.identifier, | |
| 41 | + event: 'onSave', | |
| 42 | + param: result.data | |
| 43 | + }); | |
| 44 | + } else { | |
| 45 | + this.commentService.emitEvent({ | |
| 46 | + id: result.data.id, | |
| 47 | + component: 'CommentsComponent', | |
| 48 | + event: 'onSave', | |
| 49 | + param: result.data | |
| 50 | + }); | |
| 51 | + } | |
| 52 | + | |
| 37 | 53 | this.comment.body = ""; |
| 38 | 54 | this.notificationService.success({ title: "comment.post.success.title", message: "comment.post.success.message" }); |
| 39 | 55 | }); | ... | ... |
src/lib/ng-noosfero-api/http/comment.service.ts
| 1 | 1 | import { Injectable, Inject, EventEmitter } from "ng-forward"; |
| 2 | 2 | import {RestangularService} from "./restangular_service"; |
| 3 | 3 | import {ArticleService} from "./article.service"; |
| 4 | +import {Mixins, EventService, IEvent} from '../mixins/event-service.mixin'; | |
| 5 | + | |
| 4 | 6 | |
| 5 | 7 | @Injectable() |
| 8 | +@Mixins(EventService) | |
| 6 | 9 | @Inject("Restangular", "$q", "$log", ArticleService) |
| 7 | -export class CommentService extends RestangularService<noosfero.Comment> { | |
| 10 | +export class CommentService extends RestangularService<noosfero.Comment> implements EventService { | |
| 11 | + | |
| 12 | + events: IEvent[] = []; | |
| 8 | 13 | |
| 9 | - public onSave: EventEmitter<noosfero.Comment> = new EventEmitter<noosfero.Comment>(); | |
| 14 | + called: string; | |
| 10 | 15 | |
| 11 | 16 | constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService, protected articleService: ArticleService) { |
| 12 | 17 | super(Restangular, $q, $log); |
| ... | ... | @@ -33,4 +38,9 @@ export class CommentService extends RestangularService<noosfero.Comment> { |
| 33 | 38 | let articleElement = this.articleService.getElement(<number>article.id); |
| 34 | 39 | return this.create(comment, articleElement, null, { 'Content-Type': 'application/json' }, false); |
| 35 | 40 | } |
| 41 | + | |
| 42 | + // Mixin "EventService" declarations | |
| 43 | + addEvent: <T>(data: { event: string, callback: Function, id?: any, component?: string, scope?: any }) => void; | |
| 44 | + getEvents: (data: { event: string, id?: any, component?: string }) => void; | |
| 45 | + emitEvent: <T>(data: { event: string, param: T, id?: any, component?: string }) => void; | |
| 36 | 46 | } | ... | ... |
| ... | ... | @@ -0,0 +1,104 @@ |
| 1 | +import { EventEmitter } from "ng-forward"; | |
| 2 | +export {Mixins} from "../decorators/mixins"; | |
| 3 | + | |
| 4 | +export interface IEvent { | |
| 5 | + id: any; | |
| 6 | + component: string; | |
| 7 | + emitters: { | |
| 8 | + onSave?: EventEmitter<noosfero.Comment> | |
| 9 | + }; | |
| 10 | +} | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * @ngdoc object | |
| 14 | + * @name mixins.EventService | |
| 15 | + * @see Typescript official documentation https://www.typescriptlang.org/docs/mixins.html | |
| 16 | + * @description | |
| 17 | + * | |
| 18 | + * A mixin class to be implemented by another class (e.g. Angular Services) | |
| 19 | + * to emit's and subscribe events | |
| 20 | + * | |
| 21 | + * <pre> | |
| 22 | + * @Mixins(EventService) | |
| 23 | + * export class MyService implements EventService { | |
| 24 | + * | |
| 25 | + * } | |
| 26 | + * </pre> | |
| 27 | + */ | |
| 28 | +export class EventService { | |
| 29 | + | |
| 30 | + events: IEvent[] = []; | |
| 31 | + | |
| 32 | + called: string; | |
| 33 | + | |
| 34 | + addEvent<T>(data: { event: string, callback: Function, id?: any, component?: string, scope?: any }) { | |
| 35 | + | |
| 36 | + let hasEvent = false; | |
| 37 | + | |
| 38 | + if (this.events.length > 0) { | |
| 39 | + | |
| 40 | + this.events.forEach((eventData: IEvent, index: number) => { | |
| 41 | + if (data.id === eventData.id) { | |
| 42 | + | |
| 43 | + if (!(<any>this.events)[index].emitters[data.event]) { | |
| 44 | + (<any>this.events)[index].emitters[data.event] = new EventEmitter(); | |
| 45 | + } else { | |
| 46 | + (<EventEmitter<T>>(<any>this.events)[index].emitters[data.event]).subscribe(data.callback.bind(data.scope || this)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + return (hasEvent = true); | |
| 50 | + } | |
| 51 | + }); | |
| 52 | + } | |
| 53 | + | |
| 54 | + if (!hasEvent) { | |
| 55 | + | |
| 56 | + let newEvent: IEvent = { | |
| 57 | + id: data.id, | |
| 58 | + component: data.component, | |
| 59 | + emitters: <any>{} | |
| 60 | + }; | |
| 61 | + | |
| 62 | + (<any>newEvent.emitters)[data.event] = <EventEmitter<T>>new EventEmitter(); | |
| 63 | + (<any>newEvent.emitters)[data.event].subscribe(data.callback); | |
| 64 | + | |
| 65 | + this.events.push(newEvent); | |
| 66 | + } | |
| 67 | + } | |
| 68 | + | |
| 69 | + getEvents(data: { event: string, id?: any, component?: string }): any { | |
| 70 | + let result: any = {}; | |
| 71 | + | |
| 72 | + for (let i = 0; i < this.events.length; i++) { | |
| 73 | + if (data.id === (<Array<IEvent>>this.events)[i].id && !data.component) { | |
| 74 | + | |
| 75 | + result = (<any>this.events)[i].emitters[data.event]; | |
| 76 | + break; | |
| 77 | + } else if (data.component === (<any>this.events)[i].component) { | |
| 78 | + result[data.component] = (<any>this.events)[i].emitters[data.event]; | |
| 79 | + | |
| 80 | + break; | |
| 81 | + } else { | |
| 82 | + result[(<any>this.events)[i].component] = (<any>this.events)[i].emitters[data.event]; | |
| 83 | + } | |
| 84 | + } | |
| 85 | + | |
| 86 | + return result; | |
| 87 | + } | |
| 88 | + | |
| 89 | + emitEvent<T>(data: { event: string, param: T, id?: any, component?: string }) { | |
| 90 | + | |
| 91 | + let eventOrEmitters = this.getEvents(data); | |
| 92 | + | |
| 93 | + if (eventOrEmitters instanceof EventEmitter) { | |
| 94 | + | |
| 95 | + eventOrEmitters.next(data.param); | |
| 96 | + } else { | |
| 97 | + | |
| 98 | + Object.keys(eventOrEmitters).forEach((emitter: string) => { | |
| 99 | + (<EventEmitter<T>>eventOrEmitters[emitter]).next(data.param); | |
| 100 | + }); | |
| 101 | + | |
| 102 | + } | |
| 103 | + } | |
| 104 | +} | ... | ... |
src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts
| ... | ... | @@ -16,26 +16,37 @@ export class AllowCommentComponent { |
| 16 | 16 | @Input() content: string; |
| 17 | 17 | @Input() paragraphUuid: string; |
| 18 | 18 | @Input() article: noosfero.Article; |
| 19 | - commentsCount: number; | |
| 19 | + commentsCount: number = 0; | |
| 20 | 20 | display = false; |
| 21 | 21 | |
| 22 | - constructor(private $scope: ng.IScope, | |
| 22 | + constructor( | |
| 23 | + private $scope: ng.IScope, | |
| 23 | 24 | private commentParagraphEventService: CommentParagraphEventService, |
| 24 | 25 | private commentParagraphService: CommentParagraphService, |
| 25 | - private commentService: CommentService) { } | |
| 26 | + private commentService: CommentService | |
| 27 | + ) { } | |
| 26 | 28 | |
| 27 | 29 | ngOnInit() { |
| 28 | 30 | this.commentParagraphEventService.subscribeToggleCommentParagraph((article: noosfero.Article) => { |
| 29 | 31 | this.article = article; |
| 30 | 32 | this.$scope.$apply(); |
| 31 | 33 | }); |
| 32 | - | |
| 33 | 34 | this.commentParagraphService.commentParagraphCount(this.article, this.paragraphUuid).then((count: number) => { |
| 34 | 35 | this.commentsCount = count; |
| 35 | 36 | }); |
| 36 | 37 | |
| 37 | - this.commentService.onSave.subscribe((comment: noosfero.Comment) => { | |
| 38 | - this.commentsCount++; | |
| 38 | + this.commentService.addEvent<noosfero.Comment>({ | |
| 39 | + id: this.paragraphUuid, | |
| 40 | + event: 'onSave', | |
| 41 | + component: 'SideCommentsComponent', | |
| 42 | + scope: this, | |
| 43 | + callback: () => { | |
| 44 | + if (!this.commentsCount) { | |
| 45 | + this.commentsCount = 0; | |
| 46 | + } | |
| 47 | + this.commentsCount++; | |
| 48 | + this.$scope.$apply(); | |
| 49 | + } | |
| 39 | 50 | }); |
| 40 | 51 | } |
| 41 | 52 | ... | ... |
src/plugins/comment_paragraph/side-comments/side-comments.component.ts
| ... | ... | @@ -5,7 +5,7 @@ import {CommentParagraphService} from "../http/comment-paragraph.service"; |
| 5 | 5 | |
| 6 | 6 | @Component({ |
| 7 | 7 | selector: "comment-paragraph-side-comments", |
| 8 | - templateUrl: 'app/article/comment/comments.html', | |
| 8 | + templateUrl: 'plugins/comment_paragraph/side-comments/side-comments.html', | |
| 9 | 9 | }) |
| 10 | 10 | @Inject(CommentService, "$scope", CommentParagraphService) |
| 11 | 11 | export class SideCommentsComponent extends CommentsComponent { | ... | ... |
src/plugins/comment_paragraph/side-comments/side-comments.html
0 → 100644
| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | +<div class="comments"> | |
| 2 | + <noosfero-post-comment ng-if="ctrl.showForm" [article]="ctrl.article" [parent]="ctrl.parent" [comment]="ctrl.newComment" [identifier]="ctrl.paragraphUuid"></noosfero-post-comment> | |
| 3 | + | |
| 4 | + <div class="comments-list"> | |
| 5 | + <noosfero-comment ng-repeat="comment in ctrl.comments | orderBy: 'created_at':true" [comment]="comment" [article]="ctrl.article"></noosfero-comment> | |
| 6 | + </div> | |
| 7 | + <button type="button" ng-if="ctrl.displayMore()" class="more-comments btn btn-default btn-block" ng-click="ctrl.loadNextPage()">{{"comment.pagination.more" | translate}}</button> | |
| 8 | +</div> | ... | ... |
src/spec/mocks.ts
| ... | ... | @@ -122,18 +122,13 @@ export var mocks: any = { |
| 122 | 122 | instant: () => { } |
| 123 | 123 | }, |
| 124 | 124 | commentService: { |
| 125 | + events: <any[]>[], | |
| 126 | + called: <string>'', | |
| 125 | 127 | getByArticle: (article: noosfero.Article) => { |
| 126 | 128 | return Promise.resolve({ data: {} }); |
| 127 | 129 | }, |
| 128 | - onSave: { | |
| 129 | - event: Function, | |
| 130 | - subscribe: (fn: Function) => { | |
| 131 | - mocks.commentService['onSave'].event = fn; | |
| 132 | - }, | |
| 133 | - next: (param: any) => { | |
| 134 | - mocks.commentService['onSave'].event(param); | |
| 135 | - } | |
| 136 | - } | |
| 130 | + addEvent: () => { }, | |
| 131 | + emitEvent: () => { } | |
| 137 | 132 | }, |
| 138 | 133 | sessionWithCurrentUser: (user: any) => { |
| 139 | 134 | return { | ... | ... |