it-swarm.com.ru

* ngIf и * ngFor для одного и того же элемента, вызывающего ошибку

У меня проблема с попыткой использовать Angular *ngFor и *ngIf на одном элементе.

При попытке перебрать коллекцию в *ngFor, коллекция воспринимается как null и, следовательно, происходит сбой при попытке доступа к ее свойствам в шаблоне.

@Component({
  selector: 'Shell',
  template: `
    <h3>Shell</h3><button (click)="toggle()">Toggle!</button>

    <div *ngIf="show" *ngFor="let thing of stuff">
      {{log(thing)}}
      <span>{{thing.name}}</span>
    </div>
  `
})

export class ShellComponent implements OnInit {

  public stuff:any[] = [];
  public show:boolean = false;

  constructor() {}

  ngOnInit() {
    this.stuff = [
      { name: 'abc', id: 1 },
      { name: 'huo', id: 2 },
      { name: 'bar', id: 3 },
      { name: 'foo', id: 4 },
      { name: 'thing', id: 5 },
      { name: 'other', id: 6 },
    ]
  }

  toggle() {
    this.show = !this.show;
  }

  log(thing) {
    console.log(thing);
  }

}

Я знаю, что простое решение состоит в том, чтобы переместить *ngIf вверх на уровень, но для таких сценариев, как зацикливание элементов списка в ul, я получу либо пустое li, если коллекция пуста, либо мои lis обернуты в избыточные элементы контейнера.

Пример на это plnkr .

Обратите внимание на ошибку консоли:

EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in [email protected]:12]

Я что-то не так делаю или это ошибка?

358
garethdn

Angular v2 не поддерживает более одной структурной директивы для одного и того же элемента.
В качестве обходного пути используйте элемент<ng-container>, который позволяет вам использовать отдельные элементы для каждой структурной директивы, но он не помечен как DOM .

<ng-container *ngIf="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</ng-container>

<ng-template> (<template> before Angular v4) позволяет делать то же самое, но с другим синтаксисом, который сбивает с толку и больше не рекомендуется 

<ng-template [ngIf]="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</ng-template>
479
Günter Zöchbauer

Как все отметили, несмотря на то, что наличие нескольких шаблонных директив в одном элементе работает в Angular 1.x, это не разрешено в Angular 2. Вы можете найти больше информации здесь: https://github.com/angular/угловой/проблемы/7315

2016 Angular 2 бета

решением является использование <template> в качестве заполнителя, поэтому код выглядит так

<template *ngFor="let nav_link of defaultLinks"  >
   <li *ngIf="nav_link.visible">
       .....
   </li>
</template>

но по какой-то причине выше не работает в 2.0.0-rc.4 в этом случае вы можете использовать это 

<template ngFor let-nav_link [ngForOf]="defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</template>

Обновленный ответ 2018

С обновлениями, прямо сейчас в 2018 году угловой v6 рекомендуют использовать <ng-container> вместо <template>

так вот обновленный ответ.

<ng-container *ngFor="let nav_link of defaultLinks" >
   <li *ngIf="nav_link.visible">
       .....
   </li> 
</ng-container>
37
imal hasaranga perera

Как упоминалось @Zyzle и @ Günter в комментарии ( https://github.com/angular/angular/issues/7315 ), это не поддерживается.

С 

<ul *ngIf="show">
  <li *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </li>
</ul>

нет пустых элементов <li>, когда список пуст. Даже элемент <ul> не существует (как и ожидалось).

Когда список заполнен, лишних элементов контейнера нет.

Обсуждение github (4792) , о котором @Zyzle упомянул в своем комментарии, также представляет другое решение с использованием <template> (ниже я использую вашу оригинальную разметку - с использованием <div>s):

<template [ngIf]="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</template>

Это решение также не содержит каких-либо дополнительных/избыточных элементов контейнера.

27
Mark Rajcok

У меня есть еще одно решение.

Используйте [hidden] вместо *ngIf

<div [hidden]="!show" *ngFor="let thing of stuff">

Разница в том, что *ngIf удалит элемент из DOM, в то время как [hidden] фактически играет со стилем CSS, установив display: none

27
Ajmal sha

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

Вот основной (не очень) способ, которым вы могли бы это сделать: http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P

4
Zyzle

В приведенной ниже таблице перечислены только те элементы, для которых задано значение «новичок» . Требуется *ngFor и *ngIf для предотвращения появления нежелательных строк в html.

Первоначально имел *ngIf и *ngFor в одном и том же теге <tr>, но не работает . Добавлен <div> для цикла *ngFor и помещен *ngIf в тег <tr>, работает как положено.

<table class="table lessons-list card card-strong ">
  <tbody>
  <div *ngFor="let lesson of lessons" >
   <tr *ngIf="lesson.isBeginner">
    <!-- next line doesn't work -->
    <!-- <tr *ngFor="let lesson of lessons" *ngIf="lesson.isBeginner"> -->
    <td class="lesson-title">{{lesson.description}}</td>
    <td class="duration">
      <i class="fa fa-clock-o"></i>
      <span>{{lesson.duration}}</span>
    </td>
   </tr>
  </div>
  </tbody>

</table>
3
charliebear240

Это будет работать, но элемент все еще будет в DOM.

.hidden{
    display: none;
}

<div [class.hidden]="!show" *ngFor="let thing of stuff">
  {{log(thing)}}
  <span>{{thing.name}}</span>
</div>
3
ronald8192

Обновлено до angular2 beta 8

Теперь, начиная с angular2 beta 8, мы можем использовать *ngIf и *ngFor для одного и того же компонента см. Здесь .

Alternate:

Иногда мы не можем использовать теги HTML внутри другого, как в tr, th (table) или в li (ul). Мы не можем использовать другой тег HTML, но мы должны выполнить какое-то действие в той же ситуации, чтобы мы могли использовать тег функции HTML5 <template> таким образом.

ngДля использования шаблона:

<template ngFor #abc [ngForOf]="someArray">
    code here....
</template>

если использовать шаблон:

<template [ngIf]="show">
    code here....
</template>    

Для получения дополнительной информации о структурных директивах в angular2 см. Здесь .

2
Pardeep Jain

Вы не можете использовать более одного Structural Directive в Angular для одного и того же элемента, это приводит к путанице и структуре, поэтому вам нужно применять их в 2 отдельных вложенных элемента (или вы можете использовать ng-container), прочитайте это утверждение из команды Angular:

Одна структурная директива на элемент Host

Когда-нибудь вы захотите повторить блок HTML, но только когда особое условие верно. Вы попробуете поставить как * ngFor , так и * ngIf на том же элементе Host. Angular не позволит вам. Ты можешь применять только одну структурную директиву к элементу.

Причина простота. Структурные директивы могут делать сложные вещи с элементом Host и его потомками. Когда две директивы лежат претендовать на тот же элемент Host, какой из них имеет приоритет? Который должен идти первым, NgIf или NgFor? Может ли NgIf отменить эффект из NgFor? Если так (и кажется, что так и должно быть), то как Угловой обобщить возможность отмены для других структурных директивы?

На эти вопросы нет простых ответов. Запрещающие множественные структурные директивы делают их спорными. Есть простое решение для этот вариант использования: поместите * ngIf на элемент контейнера, который упаковывает * ngFor element. Один или оба элемента могут быть ng-контейнером поэтому вам не нужно вводить дополнительные уровни HTML.

Таким образом, вы можете использовать ng-container (Angular4) в качестве оболочки (будет удален из dom) или div или span, если у вас есть class или некоторые другие атрибуты, как показано ниже:

<div class="right" *ngIf="show">
  <div *ngFor="let thing of stuff">
    {{log(thing)}}
    <span>{{thing.name}}</span>
  </div>
</div>
2
Alireza
<div *ngFor="let thing of show ? stuff : []">
  {{log(thing)}}
  <span>{{thing.name}}</span>
</div>
1
Prashant Borde

<!-- Since angular2 stable release multiple directives are not supported on a single element(from the docs) still you can use it like below -->


<ul class="list-group">
                <template ngFor let-item [ngForOf]="stuff" [ngForTrackBy]="trackBy_stuff">
                    <li *ngIf="item.name" class="list-group-item">{{item.name}}</li>
                </template>
   </ul>

0
Rajiv

в формате HTML:

<div [ngClass]="{'disabled-field': !show}" *ngFor="let thing of stuff">
    {{thing.name}}
</div>

в css:

.disabled-field {
    pointer-events: none;
    display: none;
}
0
mojtaba ramezani

Вы не можете использовать несколько структурных директив для одного элемента. Оберните ваш элемент в ng-template и используйте там одну структурную директиву

0
Pradip Patil

Вы также можете использоватьng-template(вместо шаблона. См. Примечание для предостережения об использовании тега шаблона) для применения обоих * ngFor и ngIf к одному и тому же элементу HTML. Вот пример, где вы можете использовать и * ngIf, и * ngFor для того же элемента tr в угловой таблице.

<tr *ngFor = "let fruit of fruiArray">
    <ng-template [ngIf] = "fruit=='Apple'>
        <td> I love apples!</td>
    </ng-template>
</tr>

где fruiArray = ['Apple', 'banana', 'mango', 'pineapple'].

Заметка:

Предостережение об использовании только тега template вместоng-templateзаключается в том, что в некоторых местах он генерирует StaticInjectionError.

0
Steffi Keran Rani J