Angular – jak dynamicznie stworzyć element?

0

Obłaskawiania Angulara ciąg dalszy (po tym problemie).

Tym razem chciałbym stworzyć element. Mógłbym to zrobić co prawda bezpośrednio w szablonie. Jednak przy tworzeniu muszę wykonać parę rzeczy, a dokumentacja Angulara odradza interpolację zbytniej logiki. Tak więc chciałbym jakoś inaczej to zrobić.

Bez Angulara zrobiłbym to tak:

HTML:

<div id="someContainer">
</div>

JavaScript:

const createSomeElement = function() {
  const someElement = document.createElement("DIV");
  const content = "lalala"; // in fact some heavy computations
  const contentElement = document.createTextNode(content);
  someElement.appendChild(contentElement);
  return someElement;
}

const someContainer = document.getElementById("someContainer");
someContainer.appendChild(createSomeElement());

Z Angularem próbowałem tak:

app.component.html:

<!-- Sposób nr 1 -->
<div id="someContainer">
  {{ createSomeElement() }}
</div>

<!-- Sposób nr 2 -->
<div [innerHtml]="createSomeElement()">
</div>

app.component.ts:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  createSomeElement = function() {
    const someElement = document.createElement("DIV");
    const content = "lalala"; // in fact some heavy computations
    const contentElement = document.createTextNode(content);
    someElement.appendChild(contentElement);
    return someElement;
  }
}

Jednak to nie działa, w przypadku obu sposobów wyświetlany jest zamiast "lalala" napis "[object HTMLDivElement]" (zgodnie zresztą z moimi oczekiwaniami).


UPDATE: Zaktualizowałem kod JavaScript w app.component.ts.


PS. Pytam o tak prostą rzecz, ponieważ chcę uniknąć nauki zbyt wielu nowych rzeczy naraz. Wolałbym krok po kroku wprowadzać się w funkcjonalności, które oferuje Angular. Dlatego też proszę o jak najprostsze rozwiązanie.

1

Polecam Renderer2 do takich rzeczy o ile dobrze rozumiem

0

@Kondziowsky: dzięki. Zobaczymy, ale na razie widzę, że nie obejdzie się bez czytania o paru innych rzeczach dot. Angulara, o których jeszcze nie czytałem.

1

Z poziomu js w angular raczej nie powinno się manipulować drzewem DOM.
Oczywiście w zależności co chcesz osiągnąć ale w większości przypadków chyba
wystarczą dyrektywy *ngFor i *ngIf.

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <button (click)="addElement('test')">Add element</button>
    <div id="someContainer">
         <div *ngFor="let element of elements">
              {{ element }}
         </div>
    </div>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  elements: string[] = [];
   
  addElement(element) {
     this.elements.push(element);
  }
}

0

Za pomocą innerHtml możesz to też zrobić przypisując zmienną, a zmienną określić jako string np.

this.myDiv = '<div class="aha"></div>';

Wtedy zadziała.

0

@lookacode1: problem jest taki, że ja nie mam przycisku, tylko element powinien być utworzony automatycznie po uruchomieniu aplikacji.

@Kondziowsky: ale właśnie o to chodzi, że ten element nie ma takiej prostej składni. Jest w nim kilka innych elementów (przynajmniej powinno być teoretycznie, ale zobaczę, jak jeszcze prawdziwy kod zmienię). Dlatego też nie mogę tak przypisać.


UPDATE:

Być może nie zaznaczyłem tego zbyt wyraźnie, wybaczcie. Te "some heavy computations" z przykładu to w rzeczywistości, w teorii, ma być tworzenie paru innych elementów. Ogólnie chodzi mi o to, że mógłbym (?) wszystko wrzucić do szablonu, do interpolacji {{ tutaj heavy computations }}, ale to jest odradzane w dokumentacji. Dlatego szukam innej drogi.

Z tego, co zauważyłem, Angular nie ma czegoś własnego na kształt metody onload w osobnym pliku JS, w której można wrzucić co tylko się chce. Wolałbym nie robić haków i próbować tak, jak Angular pozwala.


UPDATE2: Spróbuję pokazać wersję przykładu bardziej adekwatną do prawdziwego kodu, który chcę mieć.


UPDATE3: Wersja funkcji z przykładu bardziej adekwatna do prawdziwego kodu:

const createSomeElement = function (param1, param2) {
    const internalCreateSomeElement = function (param3, param4) {
      // proper creation of the element
    };

    let resultElement = undefined;

    if (param1.isSometing(param2)) {
      resultElement = internalCreateSomeElement(param1, something1);
    } else if (param1.isSomethingElse(param2)) {
      resultElement = internalCreateSomeElement(param1, something2);
    } else {
      resultElement = ""; // or something similar, I do not know yet
    }

    return resultElement;
};
1

Tworzenie i zarządzanie DOM zostaw dla Angulara. Twoja logika powinna przygotować model, który wykorzystasz w widoku. Np.

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    <div id="container">
      <ng-container *ngFor="let element of elements">
         <div *ngIf="isSomething(element)">{{ element.value }}</div>
         <span *ngIf="isSomethingElse(element)">{{ element.value }}</span>
      </ng-container>
   </div>`
})
export class AppComponent  {

    elements = [
      { param1: 0, param2: 1, value: 'A'},
      { param1: 2, param2: 3, value: 'B'},
      { param1: 4, param2: 5, value: 'C'},
      { param1: 6, param2: 7, value: 'D'},
      { param1: 8, param2: 9, value: 'E' }
  ];

  isSomething(element) {
      return element.param1 % element.param2 == 0;
  }

  isSomethingElse(element) {
      return element.param1 % element.param2 != 0;
  }
}
0

OK, stosując wspomniane podejście (deklaratywne):

  1. logika została okrojona (czytaj: uproszczona, bo nie mam ochoty walczyć dalej na tę chwilę);
  2. działa. :)

Kod wynikowy wygląda tak (nie bazuje na kodzie z przykładu powyżej, tylko na moim wynikowym kodzie):

<ng-template [ngIf]="isSomething1(param1)" [ngIfThen]="template1" [ngIfElse]="template2"></ng-template>
<ng-template #template1>
  <a [href]="getSomething2(param1)">
    {{ getSomething2(param1) }}
  </a>
</ng-template>
<ng-template #template2>
  <p>
    {{ param1 }}
  </p>
</ng-template>

Nie bardzo mi się to podoba, w czystym JavaScripcie jakoś lepiej wyglądało. Przy czym nie mówię tutaj o rozszerzonej składni ngIf, tylko o całości kodu (rozszerzona składnia ngIf swoją drogą wydaje się czytelniejsza niż ta skrócona). To pewnie skutek tego, że jeszcze mało umiem Angulara.

Dziękuję. :)


PS. W zasadzie... nie wyglądało to lepiej w czystym JS. ;) Tylko wyobrażam sobie, że wyglądałoby lepiej, gdybym uznał, że powinienem poświęcić czas na refaktoryzację kodu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1