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.

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

10 Comments

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.
I just realized it didn't completely solve my problem since it doesn't apply to other elements in my <router-outlet></router-outlet>. Any idea how to fix this?
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
And isn't there an easier way to do this? Or replicate what I'm trying to accomplish in my directive?
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
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.