Wie deklariere ich eine Modellklasse in meiner Angular 2-Komponente mit TypeScript?

81

Ich bin neu in Angular 2 und TypeScript und versuche, Best Practices zu befolgen.

Anstatt ein einfaches JavaScript-Modell ({}) zu verwenden, versuche ich, eine TypeScript-Klasse zu erstellen.

Angular 2 scheint es jedoch nicht zu mögen.

Mein Code lautet:

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;
}

und ich benutze es als:

import { testWidget} from "lib/testWidget";

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

Ich erhalte eine Fehlermeldung von Angular:

AUSNAHME: Es können nicht alle Parameter für testWidget aufgelöst werden: (?).

Also dachte ich, Modell ist noch nicht definiert ... Ich werde es nach oben verschieben!

Außer jetzt bekomme ich die Ausnahme:

ORIGINAL AUSNAHME: Kein Anbieter für Model!

Wie schaffe ich das?

Edit: Danke an alle für die Antwort. Es führte mich auf den richtigen Weg.

Um dies in den Konstruktor einzufügen, muss ich es den Anbietern auf der Komponente hinzufügen.

Dies scheint zu funktionieren:

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
quelle

Antworten:

152

Ich würde es versuchen:

Teilen Sie Ihr Modell in eine separate Datei mit dem Namen model.ts:

export class Model {
    param1: string;
}

Importieren Sie es in Ihre Komponente. Dies gibt Ihnen den zusätzlichen Vorteil, dass Sie es in anderen Komponenten verwenden können:

Import { Model } from './model';

In der Komponente initialisieren:

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

Greifen Sie im HTML entsprechend darauf zu:

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

Ich möchte der Antwort einen Kommentar von @PatMigliaccio hinzufügen, da es wichtig ist, sich an die neuesten Tools und Technologien anzupassen:

Wenn Sie verwenden angular-cli, können Sie anrufen ng g class modelund es wird es für Sie generieren. Modell wird durch eine beliebige Benennung ersetzt.

Brendon Colburn
quelle
1
Interessant ... Wenn ich versuche, die Kurzform des Konstruktors (privates Modell: Modell) zu verwenden, wird die Fehlermeldung "Kein Anbieter" angezeigt. Wenn ich es jedoch als privates Modell definiere: Model = new Model (), funktioniert es. Warum ist das?
Scottie
7
Ich bin kein Angular 2-Architekt, aber basierend auf meiner Erfahrung mit Angular, wenn Sie etwas durch den Konstruktor einbringen, implizieren Sie, dass es injiziert wird. Für das Injizieren müssen Sie es als Anbieter wie folgt zur @Component hinzufügen : providers: [Model]. Gemäß der Angular 2 Tour of Hero-Demo sollten Sie sie nur als Eigenschaft anstelle einer injizierbaren verwenden, da diese Funktionalität normalerweise komplexeren Klassen wie Diensten vorbehalten ist.
Brendon Colburn
1
Problem mit der Art und Weise, wie das Modell instanziiert wird (nicht verwendet new). Ist das, dass der Konstrukteur des Modells nicht aufgerufen wird. Und das instanceOf Modelwird falsch sein
Poul Kruijt
Aber es gab zunächst keinen Konstruktor? Ich sehe dies nicht als großes Problem für diese Implementierung. Wenn die Dinge komplexer werden, dann sicher. Ich lerne aber auch noch. Ich habe gerade diese Kurzmethode gelernt, neulich nicht zu verwenden newund mag sie für einfache Fälle wie diesen.
Brendon Colburn
5
Wenn Sie verwenden angular-cli, können Sie anrufen ng g class modelund es wird es für Sie generieren. modeldurch eine beliebige Benennung ersetzt werden.
Pat Migliaccio
16

Das Problem liegt darin, dass Sie Modelweder dem bootstrap(was es zu einem Singleton macht) noch dem providersArray Ihrer Komponentendefinition hinzugefügt haben :

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

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

Und ja, Sie sollten Modelüber dem definieren Component. Aber besser wäre es, es in seine eigene Datei zu legen.

Wenn Sie jedoch möchten, dass es sich nur um eine Klasse handelt, aus der Sie mehrere Instanzen erstellen können, verwenden Sie diese einfach 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
quelle
1
Modell ist nur eine Klasse, Import sollte ideal funktionieren. Warum brauchen wir es in providersArray?
Pankaj Parkar
Ja, aber sein Templating würde hier nicht funktionieren, es wäre model.param1. Auch hat er ihm keinen Anfangswert gegeben?
Brendon Colburn
@PankajParkar Weil ich immer noch eine bekomme, No Provider for Modelwenn ich sie nicht zum Anbieter-Array hinzufüge
Poul Kruijt
@PierreDuc hast du exportvor dem ModelUnterricht?
Pankaj Parkar
@ PankajParkar schauen Sie hier
Poul Kruijt
6

In Ihrem Fall befindet sich das Modell auf derselben Seite, aber Sie haben es nach Ihrer Komponentenklasse deklariert. Sie müssen es also verwenden, um darauf forwardRefzu verweisen Class. Ziehen Sie dies nicht vor, sondern haben Sie das modelObjekt immer in einer separaten Datei.

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

Zusätzlich müssen Sie Ihre Ansichtsinterpolation ändern, um auf das richtige Objekt zu verweisen

{{model?.param1}}

Besser ist es, wenn Sie Ihre ModelKlasse in einer anderen Datei definieren lassen und sie dann importieren, wenn Sie dies benötigen. Haben exportSie auch vor Ihnen Klassennamen, damit Sie ihn importieren können.

import { Model } from './model';
Pankaj Parkar
quelle
@BrendonColburn danke Mann, das habe ich in deiner Antwort gesehen. Also dachte ich, es gibt keine Bedeutung, meine Antwort nachträglich zu bearbeiten. Danke Mann für die Heads-Ups,
Prost
5

Mein Code ist

    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 = "";
  }    
  }
}

und das HTML geht so:

<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
quelle
3
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();
      }
  }
}

Die || kann für sehr komplexe Datenobjekte sehr nützlich sein, wenn Standarddaten nicht vorhanden sind.

. .

In Ihrer Datei component.ts oder service.ts können Sie Antwortdaten in das Modell deserialisieren:

// 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
quelle
2

Sie können das Angular-Cli verwenden, wie die Kommentare in der Antwort von @ brendon nahe legen.

Vielleicht möchten Sie auch versuchen:

ng g class modelsDirectoy/modelName --type=model

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

ng g class Beachten ng g c
Sie : ! == Sie können jedoch ng g clabhängig von Ihrer Angular-Cli-Version als Verknüpfung verwenden.

user9869932
quelle
0

Mir ist klar, dass dies eine etwas ältere Frage ist, aber ich wollte nur darauf hinweisen, dass Sie die Modellvariable Ihrer Test-Widget-Klasse falsch hinzugefügt haben. Wenn Sie eine Modellvariable benötigen, sollten Sie nicht versuchen, sie über den Komponentenkonstruktor zu übergeben. Sie sollen nur Dienste oder andere Arten von Injektionsmitteln auf diese Weise weitergeben. Wenn Sie Ihr Test-Widget innerhalb einer anderen Komponente instanziieren und ein Modellobjekt als übergeben müssen, würde ich die Verwendung der Entwurfsmuster OnInit und Input / Output mit eckigem Kern empfehlen.

Als Beispiel sollte Ihr Code wirklich so aussehen:

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

Eine Klasse, die im Wesentlichen nur eine Struktur / ein Modell ist, sollte nicht eingefügt werden, da dies bedeutet, dass Sie nur eine einzige gemeinsam genutzte Instanz dieser Klasse innerhalb des bereitgestellten Bereichs haben können. In diesem Fall bedeutet dies, dass jedes Mal, wenn testWidget instanziiert wird, eine einzelne Instanz von Model vom Abhängigkeitsinjektor erstellt wird. Wenn es auf Modulebene bereitgestellt würde, hätten Sie nur eine einzige Instanz, die von allen Komponenten und Diensten innerhalb dieses Moduls gemeinsam genutzt wird.

Stattdessen sollten Sie den objektorientierten Standardpraktiken folgen und eine private Modellvariable als Teil der Klasse erstellen. Wenn Sie beim Instanziieren der Instanz Informationen an dieses Modell übergeben müssen, sollten diese von einem Dienst (injizierbar) verwaltet werden, der von bereitgestellt wird das übergeordnete Modul. Auf diese Weise sollen sowohl die Abhängigkeitsinjektion als auch die Kommunikation im Winkel ausgeführt werden.

Wie einige der anderen erwähnt, sollten Sie Ihre Modellklassen auch in einer separaten Datei deklarieren und die Klasse importieren.

Ich würde dringend empfehlen, zur Referenz zur eckigen Dokumentation zurückzukehren und die Basisseiten zu den verschiedenen Anmerkungen und Klassentypen zu lesen: https://angular.io/guide/architecture

Beachten Sie insbesondere die Abschnitte zu Modulen, Komponenten und Diensten / Abhängigkeitsinjektion, da diese für das Verständnis der Verwendung von Angular auf architektonischer Ebene von entscheidender Bedeutung sind. Angular ist eine sehr architekturintensive Sprache, weil sie so hoch ist. Die Trennung von Bedenken, Abhängigkeitsinjektionsfabriken und die Javascript-Versionierung für die Vergleichbarkeit des Browsers werden hauptsächlich für Sie erledigt. Sie müssen jedoch die Anwendungsarchitektur korrekt verwenden, da sonst die Dinge nicht wie erwartet funktionieren.

user2904660
quelle
0

Erstellen Sie model.ts in Ihrem Komponentenverzeichnis wie folgt

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

Importieren Sie dann in Ihrer oben importierten Komponente {DataModel} aus './model'.

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

Ihr DataObject sollte alle Eigenschaften von DataObjectName haben.

Prasad
quelle