Pull to refresh
@Druuread⁠-⁠only

User

Send message
Надо useExisting вместо useClass
пропустил, вот этот кусок:
export interface IComponent {
    data: string;
}
export const IComponentToken = new InjectionToken<IComponent>("IComponent");

еще я мог в декларации провайдера напутать, я ее не помню, надо в доках смотреть
> Все. Просто можно выполнять задачу легко, быстро и поддерживаемо, а можно до посинения обмазываться никому ненужной архитектурой.

Ну а теперь перепишите это к нормальному виду, для полноценной задачи, чтобы бизнес-логика была вынесена в отдельный класс хотя бы.

> Если мне понадобиться вынести логику в «модель», я вынесу и буду принимать ее через props.

Ну так продемонстрируйте. На однострочниках-то все всегда хорошо :)

> А если еще дальше — я поставлю inversifyjs и буду инжектить ее прямо в компонент/контейнер. Все.

Ну так вы не стесняйтесь, продемонстрируйте то, о чем сейчас так лихо рассказали. Я же не так просто правило Гринспена вспомнил :)

> Ну давайте, жгите!

Давайте с чего-нибудь простого начнем. Сделайте мне аналог компоненты с селектором-атрибутом.
@Component({
    selector: "component1",
    template: `<span>{{ data }}</span>`,
    providers: [{ provide: IComponentToken, useClass: forwardRef(() => Component1) }]

})
export class Component1 implements IComponent {
    @Input() data = "1";
}

@Component({
    selector: "component2",
    template: `<div>{{ data }}</div>`,
    providers: [{ provide: IComponentToken, useClass: forwardRef(() => Component1) }]
})
export class Component2 implements IComponent {
    @Input() data = "2";
}

@Directive({
    selector: "[mixin]",
})
export class MixinDirective implements OnInit  {
    constructor(@Host() @Inject(IComponentToken) private host: IComponent) {        
    }

    ngOnInit() {
        this.host.data = "3";
    }
}

както так
> Сохранив при этом компонуемость в том виде, в котором мне нужно.

Ну как же вы сохраните? Вид и модель будут отдельно? Будут.

> Я не вижу ни одной причины гоняться за мнимым отделением шаблона от его компонента

А вы в реакте всю логику суете прям в компонент, таким вот невнятным ворохом? Ну тогда ясно все.

> Вопрос дизайна и подхода — текущий не дает мне нужной гибкости, которую дает реакт.

Как не дает, если гибкость решения на ангуляре наоборот _выше_? Все, что можно на реакте — можно легко повторить на ангуляре. Но вот если я начну просить ангуляровские вещи повторить на реакте — у вас это просто вообще не выйдет, тут сработает аналог правила Гринспена :)

> как простейшие в реакте вещи сделать с огородом костылей в 20 раз сложнее на ангуляре

Ну у вас проблема в том, что вы не понимаете архитектуры ангуляра и называете «костылями» пару строчек вполне идеоматического кода. Вы пытаетесь задаунгрейдить ангуляр до примитивного уровня реакта, где просто ничего нет. Это знаете, как сравнивать какой-нибудь ЯВУ с ассемблером. Приходит человек и говорит: «а вот я хочу параметр через стек передать для вызова подпрограммы. В ассемблере пишу две строчки и все работает — push/pop, а как у вас, в java/c#/javascript/etc? Ой какой ужас в 20 раз сложнее». Примерно так для меня ваши тезисы выглядят, если честно.
> ут идеально бы подошел интерфейс, но инжектор не умеет инжектить компоненты по интерфейсу.

Вообще умеет. Брать токен по типу — это лишь стандартное поведение, можно в аргументах в конструкторе сделать (@Inject(token) host: InterfaceComponent) и в хост заинжектится нужный токен, полностью будет как-то: ( Host() Inject(token) host: InterfaceComponent) (@Host() ограничивает инжектор, с-но, хостом), ну и в самой хост-компоненте надо зарегистрировать провайдер для соответствующего токена.
> HostListener, как и HostBinding, работает только с нативными ивентами на дом-ноде хоста.

Нет, он со всеми работает, я специально проверил.

> Да, вот только мне явно нужно знать класс компонента, к которому я прицепляюсь.

Так вы уже определитесь — вам loaded не нужен (и тогда вы точно знаете класс) или нужен (тогда не знаете). Если класс неизвестен, то делается интерфейс, который компоненты, к которым можно подмешивать нужное поведение, реализуют, и хост инжектится потом по кастомному токену

> Зачем вообще этот loaded?

Чтобы можно было подмешивать поведение к разным компонентам. А если этого не надо, то вообще не понятно, в чем была исходная проблема.

> Плюс у меня есть смутные подозрения, что, выносят темплейт из класса, мы теряем всяческую помощь IDE внутри этого темплейта.

В тайпскрипте уже сделали language-service, так что это не проблема теперь.

> Зачем должна, кому должна,

Человеческой архитектуре. В реакте-то вы все равно к той же схеме придете, так какая разница.
raveclassic и, собственно, вы же можете поступить как в реакте — то есть написать ф-ю, которая принимает один класс (модель), и возвращает новый — с измененным поведением. Вам только надо будет явно связать эту модель со старым видом, и все:
const template = `<div><button>{{ name }}</button></div>`;

@Component({
    selector: "my-button",
    template: template,
})
export class MyButtonComponent {
     @Input() name = "";
}

export function loaded(component: any) {
    return class extends component {
        name = "loaded";
    };
}

@Component({
    selector: "loaded-button",
    template: template,
})
export class LoadedButtonComponent extends loaded(MyButtonComponent) {};
Там же указано решение через инжект хоста (оно, кстати, и лучше, т.к. типы будут).
через HostListener:
@Component({
    selector: "test",
    template: `<div><button (click)='testClick($event)'></button></div>`
})
export class TestComponent {
    @Output() custom = new EventEmitter<string>();    

    testClick(e: any) {
        console.log("outer");
        this.custom.emit("to inner");        
    }
}

@Directive({
    selector: "[atest]",   
})
export class ATestDirective {
    @HostListener("custom", ["$event"]) acustom(e: string) {
        console.log("inner")
        console.log(e);
    };
}

// inner -> outer -> to inner
> Ну так а с декларативными эффектами я и грязную так же просто тестирую.

С декларативными эффектами она не грязная :)
Но оверхед от достижения этой декларативности кажется значительно превышающим какой-то один мок.
> Идеологически на уровне редьюсера стейт с одним и тем же промисом (даже ссылка та же) но в разных состояних — это 3 разных стейта, так как по сути описывают 3 разных состояния: pending, success, fail. Это же разные состояния? Разные.

Это состояние промиса. Зачем его тащить в стейт? ПРусть будет локлаьным для промиса.

> А тут бац, сессия сдохла — как это в редьюсере похендлить?

А как вы это хендлите вне редьюсера?

> Как вы синхронно отмотаете список экшенов, когда среди них есть асинхронные?

Среди них нет асинхронных. Любой экшн — это просто тип с пейлоадом.

> А там какая-то асинхронщина, которая другие экшены выбрасывает. А в оплоге они уже есть, упс.

Вот именно. Это проблема, когда у нас произвольные сайд-эффекты, но когда эффекты ограничены (например — исключительно спавном, возможно асинхронным, других экшенов), то они не оказывают влияния ни на стор, ни на ход рендера, по-этому мы можем просто мокнуть ф-ю, которая внутри редьюсера вызывает эффект, в x => {return;}, которая ничего не делает.

> Собственно undo/redo так и делается. Там не экшены применяются/отменяются, а просто заменяется весь стейт.

В этом случае редьюсеры не вызываются и вообще никаких проблем нет. Мотай себе стейт куда захочешь и как захочешь.
«Чистую» часть редьюсеров вы можете тестировать так же просто. а «Грязная»… ну она и в оригинальном редаксе была грязной, просто находилась не в редьюсерах.
> Нет, в зависимости от состояния этого промиса, результаты будут разные.

С чего бы это? Мы же не делаем await промиса в редьюсере, редьюсер только создает промис, но не ожидает его завершения. Потом в then промиса диспатчится какой-то экшон и уходит в стор. В итоге у нас в стор в обоих случаях приходит вполне детерменированно два одинаковых экшона в одном и том же порядке.

> Он не должен что-то там спавнить по ходу, так как сразу обрастет сайд-эффектом.

Этот сайдэффект все равно есть. И от того что вы его отодвинули ничего не меняется — ну это просто заметание под ковер.

> И я не очень понимаю, почему выполнение эффекта вне редьюсера вызывает столько негодования.

Потому что это неудобно. Вот и все. И при этом ничего не дает.

> Держа редьюсеры чистыми, вы на халяву получаете простоту отладки, простоту тестирования, тайм-тревелинг, undo/redo и прочее-прочее.

В описываемом мной случае все точно так же — у нас линейная последовательность экшенов, которую можно отматывать и так далее.
Надо просто во все инпуты, где пробрасываются именно данные (а не константы/конфиги) делать по дефолту обсерваблами (и, с-но, асинк в темплейте). Это даже не то что для таких вот случаев, а в принципе архитектура ангуляра заточена больше под такой flow, он в целом будет удобнее (хотя поначалу и непривычно).
> Разница есть — ваш редьюсер недетерминирован.

Вполне детерменирован — он же вернет в точности тот же результат. Просто по пути заспавнит экшон, который придет потом в стор. В точности то же самое и в приведенном выше решении — просто экшен заспавнится несколько раньше. Единственное, что нам нужно — чтобы сама последовательность экшенов не нарушалась — ведь состояние стора больше ни от чего не зависит! Если последовательность обработки экшенов неизменна — то и состояние стора на каждом шаге будет тем же. А когда и как запускаются промисы — вобщем-то не важно, это деталь реализации, которая не влияет на результат.
> Но почему-то мне это кажется не решением

Ну это странное мнение, учитывая что attribute directives предназначены _именно_ для решения таких проблем. То есть вот он инструмент специально для этого заточенный.

> UPD: Кстати, а HostBinding поддерживает async pipe?

В каком смысле? Пайпы же находятся в темплейте. Если вы хотите сделать так, чтобы какоето проперти, которое использовалось без асинка, начало использоваться как бы с асинком — то насколько я знаю, так нельзя, надо делать subscribe и менять обычное значение. Но если оно и так уже было с асинком — то вы можете засунуть туда observable.
Я не понял. И так, у вас есть компонент, у компонента какие-то пропсы. Вы хотите сделать в точности такой же компонент с таким же видом, но чтобы в нем не было пропсов, а он сам подгружал данные (с сервера, редаксом, еще как -либо — не важно)? Все так? Тогда делаете attribute directive и выставляете инпуты хоста через @HostBinding, нет?
То есть, он все-таки отвечает за уи?

Information

Rating
Does not participate
Registered
Activity