обрабатывать пользовательскую директиву с помощью ngFor - Angular2

Вчера я пытался поиграть с одним из вопросов SO и столкнулся с проблемой в своем решении.

в шаблоне компонента я использую директиву ngFor, в которой я использую свою пользовательскую директиву popover. Только через директиву, я хочу показать скрытое содержимое для каждого объекта ngFor.

После проверки http://plnkr.co/edit/X4U8ofJ5rgmE1YQ7fTAG?p=preview вы осознает мою проблему.

в событии mouseenter, используемом в директиве, я хочу показать содержимое соответствующего объекта ngFor.

directive.ts

import {Input,Component,Output,EventEmitter,Input,Directive,Hostbinding} from 'angular2/core';
import {Component, Input, OnInit, OnChanges, ChangeDetectionStrategy, ElementRef} from 'angular2/core';
@Directive({
    selector: '.tower-details',
    host:{
      '(mouseenter)':'show($event)',
      '(mouseout)':'hide()'
    }
})
export class popover{
   @Input() value: string;
   @Output() valueChange=new EventEmitter();


   ngOnChanges(...args:any[]){
     //console.log(args[0].value);
   }
   show(val)
   {
     console.log(val.target);
     this.valueChange.emit(true);
   }
   hide()
   {
     console.log('hide');
     this.valueChange.emit(false);
   }

}

app.ts

 template: `
    <div  *ngFor="#p of popovers;#index=index">
            <div class="tower-details"  [(value)]="show" style="display: block;border:1px solid green;background-color:orange" >
             Hover Me ! {{index}}
                <div *ngIf="show">
                <div class="popover top" style="display: block;border:1px solid green">
                    <h3 class="popover-title">{{p.title}}</h3>
                    <div class="popover-content">pop up content</div>
                </div>
                </div>
            </div>
            <br>
            <br>
     </div>
  `

person micronyks    schedule 12.04.2016    source источник


Ответы (2)


Я думаю, вы хотите что-то вроде

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div  *ngFor="let p of popovers;let index=index">
            <div class="tower-details"  [(value)]="show[p.title]" style="display: block;border:1px solid green;background-color:orange" >
             Hover Me ! {{index}}
                <div *ngIf="show[p.title]">
                <div class="popover top" style="display: block;border:1px solid green">
                    <h3 class="popover-title">{{p.title}}</h3>
                    <div class="popover-content">pop up content</div>
                </div>
                </div>
            </div>
            <br>
            <br>
     </div>
  `,
    directives: [popover]


})
export class App {

  popovers=[{"title":"popover1"},{"title":"popover2"},{"name":"title"}]
  show={'popover1': false, 'popover2': false, 'title': false};
}

Состояние каждого элемента должно храниться отдельно. Если вы используете одно и то же значение для каждого, они показывают/скрывают синхронность.

Поскольку вы используете двухстороннюю привязку к [(value)]="show", значение каждого элемента будет распространяться на App и обратно на каждый tower-details.

Пример планкера

Обновить

Вы можете экспортировать директиву, например:

@Directive({
    selector: '.tower-details',
    host:{
      '(mouseenter)':'show($event)',
      '(mouseout)':'hide()'
    },
    exportAs: 'tower'
})
export class popover{

затем вы можете создать переменную шаблона для ссылки на значение в директиве

        <div class="tower-details" #tower="tower" style="display: block;border:1px solid green;background-color:orange" >
         Hover Me ! {{index}} - {{tower.value}}
            <div *ngIf="tower.value">

Вам также нужно установить value в директиве, а не просто генерировать событие. На самом деле генерировать событие больше не нужно, за исключением того, что вы хотите привязать его к другим средствам.

   show(val)
   {
     console.log(val.target);
     this.value = true;
     this.valueChange.emit(true);
   }
   hide()
   {
     console.log('hide');
     this.value = false;
     this.valueChange.emit(false);
   }

Пример планкера

person Günter Zöchbauer    schedule 12.04.2016
comment
Гюнтер, что, если у вас нет такого логического свойства, и вы все равно хотите обрабатывать его только через директиву. - person micronyks; 12.04.2016
comment
здесь вы знаете, что у вас есть три объекта, поэтому вы придумали три объекта. Но на самом деле, что если в массиве тысячи объектов. Вы просто не можете пойти по этому пути. Правильно? Я хочу какой-то динамичный способ. например. используйте index, и если мы можем что-то с этим сделать, используя директиву. - person micronyks; 12.04.2016
comment
Вы можете сделать это, как в моем первом подходе, с любым количеством элементов, для этого потребуются лишь незначительные изменения кода. Я думаю, вам все равно больше понравится мой второй подход. - person Günter Zöchbauer; 12.04.2016
comment
2-й подход намного лучше, чем 1, я думаю. Просто супер! - person micronyks; 12.04.2016

На самом деле переменная show, которую вы используете, является глобальной. Вы должны использовать что-то вроде этого:

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div *ngFor="#p of popovers;#index=index">
        <div class="tower-details" (valueChange)="show[index]=$event" style="display: block;border:1px solid green;background-color:orange" >
         Hover Me ! {{index}}
            <div *ngIf="show[index]">
            <div class="popover top" style="display: block;border:1px solid green">
                <h3 class="popover-title">{{p.title}}</h3>
                <div class="popover-content">pop up content</div>
            </div>
            </div>
        </div>
        <br>
        <br>
  </div>
  {{show | json }}
`,
directives: [popover]
})
export class App {
  popovers=[{"title":"popover1"},{"title":"popover2"}, {"name":"title"}]
  show=[false,false,false];
}

См. этот планкр: http://plnkr.co/edit/pIgH4OdMIf7rj2NOw9b6?p=preview .

Изменить

Еще раз подумав, вы можете напрямую использовать состояние примененной директивы, чтобы избежать использования массива show.

@Directive({
  selector: '.tower-details',
  host:{
    '(mouseenter)':'show($event)',
    '(mouseout)':'hide()'
  },
  exportAs: 'popover'
})
export class popover{
  shoudShow: false;

  show(val) {
    this.shoudShow = true;
  }

  hide() {
    this.shoudShow = false;
  }
}

И так в компоненте:

@Component({
  selector: 'my-app',
  template: `
    <div  *ngFor="#p of popovers;#index=index">
        <div class="tower-details" #dir="popover" style="display: block;border:1px solid green;background-color:orange" >
         Hover Me ! {{index}}
            <div *ngIf="dir.shoudShow">
            <div class="popover top" style="display: block;border:1px solid green">
                <h3 class="popover-title">{{p.title}}</h3>
                <div class="popover-content">pop up content</div>
            </div>
            </div>
        </div>
        <br>
        <br>
   </div>
  `,
  directives: [popover]
})
export class App {
  popovers=[{"title":"popover1"},{"title":"popover2"}, {"name":"title"}]
}

См. этот новый планкр: http://plnkr.co/edit/4Ewx15fYgm8AgejWZmXl?p=preview.

person Thierry Templier    schedule 12.04.2016
comment
здесь вы знаете, что у вас есть три объекта, поэтому вы придумали три объекта. Но на самом деле, что если в массиве тысячи объектов. Вы просто не можете пойти по этому пути. Правильно? Я хочу какой-то динамичный способ. например. используйте index, и если мы можем что-то с этим сделать, используя директиву. - person micronyks; 12.04.2016
comment
На самом деле, массивы, конечно, могут создаваться динамически. Я подумал о другом решении ;-) Я соответственно обновил свой ответ... - person Thierry Templier; 12.04.2016