Как объявить класс модели в моем компоненте Angular 2 с помощью TypeScript?

82

Я новичок в Angular 2 и TypeScript и стараюсь следовать лучшим практикам.

Вместо использования простой модели JavaScript ({}) я пытаюсь создать класс TypeScript.

Однако Angular 2 это, похоже, не нравится.

Мой код:

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

и я использую его как:

import { testWidget} from "lib/testWidget";

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

Я получаю сообщение об ошибке от Angular:

ИСКЛЮЧЕНИЕ: Невозможно разрешить все параметры для testWidget: (?).

Вот я и подумал: Модель еще не определена ... Перемещу наверх!

Вот только теперь я получаю исключение:

ОРИГИНАЛЬНОЕ ИСКЛЮЧЕНИЕ: нет поставщика для модели!

Как мне этого добиться?

Изменить: Спасибо всем за ответ. Это привело меня на правильный путь.

Чтобы вставить это в конструктор, мне нужно добавить его к поставщикам компонента.

Кажется, это работает:

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) {}
}
Скотти
источник

Ответы:

153

Я бы попробовал это:

Разделите вашу модель в отдельный файл с именем model.ts:

export class Model {
    param1: string;
}

Импортируйте его в свой компонент. Это даст вам дополнительное преимущество, так как вы сможете использовать его в других компонентах:

Import { Model } from './model';

Инициализировать в компоненте:

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

Получите доступ к нему соответствующим образом в html:

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

Я хочу добавить к ответу комментарий @PatMigliaccio, потому что важно адаптироваться к новейшим инструментам и технологиям:

Если вы используете, angular-cliвы можете позвонить, ng g class modelи он сгенерирует его для вас. модель заменяется любым названием, которое вы пожелаете.

Брендон Колберн
источник
1
Интересно ... Если я попытаюсь использовать сокращенное обозначение конструктора (частная модель: Модель), я получаю сообщение об ошибке "Нет поставщика". Однако, если я определяю его как частную модель: Model = new Model (), она работает. Почему это?
Скотти
7
Я не архитектор Angular 2, но, исходя из моего опыта работы с Angular, когда вы вводите что-то через конструктор, вы подразумеваете, что это вводится. Инъекционное требует , чтобы добавить его в @Component в качестве поставщика , как , например: providers: [Model]. Также, согласно демонстрации Angular 2 Tour of Hero, вы должны просто использовать его как свойство, а не как инъекцию, поскольку эта функциональность обычно зарезервирована для более сложных классов, таких как службы.
Брендон Колберн
1
Проблема с вашим способом создания экземпляра модели (без использования new). Разве что конструктора модели не назовут. И это instanceOf Modelбудет ложью
Poul Kruijt
Но не было конструктора? Я не вижу в этом большой проблемы для данной реализации. Если все станет сложнее, тогда обязательно. Я тоже все еще учусь. Я только что выучил этот сокращенный метод, чтобы не использовать его newна днях, и мне он нравится в таких простых случаях, как этот.
Брендон Колберн
5
Если вы используете, angular-cliвы можете позвонить, ng g class modelи он сгенерирует его для вас. modelзаменяется любым именем, которое вы пожелаете.
Пэт
16

Проблема в том, что вы не добавили Modelни в bootstrap(что сделает его одноэлементным), ни в providersмассив определения вашего компонента:

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

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

И да, вы должны определить Modelвыше Component. Но лучше было бы поместить его в свой файл.

Но если вы хотите, чтобы это был просто класс, из которого вы можете создавать несколько экземпляров, вам лучше просто использовать 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() {}
}
Пол Крюйт
источник
1
Модель - это просто класс, импорт должен работать идеально. зачем нам это в providersмассиве?
Pankaj Parkar
Да, но его шаблон здесь не работает, это будет model.param1. Кроме того, он не дал ему начальную стоимость?
Брендон Колберн
@PankajParkar Потому что я все равно получаю, No Provider for Modelесли не добавляю его в массив провайдеров
Пол Круйт
@PierreDuc у тебя есть exportперед уроком Model?
Pankaj Parkar,
@PankajParkar посмотрите здесь
Poul Kruijt
6

В вашем случае у вас есть модель на той же странице, но она объявлена ​​после вашего класса Component, поэтому вам нужно использовать forwardRefдля ссылки Class. Не предпочитаю это делать, всегда храните modelобъект в отдельном файле.

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

Кроме того, вы должны изменить интерполяцию просмотра, чтобы ссылаться на правильный объект

{{model?.param1}}

Лучше всего, что вы должны сделать, вы можете определить свой Modelкласс в другом файле, а затем импортировать его, когда вам это нужно. Также exportукажите имя класса, чтобы его можно было импортировать.

import { Model } from './model';
Панкадж Паркар
источник
@BrendonColburn, спасибо, чувак, я видел это в твоем ответе. так что я подумал, что нет смысла редактировать мой ответ, спасибо, мужик, за внимание, ура :)
Панкадж Паркар
5

мой код

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

и html выглядит так:

<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>
Ахмед Баша
источник
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();
      }
  }
}

|| может стать очень полезным для очень сложных объектов данных вместо данных по умолчанию, которые не существуют.

. .

В файле component.ts или service.ts вы можете десериализовать данные ответа в модель:

// 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));
Майк Хоуз
источник
3

Вы можете использовать angular-cli, как предлагают комментарии в ответе @ brendon.

Вы также можете попробовать:

ng g class modelsDirectoy/modelName --type=model

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

Имейте в виду ng g class :! == ng g c
Однако вы можете использовать в ng g clкачестве ярлыка в зависимости от вашей версии angular-cli.

user9869932
источник
0

Я понимаю, что это несколько более старый вопрос, но я просто хотел указать, что вы неправильно добавили переменную модели в свой тестовый класс виджета. Если вам нужна переменная модели, вы не должны пытаться передать ее через конструктор компонента. Вы должны только таким образом передавать услуги или другие типы инъекций. Если вы создаете экземпляр своего тестового виджета внутри другого компонента и вам нужно передать объект модели как, я бы рекомендовал использовать шаблоны проектирования OnInit и Input / Output ядра angular.

Например, ваш код действительно должен выглядеть примерно так:

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

Класс, который по сути является просто структурой / моделью, не должен вводиться, потому что это означает, что у вас может быть только один общий экземпляр этого класса в пределах предоставленной области. В этом случае это означает, что один экземпляр Model создается инжектором зависимостей каждый раз, когда создается экземпляр testWidget. Если бы он был предоставлен на уровне модуля, у вас был бы только один экземпляр, совместно используемый всеми компонентами и службами в этом модуле.

Вместо этого вы должны следовать стандартным методам объектно-ориентированной модели и создавать частную переменную модели как часть класса, и если вам нужно передать информацию в эту модель при создании экземпляра, это должно обрабатываться службой (вводимой), предоставляемой родительский модуль. Таким образом и внедрение зависимостей, и коммуникация должны выполняться в angular.

Кроме того, как уже упоминалось, вы должны объявлять классы своей модели в отдельном файле и импортировать этот класс.

Я настоятельно рекомендую вернуться к справочной документации по angular и просмотреть основные страницы по различным аннотациям и типам классов: https://angular.io/guide/architecture

Вам следует обратить особое внимание на разделы, посвященные модулям, компонентам и службам / внедрению зависимостей, поскольку они необходимы для понимания того, как использовать Angular на архитектурном уровне. Angular - это язык с очень тяжелой архитектурой, потому что он очень высокого уровня. Разделение проблем, фабрики внедрения зависимостей и управление версиями javascript для сопоставимости браузеров в основном обрабатываются за вас, но вы должны правильно использовать их архитектуру приложений, иначе вы обнаружите, что все работает не так, как вы ожидаете.

user2904660
источник
0

создайте model.ts в каталоге компонентов, как показано ниже

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

затем в импорте компонента выше как, импортируйте {DataModel} из './model';

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

ваш DataObject должен иметь все свойства из DataObjectName.

Прасад
источник