All streams
Search
Write a publication
Pull to refresh
@Druuread⁠-⁠only

User

Send message
> Мне вам что, сюда проект скопипастить?

Зачем проект? Просто пример.

> Ну и вы так и не сказали, решение чего.

Так пусть будет с теми же кнопками. Просто структурировано так, как это будет в реальности, на нормальных компонентах, с выделенной моделью.

> Это безобразие и перекочевало в головы пхп-шников, пришедших в первый ангуляр

Вот в ангуляре как раз в отличии от реакта от пхп ничего не было :)
То же двухсторонний биндинг — сугубо антипхпшная концепция.

> А мы помним про composition over inhertitance.

В этой имеется ввиду фразе не та композиция, которая функциональная.

> Вы можете себе представить что-то такое:

Вот хороший пример. В случае с ф-и я могу ф-ю прямо в коде использовать, без промежуточного объявления.

> Тогда я не понимаю, чем вам не нравится ref/findDOMNode.

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

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

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

Ну так и HTML — это формальный язык описания интерфейса.

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

Так наследованием — все работает. Но вам это не нравится по синтаксическим соображениям.

> В первом ангуляре были только директивы. Которые «выглядели» как компоненты. С сохранением нужной гибкости: не нужна обертка — ставим replace.

Еще раз — вы говорите что раз есть реплейс, то атрибут-директивы не нужны. Но они там были. Почему?

> jsx — это не какой-то шаблон, который «живет отдельно от модели», это описание композиции функций — посмотрите выхлоп бабеля в конце концов уже.

Шаблоны ангуляра — это тоже описание композиции функций. Они компилируются так же как jsx в чистый джаваскрипт, там хтмля не остается. Ну и на пехепе тоже композиции функций писали :)

> Я вроде показал — создание Dynamic на лету.

Ну вы же создаете ее все равно в промежуточном виде. Как сразу в jsx? Или он превратился в тыкву тут?

> Вы ерунду-то не несите, очевидно же, что задача решена, а вам обидно.

Нет, не решена. Сделайте, чтобы работало с любой компонентной.

> А еще лучше — приведите конкретный пример для переноса на реакт.
@Directive({
    selector: "[mixin]",
})
export class MixinDirective implements OnDestroy  {
    @HostBinding("style.backgroundColor") color = "red";

    subscription = Observable.interval(1000)
        .subscribe(x => this.color = this.color === "red" ? "black" : "red");

    ngOnDestroy = () => this.subscription.unsubscribe();
}

> Ну подкиньте хоть пример, что вам накатать. А то я придумать не могу, у меня в компонентах бизнес-логика не сидит как-то.

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

> Вы до сих пор живете внутри html-шаблона, тогда как jsx !== html.

Слушайте, ну на эту маркетинговую чепуху ведутся только те, кто на php ни разу не писал. Называть-то вы это можете как угодно, но суть явления от смены нвзвания не меняется, и jsx выглядит в точности как генерация портянок html коды из пыха, то самое прокидывание пропсов кстати — оно и в php было, там же тоже функциями генерили, render-методы у классов :)

> Принцип этот не оттуда

Именно оттуда :)
Это сделано для того, чтобы логика клиент-сайда максимально напоминала логику сервер-сайда. Именно сервер-сайд у вас работает по принципы state -> view. Ну а вдом — да, деталь реализации, необходимая для того, чтобы «писать на клиенте как на сервере».

> Да, но директивы можно было делать и по имени тэга, и именно replace я имел в виду.

Вы говорите, что директивы-атрибуты нужны как костыль из-за отсутствия replace. В первом ангуляре было и то и то. Что-то не сходится?

> Вы не правильно используете синтаксис — интерфейс компонента не объявляет аттрибут blink.

Я ж вам показываю ангуляроподобный синтаксис. А задача — сделать как в ангуляре, все верно.

> Еще раз вернемся к jsx !== html — это более высокий уровень абстракции.

Да наоборот все. Шаблоны — это ДСЛ, а в jsx вы пишете по факту на хост-языке.

> Я могу вам на лету собрать мигающий компонент из немигающего — это будет правильно, и по сути тоже самое. Синтаксис другой только.

Ну хорошо, пусть другой. Но объявлять отдельно блинкающуюся компоненту я не хочу, я хочу сразу в jsxe как-то это сделать. Как будет ваш код без объявления промежуточного AlreadyBlinkingComponent выглядеть? Что если у меня таких директив не одна, а 10? мне 2^10=1024 таких компонент объявлять? Кроме того, ваша компонента изначально позволяет менять background-color, то есть заточена работать с blinked. Я хочу возможность работы с любой компонентной, даже с той, у которой background-color в пропсах нет.
> Вы про стейт компонента или что? Ну выносите операции над стейтом в функции вида state => state и передавайте их в setState.

Ну вы, пожалуйста, продемонстрируйте, что получится. А то однострочники меня уже давно не возбуждают, я для этого слишком стар, извините уж.

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

Все несколько наоборот. Это реакт сам по себе был придуман как костыль для портирования сервер-сайд логики с php на клиент. Оттуда jsx (старательно мимикрирующий в своей семантике под php), оттуда принцип «перерендерить все» (именно это происходит при классическом запросе браузера к серверу — сервер вам генерит каждый раз всю страницу заново), отсюда vdom (чтобы этот подход не слишком сильно тормозил), изоляция стейта да и все остальное.

> Если бы компоненты ангуляра могли рендерить другие в качестве хоста (к слову, как это было в первом)

Так в первом ангуляре тоже были аттрибут-селекторы, несмотря на наличие replace.

> Но раз уж просите —

Вы просто лишний аргумент добавили, я же совсем не то просил. Сделайте аналог аттрибут-директивы, которая будет каждую секунду, например, менять background хоста: черный-красный-черный-красный и т.д.

Чтобы я мог вместо <Component/> написать <Component blink/> — и оно у меня мигает.

> Когда в то же время достигается совершенно тот же эффект.

Так я же процитирую вас:
> к решению задачи с кнопками _в таком же виде, как она решена на реакте_.

Вот и я вас прошу не достичь «такого же эффекта», а решить в том же виде, как на ангуляре. Просто потому, что я «изначально так поставил задачу».

> Но если вам совсем уж надо — есть ref, findDOMNode и все вытекающие.

Ну нет, это не по правилам. Вы же не разрешали писать кастомные декораторы? Значит, никаких refов с findDOMNode — это все ненативно, бяка и т.д., какие вы там аргументы приводили против подобных решений.

> Сравните, пожалуйста, примеры выше еще разок.

Да сравнивал, они практически построчно совпадают (я о варианте с наследованием).
Там опечатка, component1 вместо component2.
вот рабочий вариант, целиком:
export interface IComponent {
    data: string;
}
export const IComponentToken = new InjectionToken<IComponent>("IComponent");

@Component({
    selector: "component1",
    template: `<span>{{ data }}</span>`,
    providers: [{ provide: IComponentToken, useExisting: forwardRef(() => Component1) }]

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

@Component({
    selector: "component2",
    template: `<div>{{ data }}</div>`,
    providers: [{ provide: IComponentToken, useExisting: forwardRef(() => Component2) }]
})
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";
    }
}


> Что переписывать-то? Какую логику? Логику установки иконки в кнопке?

А вы только обертки над кнопками пишете? В реальном мире у большинства компонент есть нетривиальная логика.

> Я не понял, что вы имеет в виду. Можете пример на ангуляре, чтобы понятней было?

Ну attribute directive, мы же о них сейчас как раз говорили.

> Мало того, что вы на ангуляре не пришли к решению задачи с кнопками в таком же виде, как она решена на реакте.

С чего вы взяли, что она должна решаться _в таком же виде_? Главное, чтобы не сложнее, а в таком или не таком — уже не важно.
Надо 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 так и делается. Там не экшены применяются/отменяются, а просто заменяется весь стейт.

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

Information

Rating
Does not participate
Registered
Activity