angular2:操纵元素外部指令

时间:2016-04-20 20:20:00

标签: typescript angular directive

我正在尝试访问当前指令元素之外的DOM元素...用于类似resize的行为,使用外部元素作为句柄并且不要更改目标标记,特别是不使用{{{ 1}}。
我现在正在做的事情是我认为有点不正统,比如使用<ng-content>

BrowserDomAdapter

模板看起来像这样:

import {Directive, Input, HostListener} from 'angular2/core';
import {BrowserDomAdapter} from 'angular2/platform/browser';

@Directive
({
  selector: '[resizable-handle]',
  providers: [BrowserDomAdapter]
})
export class ResizableHandle
{
  // get selector from actual directive selector attribute
  @Input('resizable-handle') resizableSelector: string;

  constructor(private _domAdapter: BrowserDomAdapter){}

  @HostListener('mousedown', ['$event'])
  startResize(e: MouseEvent)
  {
    this._domAdapter.query(this.resizableSelector); //... manipulate this native element
  }
  //... and so on...
}

3 个答案:

答案 0 :(得分:3)

谢谢kemsky

根据您对模板变量的建议,我设计了这个解决方案:

@Directive
({
  selector: '[resizable-handle]', 
  host: { '(click)': 'emitHeight()' }
})
export class ResizableHandle 
{
  @Input('resizable-handle') resizableSelector: ElementRef;

  @Input() targetHeight: any;
  @Output() targetHeightChange = new EventEmitter<number>();

  emitHeight(){ this.targetHeightChange.emit(100) }
}

@Component
({
  selector: 'my-app',
  directives: [ResizableHandle],
  template: `
    <h1>Hello</h1>
    <a [resizable-handle]="targetContainer" [(targetHeight)]="containerHeight" href="javascript:void(0)"> [click me!] </a>
    <div #targetContainer [style.height.px]="containerHeight"> ... </div>
  `
})
export class AppComponent 
{
  public containerHeight = 25;
} 

Plunker preview

还要感谢Günter Zöchbauer。我使用父指令构建了另一个解决方案;这是因为手柄和目标实际上并不在同一棵树上,所以我需要一个&#34; root&#34;沟通。

@Directive({ selector: '[resizable-handle]' })
export class ResizableHandle 
{
  constructor(@Host() @Inject(forwardRef(()=>Resizable)) private _resizable: Resizable){}
  // simulate dropping a resize handle here
  @HostListener('click', ['$event'])
  emitHeight(){ this._resizable.change = Math.max( Math.random()*100, 25 ); }
}

@Directive({ selector: '[resizable-target]' })
export class ResizableTarget
{
  constructor(@Host() @Inject(forwardRef(()=>Resizable)) private _resizable: Resizable)
  {
    this._resizable.change.subscribe( c => this.height = c );
  }

  @HostBinding('style.height.px') public height;
}

@Directive({ selector: '[resizable]' })
export class Resizable
{
  private _change: number;
  private _observableChange: Observable<number>;

  constructor()
  {
    this._observableChange = Observable.create( observer => this._change = observer ).share();
  }
  set change(value){ this._change.next(value) }
  get change(){ return this._observableChange; }
}

@Component
({
    selector: 'my-app',
    directives: [ResizableHandle, ResizableTarget, Resizable],
    template: `
      <div resizable>
        <a resizable-handle href="javascript:void(0)"> [click me!] </a>
        <p>...</p>
        <div resizable-target> ...a... </div>
        <div resizable-target> ...b... </div>
      </div>
    `,
})
export class AppComponent{}

Plunker preview

答案 1 :(得分:2)

我会使用一个指令来应用于直接在目标上进行实际操作的目标元素。

然后你可以使用构造函数注入它。这总是从最近的父节点注入,它找到这个指令。

@Directive({
  selector: '[resizable-target]',
  host: {'[style.border]': 'border'},
})
export class ResizableTarget {
  // change style just to demonstrate this directive can be manipulated
  border:string = "solid 3px blue";

  @HostBinding('class.reached') reached:boolean = false;
}
@Directive({
  selector: '[resizable-handle]',
})
export class ResizableHandle {
  // inject the target
  constructor(private _target:ResizableTarget) {}

  @HostListener('mousedown', ['$event'])
  startResize(e: MouseEvent) {
    // call methods or set properties in order to manipulate the target
    this._target.reached = true; 
    this._target.border = "solid 3px red";
  }
}
@Component({
    selector: 'my-app',
    directives: [ResizableTarget, ResizableHandle],
    template: `
    <h1>Hello</h1>

    <div resizable-target>
      <div>
        <div> some content
          <div resizable-handle>handle</div>
        </div>
      </div>
    </div>
    `,
})
export class AppComponent {
}

Plunker example

答案 2 :(得分:1)

您可以使用模板变量:

@Input('resizable-handle') resizableSelector: ElementRef;

<a [resizable-handle]="target-container"> ... </a>
<!-- ...somewhere further, on a different level, the target I don't want to touch in order to get this working... -->
<div #target-container> ... </div>