2017-01-02 2 views
1

Je dois déclencher les écouteurs Host après un clic sur un bouton donné. Les écouteurs de l'hôte devraient alors mettre en évidence n'importe quel élément planant sur la page et écouter les clics de souris qui ouvriraient un modal. Le problème est que lorsque je commence à écouter les clics de souris et à cliquer, le modal ne s'ouvre parfois que lorsque je clique sur le bouton qui déclenche les écouteurs de l'hôte. Les éléments en surbrillance sont également bloqués et restent en surbrillance après un clic de souris en essayant d'ouvrir un modal.Angular2 qui déclenche l'écoute des hôtes sur un bouton, cliquez sur

Est-ce un problème asynchrone? Une idée pour résoudre ce problème, s'il vous plaît?

Highlight.directive

import { Directive, ElementRef, HostListener, Input, Output, EventEmitter, SimpleChanges } from '@angular/core'; 

import { FeedbackService } from './feedback.service'; 

import {Observable} from 'rxjs/Rx'; 

@Directive({ 
    selector: 'a, abbr, address, article, body, br, button, div, form, h1, h2, h3, h4, h5, h6, header, hr, i, iframe, img, ' + 
    'input, label, li, link, meta, nav, object, ol, option, output, p, param, pre, section, select, small, source, span,' + 
    'summary, table, tbody, td, textarea, tfoot, th, thead, time, title, tr, u, ul, video' 
}) 
export class HighlightDirective { 
    elementsArray: string[]; 
    listening: boolean = false; 
    allowClick: boolean = false; 

    @Output() notifyParent: EventEmitter<any> = new EventEmitter(); 

    @Input() start: boolean; 

    constructor(private el: ElementRef, private feedbackService: FeedbackService) { 
     this.elementsArray = ["a", 'abbr', 'address', 'article', 'body', 'br', 'button', 'div', 'form', 'h1', 'h2', 'h3', 'h4', 'h5' 
     , 'h6', 'header', 'hr', 'i', 'iframe', 'img', 'input', 'label', 'li', 'link', 'meta', 'nav', 'object', 'ol', 'option' 
     , 'output', 'p', 'param', 'pre', 'section', 'select', 'small', 'source', 'span', 'summary', 'table', 'tbody', 'td' 
     , 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'u', 'ul', 'video']; 

     feedbackService.myBool$.subscribe((newBool: boolean) => { this.listening = newBool; }); 
    } 

    //check: boolean = false; 

    ngOnChanges(changes: SimpleChanges) { 
     console.log(changes); 
     this.listening = true; 
    } 

    public getElement(): ElementRef { 
     return this.el; 
    } 

    public startFeedback(): void { 
     this.listening = true; 
    } 

    ngOnInit() { 
     //Observable.fromEvent(document, 'mouseenter').subscribe(data => {}); 
    } 

    //@HostListener('click') onClick() { 
    @HostListener('document:click', ['$event.target']) onClick(targetElement) { 
     //if (this.listening && this.allowClick) { 
     if (this.feedbackService.boolSubject.getValue() == true && this.allowClick) { 
      //document.getElementById('feedbackButton').click(); 

      console.log(11); 
      this.notifyParent.emit(targetElement); 

      //this.feedbackService.boolSubject.next(false); 

      this.el.nativeElement.style.boxShadow = null; 
      this.listening = false; 
      this.start = false; 
      this.allowClick = false; 
     } 
    } 

    @HostListener('mouseenter', ['$event.target']) onMouseEnter(targetElement) { 
     //if(this.listening) { 
     if (this.feedbackService.boolSubject.getValue() == true) { 

      if(!this.allowClick) 
       this.allowClick = true; 

      targetElement.parentNode.style.boxShadow = null; 
      if(targetElement.className != 'container') 
       targetElement.style.boxShadow = '0 0 0 5px yellow'; 
     } 
    } 

    @HostListener('mouseleave', ['$event.target']) onMouseLeave(targetElement) { 
     //if(this.listening) { 
     if (this.feedbackService.boolSubject.getValue()) { 
      targetElement.style.boxShadow = null; 
      if(targetElement.parentNode.className != 'container') 
       targetElement.parentNode.style.boxShadow = '0 0 0 5px yellow'; 

      let check = false; 

      for (let entry of this.elementsArray) { 
       if (targetElement.parentNode.nodeName == entry.toUpperCase()) { 
        check = true; 
        break; 
       } 
      } 

      if (!check) 
       targetElement.parentNode.style.boxShadow = null; 
     } 
    } 
} 

Feedback.service

import { Injectable } from '@angular/core'; 
import { Observable } from 'rxjs'; 
import { Subject } from 'rxjs/Subject'; 
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 
import {AsyncSubject} from 'rxjs/Rx'; 

@Injectable() 
export class FeedbackService { 
    myBool$: Observable<boolean>; 

    public boolSubject: BehaviorSubject<boolean>; 

    private checkValue: boolean = false; 

    constructor() { 
     this.boolSubject = new BehaviorSubject<boolean>(false); 
     this.myBool$ = this.boolSubject.asObservable(); 
    } 

    startFeedback(): void { 
     this.boolSubject.next(true); 
     //this.checkValue = true; 
    } 

    endFeedback(): void { 
     this.boolSubject.next(false); 
     //this.checkValue = false; 
    } 

    getValue(): boolean { 
     //this.boolSubject.next(true); 
     return this.checkValue; 
    } 
} 

App.component

import { Component, ViewChild, ElementRef, QueryList, Input, ViewChildren, ContentChildren } from '@angular/core'; 
import { AuthService } from './auth.service'; 

import { HighlightDirective } from './highlight.directive'; 
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 

import { ModalComponent } from './modal.component'; 
import { FeedbackModalComponent } from './feedbackModal.component'; 
import { FeedbackService } from './feedback.service'; 


@Component({ 
    moduleId: module.id, 
    selector: 'my-app', 
    template: ` 
    <div class="container"> 
    <h1 myHighlight="orange">{{title}}</h1> 
    <nav> 
     <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a> 
     <a routerLink="/heroes" routerLinkActive="active">Heroes</a> 
     <a routerLink="/secret-heroes" *ngIf="authService.loggedIn()" routerLinkActive="active">Secret Heroes</a> 
     <a (click)=authService.login() *ngIf="!authService.loggedIn()">Log In</a> 
     <a (click)=authService.logout() *ngIf="authService.loggedIn()">Log Out</a> 
     <a (click)=giveFeedback() (notifyParent)="getNotification($event)">Give Feedback</a> 

    <my-feedback-modal> 
    </my-feedback-modal> 

     </nav> 
     <router-outlet></router-outlet> 
    </div> 
    `, 
    styleUrls: ['app.component.css'] 
}) 
export class AppComponent { 
    title = 'Tour of Heroes'; 
    startFeedback = false; 
    feedbackElement: ElementRef; 

    @ViewChildren(HighlightDirective) highlightDirs: QueryList<HighlightDirective>; 

    @ViewChild(FeedbackModalComponent) feedbackModal: FeedbackModalComponent; 

    constructor(private authService: AuthService, private el: ElementRef, private feedbackService: FeedbackService) { } 

    clickedElement:BehaviorSubject<ElementRef> = new BehaviorSubject(this.el); 

    ngAfterViewInit() { 
    //this.clickedElement.next(this.highlightDir.getElement().nativeElement.nodeName); 
    } 

    getNotification(evt) { 
     // Do something with the notification (evt) sent by the child! 
     console.log(evt); 
     this.feedbackModal.show(evt); 
    } 

    giveFeedback(): void { 
     //this.startFeedback = true; 
// this.highlightDirs.forEach((highlightDir: HighlightDirective) => { 
//  highlightDir.startFeedback(); 
// }); 
    this.feedbackService.startFeedback(); 
    } 
} 

FeedbackModal.component

import { Component, ViewChild, ElementRef, Input } from '@angular/core'; 

import { ModalComponent } from './modal.component'; 

import { Hero } from './hero'; 
import { HeroService } from './hero.service'; 
import { Router } from '@angular/router'; 

import { FeedbackService } from './feedback.service'; 

@Component({ 
    moduleId: module.id, 
    selector: 'my-feedback-modal', 
    template: ` 
    <div class="modal fade" tabindex="-1" [ngClass]="{'in': visibleAnimate}" 
     [ngStyle]="{'display': visible ? 'block' : 'none', 'opacity': visibleAnimate ? 1 : 0}"> 
    <div class="modal-dialog"> 
     <div class="modal-content"> 
     <div class="modal-header"> 
      Element <{{ elementName }}> 
     </div> 
     <div class="modal-body"> 
      <div id="first-row"></div><br> 
      <form action=""> 
      <label for="rating">Rating</label> 
      <div class="row" > 
      <div class="col-xs-12"> 
       <label class="radio-inline"> 
       <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1 
       </label> 
       <label class="radio-inline"> 
       <input type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2"> 2 
       </label> 
       <label class="radio-inline"> 
       <input type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3"> 3 
       </label> 
       <label class="radio-inline"> 
       <input type="radio" name="inlineRadioOptions" id="inlineRadio4" value="option4"> 4 
       </label> 
       <label class="radio-inline"> 
       <input type="radio" name="inlineRadioOptions" id="inlineRadio5" value="option5"> 5 
       </label> 
       </div> 
      </div> 
      <br> 
      <label for="comment">Comment</label> 
      <textarea class="form-control" [(ngModel)]="hero.name" placeholder="name" name="name" rows="3"></textarea><br> 
      </form> 
     </div> 
     <div class="modal-footer"> 
      <button type="button" class="btn btn-default" (click)="hide()">Close</button> 
      <button type="button" class="btn btn-primary" (click)="save()">Save changes</button> 
     </div> 
     </div> 
    </div> 
    </div> 
    `, 
    styleUrls: ['modal.component.css'] 
}) 

export class FeedbackModalComponent { 
    public visible = false; 
    private visibleAnimate = false; 
    private elementName: any; 
    private appendingElement: any; 

    @Input() hero: Hero; 
    error: any; 

    @ViewChild(ModalComponent) modal: ModalComponent; 

    constructor(private el: ElementRef, private heroService: HeroService, 
    private router: Router, private feedbackService: FeedbackService) { } 

    ngAfterViewInit() { 
    //this.clickedElement.next(this.highlightDir.getElement().nativeElement.nodeName); 
    } 

    ngOnInit(): void { 
     this.hero = new Hero(); 
    } 

    public show(el: any): void { 
     this.feedbackService.boolSubject.next(false); 
    //this.feedbackService.endFeedback(); 

    this.el = el; 
    this.elementName = el.nodeName; 

// this.appendingElement = document.getElementById('first-row'); 
// let cloneEl = el.cloneNode(true); 
// this.appendingElement.appendChild(cloneEl); 

    this.visible = true; 
    setTimeout(() => this.visibleAnimate = true); 
    } 

    public hide(): void { 
//  this.appendingElement.removeChild(this.appendingElement.firstChild); 

    this.visibleAnimate = false; 
    setTimeout(() => this.visible = false, 300); 
    } 

    save(): void { 
    this.heroService 
     .save(this.hero) 
     .then(hero => { 
      this.hero = hero; // saved hero, w/ id if new 
      if(this.router.url == '/heroes') 
      window.location.reload(); 
      else 
      this.hide(); 
     }) 
     .catch(error => this.error = error); // TODO: Display error message 
    } 
} 

Toute aide serait grandement appréciée.

+0

Avez-vous des erreurs? –

+0

Non, je n'ai aucune erreur. – Juggy

+0

Si l'événement se produit, cela devrait fonctionner correctement lorsque vous cliquez n'importe où dans le composant. –

Répondre

0

J'ai réussi à comprendre. La seule solution de travail que j'ai trouvée semble être de mettre au rebut tout le service et la directive et de refaire app.component en ajoutant des Renderers qui listenGlobal à tout changement dans le document. Cela rend également le code plus clair mais aucune idée si c'est une bonne pratique.

Mise à jour App.component

import { Component, ViewChild, ElementRef, Renderer, OnDestroy } from '@angular/core'; 
import { AuthService } from './auth.service'; 

import { FeedbackModalComponent } from './feedbackModal.component'; 
import { Router } from '@angular/router'; 
import { NotificationsService } from 'angular2-notifications'; 


@Component({ 
    moduleId: module.id, 
    selector: 'my-app', 
    template: ` 
    <div class="container"> 
     <h1 myHighlight="orange">{{title}}</h1> 
     <nav> 
      <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a> 
      <a routerLink="/heroes" routerLinkActive="active">Heroes</a> 
      <a routerLink="/secret-heroes" *ngIf="authService.loggedIn()" routerLinkActive="active">Secret Heroes</a> 
      <a (click)=authService.login() *ngIf="!authService.loggedIn()">Log In</a> 
      <a (click)=authService.logout() *ngIf="authService.loggedIn()">Log Out</a> 
      <a (click)=giveFeedback() (notifyParent)="getNotification($event)">Give Feedback</a> 

      <my-feedback-modal> 
      </my-feedback-modal> 

      </nav> 
      <router-outlet></router-outlet> 

      <simple-notifications [options]="options"></simple-notifications> 
     </div> 
    `, 
    styleUrls: ['app.component.css'] 
}) 
export class AppComponent { 
    title = 'Tour of Heroes'; 
    public options = { 
     timeOut: 0, 
     showProgressBar: true, 
     pauseOnHover: false, 
     clickToClose: true, 
     maxLength: 50, 
     animate: "fromRight" 
    }; 

    lastEvent: any; 
    listening: boolean = false; 
    allowClick: boolean = false; 
    globalListenFunc: Function; 
    globalListenFunc2: Function; 

    @ViewChild(FeedbackModalComponent) feedbackModal: FeedbackModalComponent; 

    constructor(private authService: AuthService, private el: ElementRef, private router: Router, 
     private notificationsService: NotificationsService, private renderer: Renderer) 
    { 
     this.lastEvent = el; 
     this.lastEvent.style = []; 
     this.lastEvent.style.boxShadow = null; 
    } 

    addListeners() { 
     this.globalListenFunc = this.renderer.listenGlobal('document', 'click', (event) => { 
      if (this.listening == true && this.allowClick) { 
       this.notificationsService.remove(); 
       this.feedbackModal.show(event.srcElement); 

       // FIND A BETTER FIX 
       this.router.navigateByUrl('/dashboard'); 

       this.el.nativeElement.style.boxShadow = null; 
       this.listening = false; 
       this.allowClick = false; 

       this.removeListeners(); 
      } 
     }); 

     this.globalListenFunc2 = this.renderer.listenGlobal('document', 'mousemove', (event) => { 
      if (this.listening == true && this.lastEvent != event.srcElement) { 
       if(!this.allowClick) 
        this.allowClick = true; 

       this.lastEvent.style.boxShadow = ''; 
       if(event.srcElement.className != 'container') { 
        event.srcElement.style.boxShadow = '0 0 0 5px yellow'; 
       } 
       this.lastEvent = event.srcElement; 
      } 
     }); 
    } 

    removeListeners() { 
     this.globalListenFunc(); 
     this.globalListenFunc2(); 
    } 

    ngOnDestroy() { 
     // Remove the listeners! 
     this.globalListenFunc(); 
     this.globalListenFunc2(); 
    } 

    giveFeedback(): void { 
     this.notificationsService.info(
      'Feedback', 
      "Choose an element you would like to rate.", 
      this.options 
     ) 
     this.listening = true; 
     this.addListeners(); 
    } 
}