0

I have a directive that adds box shadow to any hovered element on page but I need it to start applying the shadow after a click of a button. The problem I have, is it only applies to a single element.

Here is an image of the box shadow

It applies only to the header after I hover it. I need it to apply to any hovered element.

My app.component:

@Component({
  moduleId: module.id,
  selector: 'my-app',
  template: `
    <h1 myHighlight="orange">{{title}} {{clickedElement | async}}</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()>Give Feedback</a>
      <a (click)="listening = !listening" >Give Feedback2</a>

      <button id="modalButton" type="button" (click)="feedbackModal.show()">test</button>
      <my-feedback-modal>
      </my-feedback-modal>

    </nav>
    <router-outlet></router-outlet>
  `,
  styleUrls: ['app.component.css']
})
export class AppComponent {
  //@Input() highlight: boolean = false;
  title = 'Tour of Heroes';

  @ViewChild(ModalComponent) modal: ModalComponent;
  @ViewChild(HighlightDirective) highlightDir: HighlightDirective;

  @ViewChild(FeedbackModalComponent) feedbackModal: FeedbackModalComponent;

  constructor(private authService: AuthService, private el: ElementRef, private cdr: ChangeDetectorRef) {
    this.cdr = cdr;
  }

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

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

  ngDoCheck() {

  }

  giveFeedback(): void {

        this.highlightDir.startFeedback();
        this.cdr.detectChanges();
        //this.highlight = true;
  }
}

My highlight.directive:

@Directive({
  selector: 'a, abbr, address, article, body, br, button, div, 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;

    constructor(private el: ElementRef, private cdr: ChangeDetectorRef) {
        this.cdr = cdr;
        this.elementsArray = ["a", 'abbr', 'address', 'article', 'body', 'br', 'button', 'div', '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'];
    }

    //@Input() defaultColor: string;
    //@Input() listening: boolean = false;
    //check: boolean = false;

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

    public startFeedback(): boolean {
        this.listening = true;
        this.cdr.detectChanges();

        return true;
    }

    @HostListener('click') onClick() {
        if(this.listening) {

            document.getElementById('modalButton').click();

            this.listening = false;
        }
    }

    @HostListener('mouseenter') onMouseEnter() {
        if(this.listening) {

            this.el.nativeElement.style.boxShadow = '0 0 0 5px yellow';
            this.el.nativeElement.parentNode.style.boxShadow = null;
        }
    }

    @HostListener('mouseleave') onMouseLeave() {
        if(this.listening) {

            this.el.nativeElement.style.boxShadow = null;
            this.el.nativeElement.parentNode.style.boxShadow = '0 0 0 5px yellow';

            let check = false;

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

          if (!check)
            this.el.nativeElement.parentNode.style.boxShadow = null;
        }
    }
}

Any help would be greatly appreciated.

Juggy
  • 27
  • 2
  • 6

1 Answers1

2

The problem is that you use @ViewChild instead of @ViewChildren. With ViewChild it only addresses the first instance of HighlightDirective it can find in the template.

Besides some other obscure choices you made here, I would say you have to change to something like this:

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

You then have to change your giveFeedback function to this:

giveFeedback(): void {
    this.highlightDirs.forEach((highlightDir: HightlightDirective) => {
       highlightDir.startFeedback();
    });
}

There is no need for the changeDetectionRef inside any of your code. This is only necessary if you put changeDetection: ChangeDetectionStrategy.OnPush on your component/directive

Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • Thank you, other elements now have the shadow applied to them but unfortunately it's buggy and certain shadows get stuck after a hover. I'd guess my Host listeners aren't set up the best way. – Juggy Dec 27 '16 at 16:31
  • I just realized it didn't completely solve my problem since it doesn't apply to other elements in my . Any idea how to fix this? – Juggy Dec 28 '16 at 13:46
  • Yes, by using a service. Create a service on which emits an event when you use the giveFeedback button. Let your HighlightDirective inject this service and listen for this event – Poul Kruijt Dec 28 '16 at 13:48
  • And isn't there an easier way to do this? Or replicate what I'm trying to accomplish in my directive? – Juggy Dec 28 '16 at 13:55
  • Well, there is a much easier way to accomplish what you want yes.. using css. And a combination of services. You seem to be using `nativeElement`. `document.getElementById`, and direct inter component communication which are all frowned up on in angular2, and will always result in spaghetti code.. as it is now. I suggest you look a little more at the examples shown on the angular.io website. Data goes down, events go up – Poul Kruijt Dec 28 '16 at 14:27
  • Thank you for the tips. Right now I need to figure out the highlights on all of the elements. You mentioned services and emitting. How would I go about it? Should I create an EventEmitter Output in app.component and emit? How would it be captured in the service and how would I listen for the event from the directive? – Juggy Dec 28 '16 at 15:08
  • I can't explain every concept of angular. You should really look into their documentation. But even though I answered your initial question and helped you along with my comments, you decided to 'unanswer' and down vote it. Not very nice I must say – Poul Kruijt Dec 29 '16 at 06:31
  • Just because I continue to fix the entirety of my problem by engaging in comments, doesn't mean I don't give credit where it's due. I unanswered my issue because it was not solved, your initial comment didn't help me as I thought it would and I wanted to continue the problem solving. Even though I'm new here, it looks to me that you seem rather focused on getting credit than on the actual problem solving. But you did provide the most helpful answer so enjoy your reputation score. – Juggy Dec 29 '16 at 09:42
  • @Juggy I'm sorry, I didn't notice that you were new here :) anyways, how it usually goes is that you ask a question about your code if something is not working. If something else comes up, it's common to make a new question. This way, if other people have the same question as you, they can easily find it. On the other hand, what you are asking me belongs more in the Code Review site, and not Stackoverflow. If you still have issues, I would be happy to give you better pointers on how you should proceed – Poul Kruijt Dec 29 '16 at 13:33
  • Thanks for the info. I am however still having problems with the styles not being applied to all elements. – Juggy Dec 30 '16 at 00:41