2016-04-15 4 views
1

Хотя эта тема уже обсуждалась в других постах, как это:Машинопись и угловые 2 Отражение

Dynamically loading a typescript class (reflection for typescript)

Я не смог найти ответа на мой конкретный вопрос. Итак, простите меня, если это дублируется.

Я пытаюсь создать очень простую директиву в Angular 2 (используя машинопись), которая позволяет динамически добавлять или удалять набор элементов управления, представленных типом. Например, если тип:

class Stone{ 
    constructor(
    public nameOfStone?: string, 
    public typeOfStone?: string 
){} 
} 

пользовательский интерфейс будет иметь что-то вроде этого:

Screenshot 1

Я могу получить эту работу с определенного типа (например: камень). Но, учитывая, что целью директивы является просто добавить эту динамическую функцию добавления/удаления, я почувствовал, что имеет смысл параметризовать создаваемый тип и использовать его для разных определений типов. Я пытался что-то вроде этого в компоненте класса:

import {Component} from 'angular2/core'; 
import {NgForm} from 'angular2/common'; 
import {ControlGroup, Control, FormBuilder, FORM_DIRECTIVES} from 'angular2/common' 
@Component({ 
    selector: 'stone-details', 
    templateUrl: '../stones/stone-details.component.html', 
    directives: [FORM_DIRECTIVES] 
}) 
export class StoneComponent { 
    type = 'Stone'; 
    Stones = new Array<Stone>(); 

    addBtnClicked(){ 
    let Stone = Object.create(window['Stone'].prototype); 
    //let Stone = new Stone('', ''); 
    this.Stones.push(Stone); 
    } 
    removeBtnClicked(index: number){ 
    if(index >= this.Stones.length){ 
     alert('Not a valid index'); 
    }else if(confirm('Remove this Stone?')){ 
     this.Stones.splice(index, 1); 
    } 
    } 
} 

class Stone{ 
    constructor(
    public nameOfDeity?: string, 
    public typeOfDeity?: string 
){} 
} 

Когда я использую комментируемой строке let Stone = new Stone('', ''); компонент работает отлично, но если я использую let Stone = Object.create(window['Stone'].prototype); это не похоже на работу и ошибку я вижу есть angular2.dev.js:23941 ORIGINAL EXCEPTION: TypeError: Cannot read property 'prototype' of undefined.

Первоначально я думал, что экспорт камня класса поможет, но ни одна из сумасшедших вариаций (экспорт класса, пытаясь ссылаться на класс как окно ['StoneComponent']. Export_1 ['Stone']). Я понимаю, что компонент не отображается непосредственно под компонентом окна, но я не уверен, что мне не хватает. Есть ли альтернативный способ сделать это? Я что-то упускаю? Пожалуйста, порекомендуйте.

P.S: Я использую последнюю версию Angular 2 и Typcript (я запустил это приложение на пару дней назад).

+0

does 'Object.create (Stone.prototype)' не работает? – jpopesculian

+0

Это так, но я не могу сделать его динамичным. То есть Object.create (Stone.prototype) работает, что не сильно отличается от Stone = new Stone(); Но я не могу использовать переменную вместо типа Stone, например: Object.create ( .prototype) – user1452030

+0

Это сложно, потому что трудно узнать, что такое родительский объект. Это не окно, я думаю. Вы пробовали это? В противном случае вы можете создать собственный словарь возможных прототипов, а затем получить доступ к прототипам через этот словарь (кажется достаточно безопасным, вам просто нужно его поддерживать). В противном случае, попробуйте 'eval (« Stone »)'? Чувствует себя супер грязным, хотя – jpopesculian

ответ

0

Поскольку класс является простой функцией, вы можете создать экземпляр с использованием new func(), но вам все равно придется пройти func из внешнего кода.

Я думаю, наиболее эффективным решением является следующее:

export class StoneComponent<T> { 
    addBtnClicked(){ 
    let item:T = <T>{} 
    this.items.push(item); 
    } 
} 

типа будет соответствовать до тех пор, как объекты имеют одинаковый набор свойств.

1

Проблема с вашим кодом - это определение порядка.

В частности, определение классов является не водрузили как функция определения. Сложная часть состоит в том, что типStone поднят, что совершенно верно, но значение Stone, функция-конструктор, нет.

Чтобы обойти это, просто переместите определение Stone над компонентом или извлеките его в отдельный модуль и импортируйте его.

Не пытаться засунуть его в глобальную переменную, скажем window. Это очень плохая практика и приведет к ошибкам и столкновениям имен быстрее, чем можно было бы подумать. Он также поражает преимущества модулей.

Короче, что вам нужно

class Stone { 
    constructor(
    readonly nameOfDeity?: string, 
    readonly typeOfDeity?: string 
) {} 
} 

export class StoneComponent { 
    kind = 'Stone'; 
    stones: Stone[] = []; 

    addBtnClicked() { 
    const stone = new Stone(); 
    this.stones.push(stone); 
    } 
    removeBtnClicked(index: number) { 
    if (index >= this.stones.length) { 
     alert('Not a valid index'); 
    } else if (confirm('Remove this Stone?')){ 
     this.stones.splice(index, 1); 
    } 
    } 
} 

UPDATE Поскольку в исходном вопросе Вы утверждаете, что это будет общий компонент, и вы будете иметь несколько классов, где фактический класс выбран с помощью kind свойство класса компонентов. Возможно, вы захотите рассмотреть следующий шаблон.

компонент-полевого kinds.ts

export class Stone { ... } 

export class Widget { ... } 

export class Gizmo { ... } 

родовой-component.ts

import * as kinds from './component-field-kinds'; 

type Kind = keyof typeof kinds; 

export class GenericComponent { 
    @Input() kind: Kind; 

    values: typeof kinds[Kind][] = []; 

    addBtnClicked() { 
    const value = new kinds[this.kind](); 
    this.values.push(value); 
} 

Обратите внимание, что для того, что он стоит, JavaScript, и поэтому машинопись не имеет такую как динамический загрузчик классов. Это то, как язык работает все время, и вся структура является первоклассной.

Это не Ява.

Смежные вопросы