Angular 5 - 多选而不按 Ctrl 键

Angular 5 - Multiselect without pressing the Ctrl key

我正在处理一个表单,我正在使用 Angular 响应式表单。 我需要在我的表单中包含 multi-select,我确实通过按 ctrl 和 selecting 多个选项来完成它。 我希望能够在不按 ctrl 键的情况下使用 multiselect。 有可能吗?

表单组件:

this.productForm = new FormGroup({
  'items': new FormControl(this.product.items, Validators.required),
});

模板:

<div class="form-group">
  <select multiple="multiple" class="form-control" id="items" formControlName="items" required>
    <option *ngFor="let item of itemList" [value]="item.id">{{item.name}}</option>
  </select>
</div>

更新:

我尝试了 greg95000 的解决方案,但它只是一个部分有效的解决方案。

<div class="form-group">
  <select multiple="multiple" class="form-control" id="items" formControlName="items" required>
    <option (mousedown)="onMouseDown($event)" (mousemove)="$event.preventDefault()" *ngFor="let item of items" [value]="item.id">{{item.name}}</option>
  </select>
</div>


public onMouseDown(event: MouseEvent) {
  event.preventDefault();
  event.target['selected'] = !event.target['selected'];
}

此解决方案打破了数据绑定

我不知道这是否有帮助,但也许您可以使用 JQuery 来做到这一点。 请参考这个回答:

这是JQuery中的代码:

$("select").mousedown(function(e){
   e.preventDefault();

   var select = this;
   var scroll = select .scrollTop;

   e.target.selected = !e.target.selected;

   setTimeout(function(){select.scrollTop = scroll;}, 0);

   $(select ).focus();
}).mousemove(function(e){e.preventDefault()});

完整 Javascript:

element.onmousedown= function(event) {
    //this == event.target
    event.preventDefault();
    var scroll_offset= this.parentElement.scrollTop;
    this.selected= !this.selected;
    this.parentElement.scrollTop= scroll_offset;
}
element.onmousemove= function(event) {
    event.preventDefault();
}

查看父元素(select 框)并记录 selecting/deselecting 选项之前的垂直滚动偏移量。然后在执行操作后手动重置它。

阻止 mousemove 事件的默认行为背后的原因是,如果您不阻止它并且您在移动鼠标时碰巧单击了一个选项,所有选项都将被取消selected。

更新

你可以试试这个解决方案,我不知道是否有更好的解决方案,但我的有效。将您的 onMouseDown 方法更改为:

  public onMouseDown(event: MouseEvent, item) {
    event.preventDefault();
    event.target['selected'] = !event.target['selected'];
    if(event.target['selected']) {
      this.productForm.value.items.push(item.id);
    } else {
      let index: number = -1;
      index = this.productForm.value.items.indexOf(item.id);
      if(index > -1) {
          this.productForm.value.items.splice(index);
      }
    }
  }

我认为您无法使用这种 selector 获得完全原生的绑定数据(因为 preventDefault)。 当然,您必须将 html 更改为:

<div class="form-group">
    <select multiple="multiple" class="form-control" id="items" formControlName="items" required>
      <option (mousedown)="onMouseDown($event, item)" (mousemove)="$event.preventDefault()" *ngFor="let item of itemList" [value]="item.id">{{item.name}}</option>
    </select>
</div>

我有一个有效的解决方案。因为我复制数组 angular 知道有变化所以 select 得到更新。 这是我的 select:

 <select class="form-control" multiple name="positionTypes" [(ngModel)]="freelancer.positionTypes" #select1>
  <option *ngFor="let positionType of positionTypes" [value]="positionType" (mousedown)="false" 
   (click)="positionTypMouseDown($event)">{{'PositionType.'+positionType | translate}}</option> 
 </select>

这是我组件中的函数:

positionTypMouseDown( event: any ) {
    event.stopPropagation();
    let scrollTop = 0;
    if ( event.target.parentNode ) {
        scrollTop = event.target.parentNode.scrollTop;
    }
    const stringValue = event.target.value.split( '\'' )[1];
    const index = this.freelancer.positionTypes.indexOf( stringValue, 0 );
    if ( index > -1 ) {
        this.freelancer.positionTypes.splice( index, 1 );
    } else {
        this.freelancer.positionTypes.push( stringValue );
    }
    // to make angular aware there is something new
    const tmp = this.freelancer.positionTypes;
    this.freelancer.positionTypes = [];
    for ( let i = 0; i < tmp.length; i++ ) {
        this.freelancer.positionTypes[i] = tmp[i];
    }
    setTimeout(( function() { event.target.parentNode.scrollTop = scrollTop; } ), 0 );
    setTimeout(( function() { event.target.parentNode.focus(); } ), 0 );
    return false;
}

我现在更新了我的答案,它在 chrome 中也能正常工作。重要的部分是焦点和滚动不关注选项而是关注 select(即 event.target.parentNode