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) {}
}
źródło
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.new
). Czy to nie zadzwoni konstruktor modelu. I toinstanceOf Model
będzie fałsznew
poprzedniego dnia i lubię ją w prostych przypadkach, takich jak ten.angular-cli
, możesz zadzwonićng g class model
i wygeneruje to za Ciebie.model
zastąpione jakimkolwiek nazewnictwem, jakiego pragniesz.Problem polega na tym, że nie dodałeś
Model
ani dobootstrap
(co spowoduje, że będzie to singleton), ani doproviders
tablicy 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ć
Model
powyż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() {} }
źródło
providers
tablicy?No Provider for Model
jeśli nie dodam go do tablicy dostawcówexport
przedModel
zajęciami?W twoim przypadku masz model na tej samej stronie, ale masz go zadeklarowany po klasie Component, więc musisz go użyć,
forwardRef
aby się do niego odwołaćClass
. Nie preferuj tego, zawsze miejmodel
obiekt 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
Lepszą rzeczą jest to, że możesz
Model
zdefiniować swoją klasę w innym pliku, a następnie zaimportować ją jako plik, gdy tego potrzebujesz. Miej teżexport
przed sobą nazwę klasy, abyś mógł ją zaimportować.import { Model } from './model';
źródło
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>
źródło
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));
źródło
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 cl
jako skrótu w zależności od twojej wersji angular-cli.źródło
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.
źródło
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.
źródło