Angular 2 bootstrap-selectpicker动态数据和ngFor

时间:2017-04-22 22:29:50

标签: angular

我在我的项目中使用bootstrap-selectpicker,当我尝试动态放置数据时,下拉列表不起作用。我认为selectpicker()过早被调用。我希望在获取数据后调用它。

export class MyComponent implements AfterViewInit {
   accounts = Observer<Account[]>;

   // Account service is just a http service returning Observer with accounts.
   constructor(private accountService : AccountService) {
       this.accounts = this.accountService.getAccounts();
   }

   ngAfterViewInit() {
       $('.selectpicker').selectpicker();
   }
}

模板:

<select class="selectpicker" multiple>
    <option *ngFor="let account of accounts | async" >{{account.username}}</option>             
</select>

只有在我使用预定义列表时才有效:

<select class="selectpicker" multiple>
    <option>One</option>             
    <option>two</option>
    <option>three</option>     
</select>

这是我的AccountService:

@Injectable()
export class AccountService {
    constructor(private http: Http) { }

    // TODO: Error handling
    getAccounts() : Observable<Account[]> {
        return this.http.get("/api/accounts").map(response => response.json());
    }
}

4 个答案:

答案 0 :(得分:2)

虽然您的技术可以正常运行,但由于ngAfterViewChecked(顾名思义)在每次更改检测运行期间都会调用,因此存在此技术存在潜在性能问题的风险。这是因为无论您的列表是否实际更改过,都会调用selectpicker('refresh')函数。

解决此问题的一种方法是稍微调整您的observable,以便仅在您的服务返回新的帐户列表时触发selectpicker('refresh')函数。

constructor(private accountService : AccountService) { }

ngOnInit() {

   $('.selectpicker').selectpicker(); // <-- i don't know if this first call is necessary before the version with 'refresh' parameter will work - try removing it
   this.accounts = this.accountService.getAccounts().do(() => {
     setTimeout(() => {
       $('.selectpicker').selectpicker('refresh');
   }, 150);
   });
}

<强>解释

基本上,关键是do运算符,它允许您在从observable发出新值时运行一些副作用代码,而不会更改来自observable的值。因此,使用此选项可以使用每个新的帐户列表运行刷新功能。我还将您的逻辑移动到ngOnInit - 您应该将构造函数逻辑限制为通过依赖注入简单地初始化属性。最后,您需要将selectpicker('refresh')调用包装在setTimeout中,以便在尝试在选择选择器的DOM上查找新元素之前运行更改检测(以及要更新的DOM)。确切的超时延迟与确保使用setTimeout一样重要,因此在更改检测更新DOM后,selectpicker('refresh')调用将在事件循环的单独刻度中运行。

要说清楚,我真的怀疑现有技术的性能损失是值得关注的,但上述技术可以添加到您的工具箱中,以应对性能可能会受到影响的情况。

答案 1 :(得分:1)

您可以使用主题。

let subject = new Rx.Subject();

constructor(private accountService : AccountService) {
  this.accountService.getAccounts().subscribe(subject);
  this.accounts = subject.asObservable();
}

 ngAfterViewInit() {
    subject.asObservable()
     .subscribe(x=> {
        $('.selectpicker').selectpicker();
    });
 }

<select class="selectpicker" multiple>
    <option *ngFor="let account of accounts | async" >
   {{account.username}}</option>             
 </select>

答案 2 :(得分:0)

我想我可以在动态更新selectpicker后调用selectpicker('refresh')。到目前为止,这是解决此问题的最简单方法。

ngAfterViewInit() {
    $('.selectpicker').selectpicker();
}

ngAfterViewChecked() {
    $('.selectpicker').selectpicker('refresh');
}

我通过查看React here的包装器库找到了它。

编辑:此方法似乎第一眼就能正常工作,但它没有,它会在您尝试检查选择框中的某个项目时刷新,并且不允许选择更多项目。对于工作解决方案,请参阅snorkpete的anwser。

答案 3 :(得分:0)

只需在ngIf="accounts"标记的上方<div>中添加<select>,就像这样:

<div *ngIf="accounts">
   <select class="selectpicker" multiple>
      <option *ngFor="let account of accounts" >{{account.username}}</option>             
   </select>
</div>