* ngIf і * ngДля одного елемента, що викликає помилку


473

У мене виникають проблеми із спробою використання 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якщо колекція порожнім, або мій liзагорнутий у зайві елементи контейнера.

Приклад на цьому plnkr .

Зверніть увагу на помилку консолі:

EXCEPTION: TypeError: Cannot read property 'name' of null in [{{thing.name}} in ShellComponent@5:12]

Я щось роблю неправильно чи це помилка?


stackoverflow.com/questions/40529537/… я б пішов з ng-контейнер
Роберт Кінг

Відповіді:


680

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>до Angular v4) дозволяє робити те саме, але з іншим синтаксисом, який заплутаний і більше не рекомендується

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

5
Дуже дякую. Дивно все ще недокументовано: github.com/angular/angular.io/isissue/2303
Алекс Фуентес

7
Як буде виглядати код, коли нам доведеться мати * ngIf всередині * ngFor? Тобто умова IF буде заснована на значенні елемента циклу.
Юврай Патіль

22
Просто поставте ngForна <ng-container>елемент і ngIfна <div>. Ви також можете мати два вкладені <ng-container>обгортки <div>. <ng-container>це лише допоміжний елемент, який не буде додано до DOM.
Günter Zöchbauer

3
Я б запропонував використовувати <ng-container>. Він поводиться так само, як, <template>але дозволяє використовувати "звичайний" синтаксис для структурних директив.
Гюнтер Зехбауер

2
Документація говорить : "Одна структурна директива на хост-елемент": "Існує просте рішення для цього випадку використання: покладіть * ngIf на елемент контейнера, який обгортає елемент * ngFor." - просто повторюю
heringer

71

Як усі зазначали, навіть якщо наявність декількох директив шаблону в одному елементі працює в кутовій 1.x, це не дозволяється в куті 2. Ви можете отримати більше інформації тут: https://github.com/angular/angular/isissue/ 7315

2016 кутова версія 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>

29

Як згадував @Zyzle і @ Günter в коментарі ( https://github.com/angular/angular/isissue/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>

Це рішення також не вводить зайвих / зайвих контейнерних елементів.


1
Я не впевнений, чому це не прийнята відповідь. <template>це спосіб додати батьківський елемент, який не відображатиметься у висновку.
Еван Плейс

8

в html:

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

в css:

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

5

Ви не можете мати один ngForі ngIfтой же елемент. Що ви можете зробити, це затриматися від заповнення масиву, який ви використовуєте, ngForдоки не натиснеться перемикач у вашому прикладі.

Ось основний (не чудовий) спосіб, як ви могли це зробити: http://plnkr.co/edit/Pylx5HSWIZ7ahoC7wT6P


Чому він не може мати обох?
Допрацюйте,

1
Тут дискусія навколо github.com/angular/angular/isissue/4792
Zyzle

1
Я знаю, чому це відбувається, це просто покращити якість відповіді, однозначно кажучи, you can'tце насправді не найкраща відповідь, не згоден ти?
maurycy

Звичайно, їх не слід використовувати разом лише тому, що наведення їх у певному порядку до шаблону не гарантує, що вони будуть виконані в тому ж порядку. Але це не пояснює, що саме відбувається, коли "Неможливо прочитати властивість" name "з нуля".
колба Есту

І * ngFor, і * ngIf (зірочкою) є структурними директивами, і вони генерують тег <template>. Структурні директиви, як ngIf, роблять свою магію за допомогою тегу шаблону HTML 5.
Pardeep Jain

5

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

Одна структурна директива на хост-елемент

Колись ви захочете повторити блок HTML, але лише тоді, коли певна умова справджується. Ви спробуєте поставити і * ngFor, і * ngIf на один хост-елемент. Кутова не дозволить вам. Ви можете застосувати лише одну структурну директиву до елемента.

Причина - простота. Структурні директиви можуть робити складні речі з елементом господаря та його нащадками. Коли дві директиви пред'являють претензії до одного і того ж елемента хоста, яка з них має перевагу? Що повинно бути першим, NgIf або NgFor? Чи може NgIf скасувати дію NgFor? Якщо так (і, схоже, так і має бути), як Angular узагальнити можливість скасування для інших структурних директив?

На ці запитання немає простих відповідей. Заборона декількох структурних директив змушує їх суперечити. Існує просте рішення для цього випадку використання: помістіть * ngIf на елемент контейнера, який обгортає елемент * ngFor . Один або обидва елементи можуть бути контейнером ng, тому вам не доведеться вводити додаткові рівні HTML.

Таким чином, ви можете використовувати ng-container(Angular4) як обгортку (буде видалено з дому) або div або span, якщо у вас є клас або деякі інші атрибути, як показано нижче:

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

4

Це буде спрацьовувати, але цей елемент залишиться у DOM.

.hidden {
    display: none;
}

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

Це дуже простий хак для комбінації <select> <option>, який я просто хочу показати відфільтровані елементи замість повного списку
davyzhang

Дуже дякую!
Абхішек Шарма

3

У таблиці нижче наведено лише елементи, які мають набір значень "початківець". Потрібен як *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>

1
Я не вважаю, що <div>всередині столу - це ідея для гусів, особливо коли є кращі альтернативи. Ви перевірили, чи працює таким чином IE, що особливо прискіпливо до елементів у<table>
Günter Zöchbauer

3

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

Тепер, як з angular2 beta 8, ми можемо використовувати *ngIfі *ngForпро той самий компонент дивіться тут .

Чергуйте:

Іноді ми не можемо використовувати теги HTML всередині іншого, наприклад у tr, th( table) або в li( ul). Ми не можемо використовувати інший тег HTML, але нам потрібно виконати певну дію в тій же ситуації, щоб ми могли розмістити тег HTML5 <template>таким чином.

ngДля використання шаблону:

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

ng Якщо використовується шаблон:

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

Більш детальну інформацію про структурні директиви в angular2 див. Тут .



1

Ви також можете використовувати 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

<!-- 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>


li елементи відображаються лише в тому випадку, якщо він має ім'я.
Раджив

3
Як ця відповідь додає значення тут? Це не дає нічого, що не передбачено іншими відповідями, або я щось пропустив?
Günter Zöchbauer

0

Не можна використовувати декілька структурних директив для одного елемента. Вставте свій елемент ng-templateі використовуйте там одну структурну директиву


0

Це можна зробити іншим способом, перевіривши довжину масиву

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

-1

app.component.ts

import {NgModule, Component} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';

@Component({
  selector: 'ngif-example',
  template: `
<h4>NgIf</h4>
<ul *ngFor="let person of people">
  <li *ngIf="person.age < 30"> (1)
  {{ person.name }} ({{ person.age }})
  </li>
</ul>
`
})
class NgIfExampleComponent {

  people: any[] = [
    {
      "name": "yogesh ",
      "age": 35
    },
    {
      "name": "gamesh",
      "age": 32
    },
    {
      "name": " Meyers",
      "age": 21
    },
    {
      "name": " Ellis",
      "age": 34
    },
    {
      "name": " Tyson",
      "age": 32
    }
  ];
}

app.component.html

<ul *ngFor="let person of people" *ngIf="person.age < 30">
  <li>{{ person.name }}</li>
</ul>

Вихідні дані

Meyers(21)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.