Angular2触发按钮上的主机侦听器单击

时间:2017-01-02 00:13:18

标签: angular service listener directive subject

我需要在单击某个按钮后触发Host侦听器。然后,主机监听器应突出显示页面上任何悬停的元素,并听取鼠标点击,这将打开一个模态。问题是,当我开始收听鼠标点击并单击时,模式有时不会打开,直到我单击触发Host侦听器的按钮。此外,突出显示的元素会“卡住”并在鼠标单击尝试打开模式后保持突出显示。

这是异步问题吗?知道怎么解决这个问题吗?

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
  }
}

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

我设法搞清楚了。我找到的唯一可行的解​​决方案似乎是废弃整个服务和指令,并通过将listenGlobal的Renderers添加到文档中的任何更改来重新构建app.component。它也使代码更整洁,但不知道它是否是一个好习惯。

更新了App.component

res = (from rows in dtInvoicesStamped.AsEnumerable()
       where rows.Field<string>("Centre Name") == col.Name &&
             (rows.Field<string>("Doc Type") == row.Name
             && rows.Field<DateTime>("Date Loaded").Month == period.Month)
       select rows).CopyToDataTable();