2017-10-03 3 views
0

Je construis un SPA dans Angular4/typescript. J'ai plusieurs javascripts qui manipulent DOM (ajouter/supprimer des classes CSS) que je veux intégrer à l'application. Quelle est la meilleure façon de le faire? Actuellement, la structure de l'application est la suivante:Gestion de plusieurs éléments DOM dans Angular4

-app: 
-app.component.ts 
-app.module.ts 
-menu.component.ts 
-menu.view.html 
-menu.css 

menu.component.ts poignées affichage des données de l'application. Je veux intégrer le script suivant:

<script> 
    const triggers = document.querySelectorAll('.cool > li'); 
    const background = document.querySelector('.dropdownBackground'); 
    const nav = document.querySelector('.top'); 

    function handleEnter(){ 
    this.classList.add('trigger-enter'); 
    setTimeout(() => this.classList.contains('trigger-enter') && 
     this.classList.add('trigger-enter-active'), 150); 
    background.classList.add('open'); 

    const dropdown = this.querySelector('.dropdown'); 
    const dropdownCords = dropdown.getBoundingClientRect(); 
    const navCoords = nav.getBoundingClientRect(); 

    const coords = { 
     height: dropdownCords.height, 
     width: dropdownCords.width, 
     top: dropdownCords.top - navCoords.top, 
     left: dropdownCords.left- navCoords.left 
    }; 
    background.style.setProperty('width', `${coords.width}px`); 
    background.style.setProperty('height', `${coords.height}px`); 
    background.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`); 
    } 

    function handleLeave(){ 
    this.classList.remove('trigger-enter', 'trigger-enter-active'); 
    background.classList.remove('open'); 

    } 
    triggers.forEach(trigger => trigger.addEventListener('mouseenter', handleEnter)); 
    triggers.forEach(trigger => trigger.addEventListener('mouseleave', handleLeave)); 
</script> 

CSS:

nav { 
    position: relative; 
    perspective: 600px; 
    } 
    nav ul { 
    list-style: none; 
    margin: 0; 
    padding: 0; 
    display: flex; 
    justify-content: center; 
    } 
    .cool > li { 
    position: relative; 
    display:flex; 
    justify-content: center; 
    } 
    .cool > li > a { 
    color: yellow; 
    text-decoration: none; 
    font-size: 20px; 
    background: rgba(0,0,0,0.2); 
    padding:10px 20px; 
    display: inline-block; 
    margin:20px; 
    border-radius:5px; 
    } 
    .dropdown { 
    opacity: 0; 
    position: absolute; 
    overflow: hidden; 
    padding:20px; 
    top:-20px; 
    border-radius:2px; 
    transition: all 0.5s; 
    transform: translateY(100px); 
    will-change: opacity; 
    display: none; 
    } 
    .trigger-enter .dropdown { 
    display: block; 
    } 
    .trigger-enter-active .dropdown { 
    opacity: 1; 
    } 
    .dropdownBackground { 
    width:100px; 
    height:100px; 
    position: absolute; 
    background: #fff; 
    border-radius: 4px; 
    box-shadow: 0 50px 100px rgba(50,50,93,.1), 0 15px 35px rgba(50,50,93,.15), 0 5px 15px rgba(0,0,0,.1); 
    transition:all 0.3s, opacity 0.1s, transform 0.2s; 
    transform-origin: 50% 0; 
    display: flex; 
    justify-content: center; 
    opacity:0; 
    } 
    .dropdownBackground.open { 
    opacity: 1; 
    } 
    .arrow { 
    position: absolute; 
    width:20px; 
    height:20px; 
    display: block; 
    background:white; 
    transform: translateY(-50%) rotate(45deg); 
    } 

html:

<nav class="top" menuElement> 
    <div class="dropdownBackground"> 
     <span class="arrow"></span> 
    </div> 

    <ul class="cool"> 
     <li> 
     <a href="#">Some information</a> 
     <div class="dropdown dropdown1"> 
     Info 
     </div> 
     </li> 
     <li> 
     <a href="#">More information</a> 
     <ul class="dropdown"> 
      <li> 
      some info 
      </li> 
     </ul> 
     </li> 
     <li> 
     <a href="#">Other Links</a> 
     <ul class="dropdown dropdown3"> 
      <li>some links</li> 
     </ul> 
     </li> 
    </ul> 
    </nav> 

Il y a 3 querySelector. J'ai essayé de mettre en œuvre la fonctionnalité en définissant une directive: menu.directive.ts:

import {Directive, HostListener, ElementRef, Renderer2} from '@angular/core'; 

@Directive({ 
    selector: '[menuElement]', 
}) 

export class MenusDirective { 
    constructor(
    private renderer: Renderer2, 
    private el: ElementRef 
){} 
    //menuLi = this.el.nativeElement.querySelectorAll('.cool > li'); 
    background = this.el.nativeElement.querySelector('.dropdownBackground'); 

handleEnter(target){ 
    this.renderer.addClass(target, 'trigger-enter'); 
    setTimeout(() => target.classList.contains('trigger-enter') && 
    this.renderer.addClass(target, 'trigger-enter-active'), 150); 
    this.background.classList.add('open'); 

    const dropdown = target.querySelector('.dropdown'); 
    const dropdownCords = dropdown.getBoundingClientRect(); 
    const filterNavCoords = this.el.nativeElement.getBoundingClientRect(); 

    const coords = { 
    height: dropdownCords.height, 
    width: dropdownCords.width, 
    top: dropdownCords.top - filterNavCoords.top, 
    left: dropdownCords.left- filterNavCoords.left 
    }; 

    this.background.style.setProperty('width', `${coords.width}px`); 
    this.background.style.setProperty('height', `${coords.height}px`); 
    this.background.style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`); 

} 

handleLeave(target){ 
    this.renderer.removeClass(target, 'trigger-enter'); 
    this.renderer.removeClass(target, 'trigger-enter-active'); 
    this.background.classList.remove('open'); 

} 


@HostListener('mouseenter', ['$event']) onMouseEnter(event: Event) { 
    this.handleEnter(event.target); 
} 

@HostListener('mouseleave', ['$event']) onMouseLeave(event: Event) { 
    this.handleLeave(event.target); 
} 

} 

Si je le sélecteur définir [menuElement] est égal à la navigation supérieure, je vais pouvoir sélectionner:

background = this.el.nativeElement.querySelector('.dropdownBackground'); 

ou

triggers = this.el.nativeElement.querySelectorAll('.cool > li'); 

mais l'événement Listener serait lier sur l'ensemble nav, donc il n'y a aucun moyen de savoir que l'un des « .cool> li » est sélectionné. Si je définis [menuElement] comme '.cool> li', je ne trouve pas un moyen de sélectionner '.dropdownBackground' ou le nav supérieur.

const background = document.querySelector('.dropdownBackground'); 

renvoie Null (j'ai essayé aussi getElementByClassName, etc.) Je ne peux pas les définir dans les différentes directives comme ils sont manipulés simultanément. Je également essayé d'ajouter la fonction HTML:

<li (mouseenter)="handleEnter($event.target)" (mouseleave)="handleLeave($event.target)"> 

Mais les fonctions ne sont pas reconnus comme définis dans menu.directive et non menu.component. Quelles sont les solutions possibles? - intégrer Javascript tel quel (chargé après le chargement du DOM) - sélectionner plusieurs objets DOM, mais lier un EventListener à l'un d'entre eux. Merci beaucoup! Veronika

Répondre

0

La façon dont j'ai compris au travail: ajouter des écouteurs d'événement dans view.html:

<li (mouseenter)="handleEnter($event.target);" (mouseleave)="handleLeave($event.target)"> 

Définir les fonctions handleEnter et handleLeave dans menu.component.ts, ajouter MenuDirective en tant que fournisseur: le menu .component.ts:

import { MenuDirective } from './menu.directive'; 

@Component({ 
    selector: 'menu', 
    templateUrl: './menu.view.html', 
    styleUrls: [ './app.component.css'], 
    providers: [MenuDirective] 
}) 
export class MenuComponent { 
    constructor(
    private menuDirective: MenuDirective, 
){} 
handleEnter(target): void { 
    this.menuDirective.handleEnter(target); 
} 

handleLeave(target): void { 
    this.menuDirective.handleLeave(target); 
} 
} 

et menu.directive.ts:

import {Directive, HostListener, ElementRef, Renderer2} from '@angular/core'; 

@Directive({ 
    selector: '[menuElement]', 
}) 

export class MenuDirective { 
    constructor(
    private renderer: Renderer2, 
    private el: ElementRef 
){} 

handleEnter(target){ 
    this.renderer.addClass(target, 'trigger-enter'); 
    setTimeout(() => target.classList.contains('trigger-enter') && 
    this.renderer.addClass(target, 'trigger-enter-active'), 150); 
    this.el.nativeElement.querySelector('.dropdownBackground').classList.add('open'); 

    const dropdown = target.querySelector('.dropdown'); 
    const dropdownCords = dropdown.getBoundingClientRect(); 
    const filterNavCoords = this.el.nativeElement.getBoundingClientRect(); 
    const coords = { 
    height: dropdownCords.height, 
    width: dropdownCords.width, 
    top: dropdownCords.top - filterNavCoords.top, 
    left: dropdownCords.left- filterNavCoords.left 
    }; 
    this.el.nativeElement.querySelector('.dropdownBackground').style.setProperty('width', `${coords.width}px`); 
    this.el.nativeElement.querySelector('.dropdownBackground').style.setProperty('height', `${coords.height}px`); 
    this.el.nativeElement.querySelector('.dropdownBackground').style.setProperty('transform', `translate(${coords.left}px, ${coords.top}px)`); 

} 

handleLeave(target){ 
    this.renderer.removeClass(target, 'trigger-enter'); 
    this.renderer.removeClass(target, 'trigger-enter-active'); 
    this.el.nativeElement.querySelector('.dropdownBackground').classList.remove('open'); 
} 

} 

Il semble trop complexe pour une telle caractéristique simple ...