Jak zadeklarować klasę modelu w składniku Angular 2 przy użyciu języka TypeScript?

82

Jestem nowy w Angular 2 i TypeScript i staram się przestrzegać najlepszych praktyk.

Zamiast używać prostego modelu JavaScript ({}), próbuję utworzyć klasę TypeScript.

Jednak Angular 2 nie wydaje się to lubić.

Mój kod to:

import { Component, Input } from "@angular/core";

@Component({
    selector: "testWidget",
    template: "<div>This is a test and {{model.param1}} is my param.</div>"
})

export class testWidget {
    constructor(private model: Model) {}
}

class Model {
    param1: string;
}

i używam go jako:

import { testWidget} from "lib/testWidget";

@Component({
    selector: "myComponent",
    template: "<testWidget></testWidget>",
    directives: [testWidget]
})

Otrzymuję błąd od Angulara:

WYJĄTEK: nie można rozwiązać wszystkich parametrów dla testWidget: (?).

Pomyślałem więc, że model nie jest jeszcze zdefiniowany ... przeniosę go na górę!

Tylko że teraz mam wyjątek:

ORYGINALNY WYJĄTEK: brak dostawcy dla modelu!

Jak to osiągnąć?

Edycja: dzięki wszystkim za odpowiedź. Poprowadził mnie na właściwą ścieżkę.

Aby wstrzyknąć to do konstruktora, muszę dodać go do dostawców w komponencie.

To wydaje się działać:

import { Component, Input } from "@angular/core";

class Model {
    param1: string;
}

@Component({
    selector: "testWidget",
    template: "<div>This is a test and {{model.param1}} is my param.</div>",
    providers: [Model]
})

export class testWidget {
    constructor(private model: Model) {}
}
Scottie
źródło

Odpowiedzi:

153

Spróbuję tego:

Podziel swój Model na osobny plik o nazwie model.ts:

export class Model {
    param1: string;
}

Zaimportuj go do swojego komponentu. Zapewni to dodatkową korzyść z możliwości użycia go w innych komponentach:

Import { Model } from './model';

Zainicjuj w komponencie:

export class testWidget {
   public model: Model;
   constructor(){
       this.model = new Model();
       this.model.param1 = "your string value here";
   }
}

Uzyskaj do niego odpowiedni dostęp w html:

@Component({
      selector: "testWidget",
      template: "<div>This is a test and {{model.param1}} is my param.</div>"
})

Chcę dodać do odpowiedzi komentarz @PatMigliaccio, ponieważ ważne jest, aby dostosować się do najnowszych narzędzi i technologii:

Jeśli korzystasz angular-cli, możesz zadzwonić ng g class modeli wygeneruje to za Ciebie. model zostanie zastąpiony dowolnym nazewnictwem, które chcesz.

Brendon Colburn
źródło
1
Interesujące ... Jeśli spróbuję zrobić skrót od konstruktora (model prywatny: Model), pojawia się błąd informujący o braku dostawcy. Jeśli jednak zdefiniuję go jako model prywatny: Model = new Model (), to działa. Dlaczego to?
Scottie,
7
Nie jestem architektem Angular 2, ale na podstawie mojego doświadczenia z Angularem, kiedy wprowadzasz coś przez konstruktora, sugerujesz, że jest wstrzykiwany. Wstrzykiwanie wymaga, aby dodać go do @Component jako dostawcy takich jak: providers: [Model]. Również zgodnie z demo Angular 2 Tour of Hero powinieneś używać go jako właściwości zamiast wstrzykiwania, ponieważ ta funkcja jest zwykle zarezerwowana dla bardziej złożonych klas, takich jak usługi.
Brendon Colburn
1
Problem ze sposobem utworzenia instancji modelu (niestosowania new). Czy to nie zadzwoni konstruktor modelu. I to instanceOf Modelbędzie fałsz
Poul Kruijt
Ale nie było od czego konstruktora? Nie widzę w tym większego problemu w tej implementacji. Jeśli sprawy staną się bardziej złożone, to na pewno. Jednak wciąż się uczę. Właśnie nauczyłem się tej skróconej metody nie używania newpoprzedniego dnia i lubię ją w prostych przypadkach, takich jak ten.
Brendon Colburn
5
Jeśli korzystasz angular-cli, możesz zadzwonić ng g class modeli wygeneruje to za Ciebie. modelzastąpione jakimkolwiek nazewnictwem, jakiego pragniesz.
Pat Migliaccio
16

Problem polega na tym, że nie dodałeś Modelani do bootstrap(co spowoduje, że będzie to singleton), ani do providerstablicy definicji twojego komponentu:

@Component({
    selector: "testWidget",
    template: "<div>This is a test and {{param1}} is my param.</div>",
    providers : [
       Model
    ]
})

export class testWidget {
    constructor(private model: Model) {}
}

I tak, powinieneś zdefiniować ModelpowyżejComponent . Ale lepiej byłoby umieścić to w swoim własnym pliku.

Ale jeśli chcesz, aby była to tylko klasa, z której możesz tworzyć wiele instancji, lepiej po prostu użyj new.

@Component({
    selector: "testWidget",
    template: "<div>This is a test and {{param1}} is my param.</div>"
})

export class testWidget {

    private model: Model = new Model();

    constructor() {}
}
Poul Kruijt
źródło
1
Model to tylko klasa, import powinien działać idealnie. dlaczego potrzebujemy go w providerstablicy?
Pankaj Parkar
Tak, ale jego szablony nie zadziałałyby tutaj, byłby to model.param1. Nie nadał mu też wartości początkowej?
Brendon Colburn,
@PankajParkar Ponieważ nadal dostaję, No Provider for Modeljeśli nie dodam go do tablicy dostawców
Poul Kruijt
@PierreDuc masz exportprzed Modelzajęciami?
Pankaj Parkar
@PankajParkar spójrz tutaj
Poul Kruijt
6

W twoim przypadku masz model na tej samej stronie, ale masz go zadeklarowany po klasie Component, więc musisz go użyć, forwardRefaby się do niego odwołać Class. Nie preferuj tego, zawsze miej modelobiekt w oddzielnym pliku.

export class testWidget {
    constructor(@Inject(forwardRef(() => Model)) private service: Model) {}
}

Dodatkowo musisz zmienić interpolację widoku, aby odnosić się do właściwego obiektu

{{model?.param1}}

Lepszą rzeczą jest to, że możesz Modelzdefiniować swoją klasę w innym pliku, a następnie zaimportować ją jako plik, gdy tego potrzebujesz. Miej też exportprzed sobą nazwę klasy, abyś mógł ją zaimportować.

import { Model } from './model';
Pankaj Parkar
źródło
@BrendonColburn dzięki stary, widziałem to w twojej odpowiedzi. Pomyślałem więc nie ma sensu edytować moje aftewards odpowiedź, dzięki człowiek na głowach się jednak okrzyki :)
Pankaj Parkar
5

mój kod to

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

class model {
  username : string;
  password : string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})



export class AppComponent {

 username : string;
 password : string;
  usermodel = new model();

  login(){
  if(this.usermodel.username == "admin"){
    alert("hi");
  }else{
    alert("bye");
    this.usermodel.username = "";
  }    
  }
}

a html wygląda tak:

<div class="login">
  Usernmae : <input type="text" [(ngModel)]="usermodel.username"/>
  Password : <input type="text" [(ngModel)]="usermodel.password"/>
  <input type="button" value="Click Me" (click)="login()" />
</div>
Ahmed Basha
źródło
4
export class Car {
  id: number;
  make: string;
  model: string;
  color: string;
  year: Date;

  constructor(car) {
      {
        this.id = car.id;
        this.make = car.make || '';
        this.model = car.model || '';
        this.color = car.color || '';
        this.year = new Date(car.year).getYear();
      }
  }
}

|| może stać się bardzo przydatny w przypadku bardzo złożonych obiektów danych do domyślnych danych, które nie istnieją.

. .

W pliku component.ts lub service.ts możesz deserializować dane odpowiedzi do modelu:

// Import the car model
import { Car } from './car.model.ts';

// If single object
car = new Car(someObject);

// If array of cars
cars = someDataToDeserialize.map(c => new Car(c));
Mike Hawes
źródło
3

Możesz użyć angular-cli, jak sugerują komentarze w odpowiedzi @ brendon.

Możesz także spróbować:

ng g class modelsDirectoy/modelName --type=model

/* will create
 src/app/modelsDirectoy
 ├── modelName.model.ts
 ├── ...
 ...
*/

Pamiętaj ng g class :! == ng g c
Jednak możesz użyć ng g cljako skrótu w zależności od twojej wersji angular-cli.

user9869932
źródło
0

Zdaję sobie sprawę, że jest to nieco starsze pytanie, ale chciałem tylko zaznaczyć, że nieprawidłowo dodałeś zmienną modelu do klasy widżetu testowego. Jeśli potrzebujesz zmiennej modelu, nie powinieneś próbować przekazywać jej przez konstruktor komponentu. Masz zamiar przekazywać w ten sposób tylko usługi lub inne typy elementów iniekcyjnych. Jeśli tworzysz wystąpienie widgetu testowego w innym komponencie i musisz przekazać obiekt modelu jako, polecam użycie wzorców projektowych OnInit i Input / Output z rdzeniem kątowym.

Na przykład Twój kod powinien naprawdę wyglądać mniej więcej tak:

import { Component, Input, OnInit } from "@angular/core";
import { YourModelLoadingService } from "../yourModuleRootFolderPath/index"

class Model {
    param1: string;
}

@Component({
    selector: "testWidget",
    template: "<div>This is a test and {{model.param1}} is my param.</div>",
    providers: [ YourModelLoadingService ]
})

export class testWidget implements OnInit {
    @Input() model: Model; //Use this if you want the parent component instantiating this
        //one to be able to directly set the model's value
    private _model: Model; //Use this if you only want the model to be private within
        //the component along with a service to load the model's value
    constructor(
        private _yourModelLoadingService: YourModelLoadingService //This service should
        //usually be provided at the module level, not the component level
    ) {}

    ngOnInit() {
        this.load();
    }

    private load() {
        //add some code to make your component read only,
        //possibly add a busy spinner on top of your view
        //This is to avoid bugs as well as communicate to the user what's
        //actually going on

        //If using the Input model so the parent scope can set the contents of model,
        //add code an event call back for when model gets set via the parent
        //On event: now that loading is done, disable read only mode and your spinner
        //if you added one

        //If using the service to set the contents of model, add code that calls your
        //service's functions that return the value of model
        //After setting the value of model, disable read only mode and your spinner
        //if you added one. Depending on if you leverage Observables, or other methods
        //this may also be done in a callback
    }
}

Klasa, która jest po prostu strukturą / modelem, nie powinna być wstrzykiwana, ponieważ oznacza to, że możesz mieć tylko jedną współdzieloną instancję tej klasy w zakresie, w jakim została dostarczona. W tym przypadku oznacza to, że za każdym razem, gdy tworzona jest instancja testWidget, przez wstrzykiwacz zależności jest tworzone pojedyncze wystąpienie modelu. Gdyby został dostarczony na poziomie modułu, miałbyś tylko jedną instancję współużytkowaną przez wszystkie komponenty i usługi w tym module.

Zamiast tego powinieneś postępować zgodnie ze standardowymi praktykami zorientowanymi obiektowo i tworzyć prywatną zmienną modelu jako część klasy, a jeśli potrzebujesz przekazać informacje do tego modelu podczas tworzenia instancji, powinno to być obsługiwane przez usługę (wstrzykiwalną) zapewnianą przez moduł nadrzędny. W ten sposób zarówno wstrzykiwanie zależności, jak i komunikacja mają być wykonywane w trybie kątowym.

Ponadto, jak wspomniano wcześniej, należy zadeklarować klasy modelu w oddzielnym pliku i zaimportować klasę.

Zdecydowanie polecam powrót do dokumentacji dotyczącej kątów i przejrzenie podstawowych stron dotyczących różnych adnotacji i typów zajęć: https://angular.io/guide/architecture

Należy zwrócić szczególną uwagę na sekcje poświęcone modułom, składnikom i usługom / iniekcji zależności, ponieważ są one niezbędne do zrozumienia, jak używać Angular na poziomie architektonicznym. Angular to język o bardzo ciężkiej architekturze, ponieważ jest na tak wysokim poziomie. Rozdzielanie problemów, fabryki wstrzykiwania zależności i wersjonowanie javascript w celu zapewnienia porównywalności przeglądarek są obsługiwane głównie za Ciebie, ale musisz poprawnie używać architektury ich aplikacji, inaczej okaże się, że rzeczy nie działają zgodnie z oczekiwaniami.

user2904660
źródło
0

utwórz model.ts w katalogu komponentów, jak poniżej

export module DataModel {
       export interface DataObjectName {
         propertyName: type;
        }
       export interface DataObjectAnother {
         propertyName: type;
        }
    }

następnie w imporcie komponentu powyżej jako, zaimportuj {DataModel} z './model';

export class YourComponent {
   public DataObject: DataModel.DataObjectName;
}

Twój DataObject powinien mieć wszystkie właściwości z DataObjectName.

Prasad
źródło