¿Cómo declaro una clase modelo en mi componente Angular 2 usando TypeScript?

82

Soy nuevo en Angular 2 y TypeScript y estoy tratando de seguir las mejores prácticas.

En lugar de usar un modelo de JavaScript simple ({}), estoy intentando crear una clase de TypeScript.

Sin embargo, a Angular 2 no parece gustarle.

Mi codigo es:

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

y lo estoy usando como:

import { testWidget} from "lib/testWidget";

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

Recibo un error de Angular:

EXCEPCIÓN: No se pueden resolver todos los parámetros para testWidget: (?).

Entonces pensé, el modelo aún no está definido ... ¡Lo moveré a la parte superior!

Excepto que ahora obtengo la excepción:

EXCEPCIÓN ORIGINAL: ¡No hay proveedor para Model!

¿Cómo logro esto?

Editar: Gracias a todos por la respuesta. Me llevó al camino correcto.

Para inyectar esto en el constructor, necesito agregarlo a los proveedores en el componente.

Esto parece funcionar:

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) {}
}
Escocés
fuente

Respuestas:

152

Intentaría esto:

Divida su modelo en un archivo separado llamado model.ts:

export class Model {
    param1: string;
}

Importarlo a su componente. Esto le dará el beneficio adicional de poder usarlo en otros componentes:

Import { Model } from './model';

Inicializar en el componente:

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

Acceda a él apropiadamente en el html:

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

Quiero agregar a la respuesta un comentario hecho por @PatMigliaccio porque es importante adaptarse a las últimas herramientas y tecnologías:

Si está utilizando angular-cli, puede llamar ng g class modely lo generará por usted. modelo siendo reemplazado por el nombre que desee.

Brendon Colburn
fuente
1
Interesante ... Si intento hacer la abreviatura de constructor (modelo privado: Modelo), aparece el error que dice No Provider. Sin embargo, si lo defino como modelo privado: Model = new Model (), funciona. ¿Por qué es esto?
Scottie
7
No soy un arquitecto de Angular 2, pero según mi experiencia con Angular, cuando traes algo a través del constructor, estás insinuando que se inyecta. La inyección requiere que se agrega a la @Component como un proveedor como por ejemplo: providers: [Model]. Además, según la demostración de Angular 2 Tour of Hero, debe usarlo como una propiedad en lugar de un inyectable, ya que esa funcionalidad generalmente está reservada para clases más complejas, como los servicios.
Brendon Colburn
1
Problema con su forma de instanciar el modelo (sin usar new). Es que no se llamará al constructor del modelo. Y eso instanceOf Modelserá falso
Poul Kruijt
¿Pero no había ningún constructor para empezar? No veo esto como un gran problema para esta implementación. Si las cosas se vuelven más complejas, seguro. Aunque todavía estoy aprendiendo. Acabo de aprender este método abreviado de no usar newel otro día y me gusta para casos simples como este.
Brendon Colburn
5
Si está utilizando angular-cli, puede llamar ng g class modely lo generará por usted. modelsiendo reemplazado por el nombre que desee.
Pat Migliaccio
16

El problema radica en que no ha agregado Modelni a bootstrap(lo que lo convertirá en un singleton) ni a la providersmatriz de la definición de su componente:

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

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

Y sí, debe definir por Modelencima de Component. Pero mejor sería ponerlo en su propio archivo.

Pero si desea que sea solo una clase desde la que puede crear múltiples instancias, es mejor que use 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
fuente
1
El modelo es solo una clase, la importación debería funcionar idealmente. ¿Por qué lo necesitamos en providersmatriz?
Pankaj Parkar
Sí, pero su plantilla no funcionaría aquí, sería model.param1. Además, ¿no le ha dado un valor inicial?
Brendon Colburn
@PankajParkar Porque todavía obtengo un No Provider for Modelsi no lo agrego a la matriz de proveedores
Poul Kruijt
@PierreDuc tienes exportantes de Modelclase?
Pankaj Parkar
@PankajParkar mira aquí
Poul Kruijt
6

En su caso, tiene el modelo en la misma página, pero lo tiene declarado después de su clase Componente, por lo que debe usarlo forwardRefpara hacer referencia Class. No prefiera hacer esto, siempre tenga el modelobjeto en un archivo separado.

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

Además, debe cambiar la interpolación de vista para hacer referencia al objeto correcto

{{model?.param1}}

Lo mejor que debe hacer es que puede hacer que su ModelClase se defina en un archivo diferente y luego importarlo cuando lo necesite. También tenga exportantes su nombre de clase, para que pueda importarlo.

import { Model } from './model';
Pankaj Parkar
fuente
@BrendonColburn, gracias, lo vi en tu respuesta. así que pensé que no tenía sentido editar mi respuesta más adelante, gracias hombre por la advertencia, salud :)
Pankaj Parkar
5

mi codigo es

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

y el html es así:

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

El || puede volverse muy útil para objetos de datos muy complejos a datos predeterminados que no existen.

. .

En su archivo component.ts o service.ts, puede deserializar los datos de respuesta en el modelo:

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

Puede usar el angular-cli como sugieren los comentarios en la respuesta de @ brendon.

Quizás también quieras probar:

ng g class modelsDirectoy/modelName --type=model

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

Tenga en cuenta ng g class :! == ng g c
Sin embargo, puede usarlo ng g clcomo atajo dependiendo de su versión de angular-cli.

usuario9869932
fuente
0

Me doy cuenta de que esta es una pregunta algo más antigua, pero solo quería señalar que ha agregado la variable del modelo a su clase de widget de prueba de manera incorrecta. Si necesita una variable de modelo, no debería intentar pasarla a través del constructor del componente. Solo tiene la intención de aprobar servicios u otros tipos de inyectables de esa manera. Si está creando una instancia de su widget de prueba dentro de otro componente y necesita pasar un objeto de modelo como, le recomendaría usar el núcleo angular OnInit y los patrones de diseño de entrada / salida.

Como ejemplo, su código realmente debería verse así:

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

Una clase que es esencialmente solo una estructura / modelo no debe inyectarse, porque significa que solo puede tener una instancia compartida única de esa clase dentro del alcance que se proporcionó. En este caso, eso significa que el inyector de dependencias crea una única instancia de Model cada vez que se crea una instancia de testWidget. Si se proporcionara a nivel de módulo, solo tendría una instancia compartida entre todos los componentes y servicios dentro de ese módulo.

En su lugar, debe seguir las prácticas estándar orientadas a objetos y crear una variable de modelo privado como parte de la clase, y si necesita pasar información a ese modelo cuando crea una instancia de la instancia, debe ser manejada por un servicio (inyectable) proporcionado por el módulo padre. Así es como se pretende que tanto la inyección de dependencia como la comunicación se realicen en angular.

Además, como algunos de los otros mencionaron, debe declarar sus clases modelo en un archivo separado e importar la clase.

Recomendaría encarecidamente volver a la referencia de documentación angular y revisar las páginas básicas sobre las diversas anotaciones y tipos de clases: https://angular.io/guide/architecture

Debe prestar especial atención a las secciones sobre módulos, componentes y servicios / inyección de dependencia, ya que son esenciales para comprender cómo usar Angular a nivel arquitectónico. Angular es un lenguaje de arquitectura muy pesada porque es de muy alto nivel. La separación de preocupaciones, las fábricas de inyección de dependencia y el control de versiones de javascript para la comparabilidad del navegador se manejan principalmente por usted, pero debe usar la arquitectura de su aplicación correctamente o encontrará que las cosas no funcionan como espera.

usuario2904660
fuente
0

cree model.ts en su directorio de componentes como se muestra a continuación

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

luego, en su componente, importe arriba como, importe {DataModel} desde './model';

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

su DataObject debe tener todas las propiedades de DataObjectName.

Prasad
fuente