Angular: 提案:输入为 Observable

创建于 2015-12-08  ·  183评论  ·  资料来源: angular/angular

对不起,我英语不好。

@Input属性值由父组件提供。 这些变化是异步发生的。
如果子组件中的 Input 属性发生了更改(它具有作为自己的属性的属性),则其更改检测器永远不会注意到它。

目标

  • 父级的输入数据和子级的输入属性应该同步。
  • 开发人员应该了解输入属性是异步更改的。

    提议

@Component({ selector: "child" })
class Child {
  @Input("input") inputValue: Observable<T>;

  ngOnInit() {
    this.inputValue.map((value)=>...);
  }
}

@Component({
  template: `
  <child [input]="valueToChild"></child>
  `
})
class Parent {
  valueToChild: T;
}

上面的代码不起作用。 目前,要接收输入为Observable<T> ,我必须像下面这样写。

@Component({ selector: "child" })
class Child {
  @Input("input") inputValue: Observable<T>
}

@Component({
  template: `
  <child [input]="valueToChild"></child>
  `
})
class Parent {
  valueToChild: Observable<T> = new Observable<T>((observer)=>{
    ...
    observer.next(val);
  });
}

示例: http :

这很有效,但不是必需的。 Parent的输入数据本来就是一个简单的数据。

我认为这个提议让我们很高兴。

谢谢。

core inputs / outputs feature Needs Design

最有用的评论

亲爱的 Angular 团队。 请给我们一些在 2020 年值得期待的东西 :-)

另外@jcomputer解决方案正是我想要的。 不太喜欢不同的装饰器名称,但也许添加一个类似于@ViewChild具有{ read }的参数是可行的。 例如:

@Input({ observable: true }) 
@Input({ asObservable: true }) 
@Input({ asSubject: true })

所有183条评论

@ laco0416 - 你的英语很好,别担心!

我非常喜欢这个想法,这是我们之前讨论过的。 它还与https://github.com/angular/angular/issues/4062 (观察视图事件)和https://github.com/angular/angular/issues/5467 (来自父母的可观察子事件)很好地匹配

重要的是要记住,不是每个人都想使用 Observables(这些人错过了!),所以我们必须为这两个用例提供选项,所以我们不太可能将@Input()直接变成 Observable。 我确实认为使用@ObserveInput()可能会奏效,我们将在发布测试版之后讨论我认为其中一些更有趣的功能。

与此同时,这里有一个基本的(和_非常_实验性的!!!不要真正这样做)这个想法的实现。 这是你在概念上的想法吗? http://plnkr.co/edit/Nvyd9IPBZp9OE2widOcW?p=preview

我认为在子组件中更改输入属性是一个非常糟糕的主意。 输入属性应为“只读”。 您的数据应该始终从父级流向子级(并且永远不会反向)。

@alexpods我相信这里的想法正是这样 - 它 _listening_ 对输入属性的变化 _as_ 一个 Observable,而不是向上游发送值,就我而言,这绝对没问题。

@robwormald

你的英文很好,别担心!

谢谢! 我就放心了

你的@ObserveInput正是我想要的!
此外, @Input没有重大变化。 我认为这是一个非常好的解决方案。

@alexpods我也是。

它以 Observable 的形式监听输入属性的变化,而不是向上游发送值,就我而言,这绝对没问题。

我的想法和罗伯一样。

@ laco0416哦,抱歉误会了。 “如果在子组件中更改了输入属性”这句话让我感到困惑。

我不知道我是否应该在这里发表评论,或者我是否应该开一个新问题。 如果我将请求添加到错误的位置,请告诉我。

我一直在尝试(但直到现在,失败)编写这样一个装饰器,然后我偶然发现了@robwormaldplunkr ,它_几乎_完美地工作(但不完全)。

这种方法让我兴奋的是,它正在利用ngOnChanges生命周期挂钩。
我想看到的是将 _all_ 生命周期钩子暴露为 Observables 的某种方式,即onChanges$: Observable<{[key: string]: SimpleChange}>onInit$: Observable<{}>等。

将所有生命周期钩子作为 Observables 使用将帮助我接收所有的东西 ;-)

有任何更新吗?

AFAIK,没有。
@robwormald你有什么消息吗?

我知道这很旧,但这会很棒! @robwormald对此有何评论?

我很乐意提供更改@Input属性值作为 Observable,就像@robwormald@ObserveInput装饰器所做的那样。 让父组件通过 Observables 并不总是可行的,尤其是在您迁移现有(Angular 1)应用程序时。 但是,无法在单个组件中“包含” Observable 使得利用 RxJS 的强大功能和优雅变得更加困难。

不幸的是,Rob 说不要使用这个版本的@ObserveInput并不是在开玩笑。 我遇到的问题是 Observables 是在“类级别”上创建的(如果该术语有意义的话),因此在组件的所有实例之间共享。 这显然不好。 在实例化时创建 Observable 对我来说也不起作用。 在这种情况下,Angular 似乎没有正确连接更改检测。

有没有人管理过更好的@ObserveInput或者有关于官方支持的任何消息?

@lephyrus虽然官方的@ObserveInput装饰器会非常好,但为了获得更改@Input属性值的 Observable 并不是绝对必要的。 装饰者只是非常优雅的“糖”。

已经有一个生命周期事件ngOnChanges 。 在ngOnChanges我们可以使用 rxjs Subject来创建可观察的变化,即:

<strong i="13">@Input</strong> inputString: string;
private Subject<string> inputString$ = new Subject<string>;

ngOnChanges(changes: { [key: string]: SimpleChange }) {
    if (changes.hasOwnProperty('inputString')) {
        this.inputString$.next(changes['inputString'].currentValue);
    }
}

constructor() {
    inputString$.subscribe(x => {
        console.log('inputString is now', x);
    });
}

或者,如果您想要更可重用的东西,您可以创建一个基类,您的组件extends

import { SimpleChange } from '@angular/core';
import { Observable, ConnectableObservable, Observer } from 'rxjs';

export interface TypedSimpleChange<T> {
    previousValue: T;
    currentValue: T;
}

export class ReactiveComponent {
    private changesObserver: Observer<{ [key: string]: SimpleChange }>;
    private changes$: ConnectableObservable<{ [key: string]: SimpleChange }>;

    constructor() {
        this.changes$ = Observable.create((observer: Observer<{ [key: string]: SimpleChange }>) => this.changesObserver = observer).publishReplay(1);
        this.changes$.connect();
    }

    public observeProperty<T>(propertyName: string): Observable<TypedSimpleChange<T>> {
        return this.changes$
            .filter(changes => changes.hasOwnProperty(propertyName))
            .map(changes => changes[propertyName]);
    }

    public observePropertyCurrentValue<T>(propertyName: string): Observable<T> {
        return this.observeProperty<T>(propertyName)
            .map(change => change.currentValue);
    }

    ngOnChanges(changes: { [key: string]: SimpleChange }) {
        this.changesObserver.next(changes);
    }
}

...可以按如下方式使用:

@Component({
    ...
})
export class YourComponent extends ReactiveComponent {
    @Input() inputString: string;

    constructor() {
        super();
        this.observePropertyCurrentValue<string>('inputString')
            .subscribe(x => console.log('inputString is now', x));
    }
}

在官方@ObserveInput装饰器可用之前,我一直在使用这种方法。

谢谢你,@wmaurer。 您的ReactiveComponent非常受欢迎,并且使用无可挑剔的代码和安全输入来启动 - 真的很棒! 重要的是,它在测试中也表现良好,并且仍然允许使用OnPush更改检测策略。 我现在很高兴等待“官方糖”。 (此外,当我弄清楚为什么必须使用Observable.create()逻辑时,我相信我会学到一些东西 - 我还没有时间研究它。)再次:谢谢! :眨眼:

@lephyrus不客气,gärn gescheh ;-)

我使用Observable.create()来获取Observer以便能够执行next() 。 我可以使用Subject既是Observable又是Observer ,但我相信“暴露” Subject ( Observer )。

@DzmitryShylovich否。本期提出的功能是只读和事件驱动的数据传递。

@ObserveInput会超级酷! :+1:

谢谢@wmaurer的一个很好的例子。 不过我有一个问题。 我希望能够使用对象而不是字符串作为可观察对象。

例如

@Input() chartConfig: ChartConfig;

constructor(private _reportService: ReportService) {
        super();
             this.observePropertyCurrentValue<string>('chartConfig')
            .subscribe(changedConfig => this.updateChart(changedConfig));
 }

export class ChartConfig {
    public id: string;
    public type: any;
    public data: any;
    public labels: any;
}

但是 this.updateChart 和 ngOnChanges 没有被调用。 如何从一个简单的字符串扩展您的样本以观察对象?

@ChrisWorks它也应该适用于对象:

this.observePropertyCurrentValue<ChartConfig>('chartConfig')
            .subscribe(changedConfig => console.log(changedConfig));

我经常这样做,所以如果这不起作用,我猜你的组件的输入有问题。

@wmaurer ,感谢您的回复。 您能否使用使用“配置”对象的工作版本来扩展您的示例? 我根本无法让我的工作。 一个示例 Git 存储库? :)

@ChrisWorks它真的应该按observePropertyCurrentValue不区分字符串输入和对象。
这是我制作的一个非常古老的 ng2 beta 0 项目,其中输入具有所有不同类型,而不仅仅是字符串:
https://github.com/wmaurer/todomvc-ng2-reactive
例如https://github.com/wmaurer/todomvc-ng2-reactive/blob/master/src/app/todo-item/todo-item.component.ts

+1 如此基本的用例,简直不敢相信它还没有被排序!

我终于得到了最好的解决方案,它无需重复和 AOT 编译即可工作:) 秘诀是将@Input()与第二个装饰器结合起来。

装饰师:

import { ReplaySubject } from 'rxjs/ReplaySubject'                                                                                                 

const subjects = new WeakMap()                                                                                                                     

export function ObservableInput() {                                                                                                
  return (target, propertyKey) => {                                                                                                                
    delete target[propertyKey]                                                                                                                     

    Object.defineProperty(target, propertyKey, {                                                                                                   
      set(value) {                                                                                                                                 
        this[propertyKey].next(value)                                                                                                              
      },                                                                                                                                                                                                                            
      get() {                                                                                                                                      
        let subject = subjects.get(this)                                                                                                           
        if (! subject)  {                                                                                                                          
          subject = new ReplaySubject<any>(1)                                                                                                      
          subjects.set(this, subject)                                                                                                              
        }                                                                                                                                          
        return subject                                                                                                                             
      },                                                                                                                                           
    })                                                                                                                                             
  }                                                                                                                                                
}                                                                                                                                                  

用法:

class SomeComponent {
  @Input() @ObservableInput()                                                                                                                    
  public index: Observable<number>
}                                                                                                               

编辑:我现在已经把它变成了一个图书馆。 很高兴收到改进它的想法。 添加一个新方法,用Observable补充现有的@Input 。 见https://github.com/ohjames/observable-input

如果我必须做这样的事情,我宁愿选择一个类似于 ngOnChanges 的主题: Subject<SimpleChanges>
如果您愿意,您仍然可以过滤并映射到一个特定的输入。

1 每个输入的主题似乎很多,而没有谈论删除类道具以创建 getter 和 setter 并且您的输入类型是错误的,并且执行input = value实际上会在可观察对象上发出值,而您不是完成你的主题。

你可以在这里找到我的想法的一个实现示例,装饰器方法是实验性的,但我认为使用继承方法应该是非常安全的(请记住,我现在只是在这里展示,它没有被使用)。

@ghetolay主题是否完成并不重要,当视图关闭时,不再有对流的订阅,并且它可以由 GC 处理。 在 observable 上发出值基本上是重点。 angular 编译器目前不检查输入属性类型,希望当它做一些更正式的事情时将可用 ;)

changes$BehaviorSubject接口泄露给组件,理想情况下它应该被输入为Observable<...> ,除此之外,它似乎合理,如果更详细一点。

至于许多科目的问题……无论您使用的是n科目还是1您仍然有n订阅。 当您有 1 个主题时,您最终会为每个订阅添加一个地图操作符和一个过滤器操作符,这样做的性能开销不会比仅使用n主题少多少,甚至可能更糟。

忽略输入参数本身的类型,基于装饰器的版本比基于anySimpleChange类型为 observables 的使用者提供了更类型安全的 API。

@ohjames

当视图关闭时,不再有对流的订阅,它可以由 GC 处理

您假设所有订阅都将绑定到视图并获得与视图相同的生命周期。 我没有对 Observable 将如何被消耗做出任何假设。

在 observable 上发出值基本上是重点。 angular 编译器目前不检查输入属性类型,希望当它做一些更正式的事情时将可用 ;)

我的意思是用户,用户仍然可以访问和设置属性。 所以它会将属性设置为一个数字,除非它被输入为 Observable 并且这永远不会设置属性而是让它发出。 这让我非常困惑。

至于很多科目的问题……不管你是用n个科目还是1个,你仍然有n个订阅。 当您有 1 个主题时,您最终会为每个订阅添加一个地图操作符和一个过滤器操作符,这样的性能开销不会比仅使用 n 个主题少多少,甚至可能更糟。

不会在这里进行这种辩论,因为我们很快就会失去对主要目的的关注。

我已更新为不公开Subject并构建了一些内容以将输入添加到更改中。
我没有使用BehaviorSubject而是使用普通的Subject因为我没有将那些 Observable 视为价值持有者,而是随着时间的推移而发生变化。
我刚刚发现了一个缺陷:使用异步管道时,第一个值不会发出,因为异步管道会在第一次发出后订阅。 我稍后会更新我的代码以正确处理这种情况。

这是链接: https :

您假设所有订阅都将绑定到视图并获得与视图相同的生命周期。 我没有对 Observable 将如何被消耗做出任何假设。

好的,我明白你的意思,完成 observables 将强制关闭所有订阅,但这并不是我真正担心的事情。 在我们的应用程序中,99% 的订阅是通过异步管道发生的,并且在少数情况下不必手动关闭,因为它们通常来自 Redux/etc 通过 combineLatest/switchMap 的外部 observables。

不会在这里进行这种辩论,因为我们很快就会失去对主要目的的关注。

那么你提出了一个无效的批评,所以有必要反驳那个批评。

~也不能通过继承使用你的类,AOT编译器生成的代码不会调用父类中的生命周期方法,见https://github.com/angular/angular/issues/12756#issuecomment -260804139,你将需要为该类的所有使用者添加ngOnDestroy() { super.ngOnDestroy() }和另一个ngOnChanges 。~ <-- 似乎已从 Angular 4.4 开始修复,但有某些限制。

我已经回顾了我对使用异步管道的担忧,并且由于您仍然可以访问输入,我认为这种用法没有任何意义。 如果您只想绑定模板上的输入值,您可以直接绑定到输入,无需在此处使用任何 observable 和 async。 如果您想从中构建一个新的可观察对象,那么您应该能够轻松获得您想要的内容(可能startWith某处使用

所以我认为我之前的陈述是可以的:

我没有使用 BehaviorSubject 而是一个普通的 Subject,因为我没有将那些 Observable 视为价值持有者,而是随着时间的推移而发生变化。 如果您需要访问输入值,该属性仍然存在,您可以同步引用它。

这段代码是高度实验性的,它只是某种 poc,我只在那个 stackblitz 上玩过它。
所以我不应该说它很安全,抱歉,它可能不适用于您所说的 AOT。
刚刚尝试过,这两种方法目前都适用于 AOT(angular v4.3.6,cli v1.4.1)。

我的主要观点是,从包含所有输入更改的changes observable 开始,您可以使用运算符执行任何您需要的操作:当任何输入更改时做出反应,当只有 1 个输入更改时,当任意数量的输入时更改,当它从特定值更改为另一个值时...
你可以编写你想要的 observable。

此外,只有 1 个主题需要管理实现和 1 个属性供用户使用,而每个输入则有 1 个。

我可以改变的是只发出新值而不是SimpleChanges对象,因为用户可能可以使用pairmap来获得与SimpleChange相同的结构SimpleChanges所以分解它只是为了稍后重新组合类似的东西是愚蠢的。

PS:

那么你提出了一个无效的批评,所以有必要反驳那个批评。

对你无效

当我们提到性能时,这不是个人观点,这些是客观可衡量的事实。 你觉得 3 个科目比 6 个额外的操作员更多的开销,当然也许你是对的,但在你进行一些测量之前,没有合理的理由相信这一点。

至于对许多输入做出反应……当然你可以只使用 switchMap 或 combineLatest 或任何其他设计用于处理多个 observable 的操作符。 您对此发表了非常大胆和全面的声明,如果您真的想继续这个对话,那么让我们从这个问题跟踪器中删除它。

这是一个具有不同缺点和优点的版本,但它需要一个基类:

import { OnChanges, OnDestroy, SimpleChanges } from '@angular/core'
import { BehaviorSubject } from 'rxjs/BehaviorSubject'
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/distinctUntilChanged'

export class ChangeObserver implements OnChanges, OnDestroy {
  protected ngChanges = new BehaviorSubject<object>({})

  ngOnChanges(changes: SimpleChanges) {
    const props = { ...this.ngChanges.value }
    for (const propName in changes) {
      props[propName] = changes[propName].currentValue
    }
    this.ngChanges.next(props)
  }

  ngOnDestroy() {
    this.ngChanges.complete()
  }

  changes<K extends keyof this>(key: K): Observable<this[K]>
  changes<V>(key: string): Observable<V>
  changes(key: string) {
    return this.ngChanges.map(props => this.ngChanges.value[key]).distinctUntilChanged()
  }
}

并使用:

class MyComponent extends ChangeObserver {
  @Input() public field: string
  // typescript knows this is Observable<string>
  field$ = this.changes('field')

  @Input() private privField: number
  // typescript can't access private stuff from outside the class so we need to help it out
  privField$ = this.changes<number>('privField')
}

不确定我是否误解了这个问题,但是异步管道不能解决这个问题吗?
[inputVar]="observableValue | async" ..

@najibla你误解了,输入不是可观察的,所以异步管道没有帮助。

@ohjames在我的情况下很好,它确实可以使用异步管道进行 observable。 当我从父组件更新 observable 值时,该值会反映在子组件中。

@najibla这个 github 问题是关于将@Input属性转换为可观察的,这与您所描述的情况有细微的关系。

即您不需要将输入属性转换为视图中的可观察对象,无论组件是否使用可观察对象响应输入属性更改,都应该在组件本身中隔离。 否则,如果您决定将属性作为可观察对象使用,则必须更改组件的界面,这与 angular 通常的“可观察优先”方法不一致。

@icepeng实际上你没有完全正确,这是关于将标准输入转换为可观察的。 所以新模式实际上是:

<app-child [prop]="prop$ | async"></app-child>

...然后它提供了一种方法来访问prop内部的AppChildComponent作为 Observable,即使它不是一个可观察的(或者如果它是一个可观察的,你会得到一个可观察的 observable )。 现在当然,如果你在你的例子中首先有prop$那么它可能看起来不明智(你现在已经可以通过输入传递 observable )。 然而,当传递的输入还不是 observable 时(例如index中的ngFor ),它提供了很多价值。

传递带有输入的 observable 的问题(现在完全有可能)是它不适用于OnPush

@fxck好吧,当 observable 发出时不会调用ngOnChanges是对的(因为输入保持不变),但是只要您使用异步管道订阅通过@Input传递的 observable OnPush将正常工作,因为异步管道手动触发子组件的更改检测器。

是的,它是针对@icepeng 的。

@ohjames @fxck我不知道现在可以传递 observable,抱歉提供了错误的信息。

由于可以直接传递 observable,我不确定@ObserveInput是否有用。
我认为如果可以更改的值不是 Observable ,那已经是一个问题了。
只是个人意见。

@icepeng考虑一下:

<ng-container *ngFor="value in (values$ | async); let i = index">
  <child-component [value]="value" [index]="i"></child-component>
</ng-container>

如何使index在这里作为可观察对象可消费而无需跳过箍?

或者也考虑您正在编写第三方库,并且不想强迫您的库的使用者将 observable 传递给您的组件(许多用户对流不满意并避免使用 rxjs)。

还有许多其他情况下这是有用的。

@ohjames index将是每个child-component静态值,所以我认为它不必是可观察的。 但是,现在我明白人们为什么想要这个功能了。 您的解决方案似乎很适合编写第三方库。

BehaviorSubject方法似乎更好,如果 Angular 核心提供这样的@NgChanges()装饰器就好了。

{
  @NgChanges() ngChanges$: BehaviorSubject<{ prop: string }>
  @Input() prop: string;

  ngOnInit() {
    this.ngChanges$.subscribe(changes => console.log(changes.prop));
  }
}

这可以实施吗?

缓冲区长度为 1 的 ReplayObservable 会更可取,当您还可以访问该属性时,就不需要 BehaviourSubject 的值语义。

至于是否可以实现,请检查许多不同实现和选项的问题历史记录。 没有一个很好,有些完全坏了,要正确地做到这一点,我们需要链接的 PR 或类似的东西。

我的 2 美分:由于 object.observe 已被贬值以支持代理,我的解决方案是扩展代理(是的,我不知道,我从构造函数返回它的原因),然后听属于此的任何键通过 $get(keyName) 方法将对象作为 Observable。 您可以将它与任何对象一起使用,而不仅仅是角度类。

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

/**
 * Extends this class to be able to observe any key affectation
 */
class ObservableProxy {
    private changes: Subject<[PropertyKey, any]> = new Subject<[PropertyKey, any]>();

    constructor() {
        return new Proxy(this, {
            set: (object, key, value) => {
                this.changes.next([key, value]);
                object[key] = value;
                return true;
            }
        });
    }

    public $get<K extends keyof this>(key: K): Observable<this[K]> {
        const value: this[K] = this[key];

        const startWith: Observable<this[K]> = Observable.of(value)
            .filter((initialValue) => // remove this filter if you dont mind receiving an undefined value on subscription
                initialValue !== undefined;
            );

        return this.changes
            .filter(([changedKey]) => {
                return changedKey === key;
            })
            .map(([changedKey, nextValue]) => nextValue)
            .merge(startWith);
    }
}

例子:

class User extends ObservableProxy {
public name: string;
}

const user: User = new User();
user.$get("name").subscribe((value)=> {
console.log(value);
})

user.name = "toto"; // prints console.log("toto")

@robwormald ,这个问题有什么吸引力吗? 它肯定会大大有助于使事情更易于使用。

@bryanrideshark获得支持的最佳机会是通过https://github.com/angular/angular/issues/10185我认为,很惊讶 PR 与此问题无关。

直到我们运送 Ivy 之后,这才会发生
2018 年 2 月 21 日星期三上午 7:56,James Pike通知@github.com 写道:

@bryanrideshark https://github.com/bryanrideshark最好的机会
这被支持是通过#10185
https://github.com/angular/angular/issues/10185我想,很惊讶
PR 与此问题无关。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/angular/angular/issues/5689#issuecomment-367372931
或静音线程
https://github.com/notifications/unsubscribe-auth/AAgpkvtGA4w8uHgiz0QsdYwBgqgM2EpAks5tXDyWgaJpZM4Gwr8f
.

你的英语很棒,你的角度也更大。

@pldin601你的方法不支持 AOT 编译代码,所以会导致大多数人的生产代码出现巨大的内存泄漏。 请查看我的observable-input包,它与 AOT 配合良好。

@pldin601您的代码的问题在于它动态添加了ngOnDestroy生命周期方法。 如果编译器可以静态推断它的存在,则 AOT 编译代码仅调用ngOnDestroy 。 因此,您的方法只有在使用装饰器的每个组件还定义了ngOnDestory (如果不需要它可以为空)时才有效。 在所有其他情况下,它会泄漏订阅,防止组件被垃圾收集。

我想这会在第 3.2 节完成后发生(希望能考虑到这一需求)?
https://is-angular-ivy-ready.firebaseapp.com/#/status

Ivy 会成为 7.0.0 的一部分吗?

我已经实现了一个装饰器,它可以将一个属性绑定到一个可观察的伴随属性。 它可以用于角度输入属性,也可以用于任何其他属性。 它的灵感来自@ohjames@ObservableInput()装饰器,但通过使用不同的属性来提供 Observable,避免了由属性 getter 和 setter 之间的类型不匹配引起的可能问题。

https://github.com/PSanetra/bind-observable

例子:

class MyClass {

  @BindObservable()
  public myProp: string = 'initialValue';
  public myProp$!: Observable<string>;

}

const myInstance = new MyClass();

myInstance.myProp$.subscribe(console.log);

myInstance.myProp = 'newValue'

此代码将值'initialValue''newValue'打印到控制台。

到目前为止,关于何时发布的任何更新observable-input包似乎是最优雅的,没有破坏任何东西+没有子类化。 @ohjames在这一点上有多稳定/经过良好测试?

我认为官方支持会更成熟,将保证跨版本的持续支持和更流畅的开发体验/将鼓励开发人员使用完整的 rxjs* 并优化推送更改检测(我认为这应该是一个核心的突破性功能)

*不影响与不可观察输入的兼容性或需要额外的混合输入样板,即:setter 在主题上调用 next

在有和没有 aot 的情况下,在 4、5 和 7 角使用 observable-input。 它似乎运作良好。 我很确定我在写它时模糊地知道我在做什么。

希望他们将它添加到框架中并鼓励其使用 每周 2 次下载 npm 与它提供的优势相比是不够的,如果他们添加它,我敢打赌人们会看到遵循这种方法很简单

我建议遵循用户定义的异步输入 API observer

decalre function AsyncInput(bindName?:string) : <T extends Observer>(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<T>) => any

用户可以将其用作

@AsyncInput()
asynchronusProperty1 = new Subject();

@AsyncInput()
asynchronusProperty2 = new ReplySubject(1);

@AsyncInput()
asynchronusProperty3 = new UserObserver(); // where UserObserver implement `Observer` interface

Angular 内部可以在值发生变化时调用observer.next(newValue)

也不要忘记@HostBinding支持!

这应该与构造函数@Input注入相结合,使其更加

class MyComponent {
  inputData$: Observable<Data>;
  constant: string;

  constructor(
    @Input() inputData$: Observable<Data>,
    @Input() constantString: string
  ) {
    this.inputData$ = inputData$;
    this.constant = constantString;
  }

}

@benneq值得注意的是它可能小到:

class MyComponent {
  constructor(
    @Input() private inputData$: Observable<Data>,
    @Input() private constantString: string,
  ) {}
}

@maxime1992是的,尽管您需要一个单独的装饰器将输入作为 observable,以区分 observable 从一个组件传递到另一个组件的情况。

@benneq @maxime1992参数名称不是装饰器元数据的一部分,如何映射?

@trotyl我想你可以使用@Input('paramName') private myParam: Observable<Data>

@trotyl @benneq是的,我不好。 没关系 :zipper_mouth_face:

我不知道是否有人已经张贴了这个解决方案,但这个是一个相当不错的解决方法

@ElianCordoba这与 HTML <input>无关。 这是关于角@Input()

这是一个不同但同样痛苦的问题@ElianCordoba。 https://github.com/angular/angular/issues/13248

我认为这是一个伪需求。 首先,输入装饰器可以接受一个 Observable 类型的值,其次,如果组件需要一个 observable 作为输入,它应该在接收到一个不可观察的值时显式地抛出错误,而不是悄悄地将其转换为 Observable 类型的值。

它与javascript中的隐式类型转换非常相似,可能会混淆甚至导致错误并且难以发现。

这不是为了消除错误的输入。 它是关于跨组件公开一个通用接口(这是不可观察的输入)。 这使您可以自由地引入 rx 功能,而不会破坏外部世界的接口。 另一个好处主要是提升可变性和内部状态,因为一切都将是输入的转换,这使得从那时起推动变化检测变得毫无意义。

相反,暴露一个需要 observables 的接口听起来很笨拙,并且仅仅因为你的组件这么说就强迫人们提供 of(value) 对我来说听起来很奇怪。

此外,它与静默转换无关,它是一个通过 rxjs 订阅更改的 API。 鉴于 angular.http 和路由器提供了可观察量,输入更改处理不提供超级尴尬,统一回调和 RxJs 的世界需要太多的样板。

并不是说我不喜欢上面的一些聪明的解决方案,但有时只是对“标准”方式的轻微重组就足以解决这里的真正问题,即缺乏清晰度/笨拙。 另外,我仍然希望有一天能有一种“官方”的常春藤后方式来做到这一点。

@Input('isOuterPanel')
set isOuterPanel(value: CheckoutPanelHeaderSettings)
{
    this.inputs.outerPanel$.next(value);
}

@Input('config')
set config(value: CheckoutPanelHeaderSettings)
{
    this.inputs.config$.next(value);
}

// observables for <strong i="6">@Inputs</strong>
inputs = {
    config$: new BehaviorSubject<CheckoutPanelHeaderSettings>(1),
    outerPanel$: new BehaviorSubject<CheckoutPanelHeaderSettings>(1)
};

然后你可以把类似combineLatest(this.service.magicInput$, this.inputs, config$)......东西与 RxJS 结合起来。

BehaviorSubjectReplaySubject有效。 BehaviorSubject 通常更安全、更可预测。

  • BehaviorSubject - 如果您有默认值,请使用
  • ReplaySubject - 如果您忘记设置输入值,observable 将不会发出

这不仅有助于代码的清晰度,而且因为我将所有输入组合在一起,我只需键入this.inputs.并自动完成即可轻松查看什么是“纯”可观察输入。


你可以通过这个更进一步的类型安全(主要是为了好玩)。

// define this globally to 'unwrap' a property 'inputs' with an object of ReplaySubject / BehaviorSubject
export type InputTypes<T extends { inputs: { [key: string]: ReplaySubject<any> | BehaviorSubject<any> } }> = {
    [P in keyof T['inputs']]: T['inputs'][P] extends Observable<infer X> ? X : unknown;
}
// define a local 'InputType' helper above each component
type InputType = InputTypes<CheckoutSmartHeaderComponent>;

那么你不需要显式指定@Input属性的类型。

@Input('config')
set config(value: InputType['config$'])
{
    this.inputs.config$.next(value);
}

我本可以为组件创建一个 ObservableInputs 接口来强制执行inputs但决定不这样做,因为如果你搞砸了它无论如何它都不会编译。

@simeyla太多样板。

我决定把我自己的装饰器放在那里。 这是我的第一个 npm 包,所以我确定我遗漏了一些东西,但这里是: https: //www.npmjs.com/package/@ng-reactive/async -input

安装

npm install @ng-reactive/async-input --save

用法

import { Component, Input } from '@angular/core';
import { AsyncInput } from '@ng-reactive/async-input';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'app-hello',
  templateUrl: './hello.component.html',
  styleUrls: ['./hello.component.css']
})
export class HelloComponent {
  @Input() name: string;
  @AsyncInput() name$ = new BehaviorSubject('Default Name');

  constructor() {
    this.name$.subscribe(name => console.log('from async input', name));
  }
}

@mfp22真的很好,我看不出为什么不应该内置这样的东西。仅供参考,npm 上用于该软件包的 github 链接已过时,它转到此处:

https://github.com/mfp22/async-input/tree/master/projects/async-input

我看到这是一个很老的问题,但该功能非常棒,我真的很高兴能在 Angular 中看到它。

更酷的是,对于可观察的输入,您不需要OnChanges钩子 - 您可以使用pairwise rxjs 运算符来获取输入可观察的先前和当前值:

@Component({...})
class MyReactiveComponent {
  @ObservableInput() prop: Observable<string>; // Whatever syntax may be...

  // emits [prevValue, currValue] and no OnChanges hook yay!!
  propChanges$ = this.prop.pipe(pairwise());
}

https://github.com/rx-ts/ngrx/blob/master/src/utils/decorators.ts#L56 -L124

这是我个人的实现,它完美地工作并且非常棒,如果我们可以内置它,那真的很酷。

我已经发布了我在项目中个人使用的解决方案作为 npm 包:

https://github.com/futhark/ngx-observable-input

安装

npm install ngx-observable-input

用法

...
<image-item [url]="currentImageUrl"></image-item>
import { Component, Input } from "@angular/core";
import { ObservableInput } from "ngx-observable-input";
import { Observable } from "rxjs";

@Component({
    selector: "image-item",
    template: `<img [src]="url$ | async" />`
})
export class GalleryComponent {
    @ObservableInput() @Input("url") public url$: Observable<string>;

    ...
}

我看到这是一个很老的问题,但该功能非常棒,我真的很高兴能在 Angular 中看到它。

更酷的是,对于可观察的输入,您不需要OnChanges钩子 - 您可以使用pairwise rxjs 运算符来获取输入可观察的先前和当前值:

@Component({...})
class MyReactiveComponent {
  @ObservableInput() prop: Observable<string>; // Whatever syntax may be...

  // emits [prevValue, currValue] and no OnChanges hook yay!!
  propChanges$ = this.prop.pipe(pairwise());
}

这个问题也困扰了我一段时间,所以我进行了一些调查,并提出了与您在此处提到的相同的解决方案 (@gund)。

我已经实现了一个小的装饰器助手( @ObservableInput ),它在内部扩展了@Input装饰器并允许这种组合。 你可以在这里查看(也可以作为npm package )。 我一直在用简单的例子进行测试,但我没有时间将它用于扩展项目。 欢迎任何反馈:)

这是一个使用示例:

@Component({
  selector: 'app-test',
  template: `<span>{{ message$ | async }}</span>`
})
class TestComponent {
  @ObservableInput<number>('price') price$: Observable<number>;
  @ObservableInput<string>('name') name$: Observable<string>;

  message$ = combineLatest(this.price$, this.name$).pipe(
    map(([price, name]) => `${name} costs {price}`)
  );
}

这个组件可以这样使用:

<app-test [price]="price"
          [name]="name">
</app-test>

@aleics您确定这可以与 AOT 编译器一起使用吗?

@aleics您确定这可以与 AOT 编译器一起使用吗?

不,当然不是开箱即用,因为@Input属性没有明确定义。 但是你可以用CUSTOM_ELEMENTS_SCHEMA定义你的模块,就像使用 Web 组件一样,然后它应该编译。

这个问题已经开放5年了🤦‍♂。 我认为有必要使用它来促进反应式编程方法

常春藤终于来了,所以我猜有人有时间来研究这个吗? 拜托拜托拜托!

天哪,我同意这是非常需要的。 输入数据应作为反应源发出,您必须使用变通方法使其在 ngOnChanges 中工作

真的很期待这个

我意识到 Angular 是开源的,但是我认为由 Google 运营的项目应该能够在不到 5 年的时间内解决大量加星标和请求的问题是错误还是幼稚? 是否有一些我不知道的财务问题阻碍了 Google 雇佣更多的开发人员来开发 Angular?

@etay2000我不知道,我觉得很有趣,你可以用 rxjs 在 Angular 中完成 75% 的事情,然后对于一些基本的和常用的组件输入,突然之间你被迫进入程序世界。 你的项目中总是有这个重要的部分,它不能与可观察的世界一起工作,所以你总是被迫使用样板将它们粘在一起。 您永远无法完全实现项目的全功能方法。 所以这个问题已经开放了 4 年,这让我想起了缺乏实用主义,这让我离开了 Angular 而转向了其他框架,因为它非常有用。 如果你想对所有事情都使用 rxjs,也许 cycle.js 是一个更好的解决方案,如果你根本不想被迫使用 rxjs,也许 Vue 是。 一半时间必须使用 observables 而另一半时间不能使用它们只是没有任何意义。 将非常强大的抽象与高学习曲线集成在一起,然后无法完全集成它只是令人费解。

我觉得优先级链是这样的

谷歌内部用户的要求 -> 与编译器更快有关的任何事情 -> 与应用程序大小有关的任何事情 -> 任何不会让新手感到不安的事情 -> 开发人员真正想要的

虽然技术负责人会说他们同意这很重要,但事实是 Igor 和 Misko 倾向于在模板编译、视图绑定和渲染方面考虑大多数问题。 不管 Angular 的消费者说什么对他们很重要,解决方案总是改变模板系统,或者编写一个新的渲染器,或者让编译器变得更好。

来源:https://medium.com/@jeffbcross/jeffs-letter-to-the-angular-team-and-community-5367934a16c9)

看看一些最受好评的问题。

提案:输入为 Observable #5689

创建于 5 年前,相关团队成员的最后回复 - 5 年前,当前状态未知。

支持模板中的冷事件流 #13248

创建于 4 年前,相关团队成员的最后回复 - 1 年前,当前状态 - 等待 Ivy 登陆。

提案:提供组件生命周期挂钩作为 Observables #10185

创建于 4 年前,相关团队成员的最后回复 - 3 年前,当前状态未知。

建议:需要能够在组件声明中向宿主元素添加指令。 #8785

创建于 4 年前,相关团队成员的最后回复 - 2 年前,当前状态未知。

as local-val没有 *ngIf #15280

创建于 4 年前,相关团队成员的最后回复 - 2 年前,当前状态未知。

支持动态内容投影 #8563

创建于 4 年前,相关团队成员的最后回复 - 3 年前,当前状态未知。

ng-content 默认内容 #12530

创建于 4 年前,相关团队成员的最后回复 - 从未,当前状态未知。

然后是组件特性之类的东西,这将允许更高阶的组件,当然还有更多最受好评的问题,但我希望这些问题会得到解决(即解决,不一定期望它们在 Ivy 之后立即实现) ),因为它们与框架和反应性的日常使用有关,我认为没有人会争辩说,这是大多数人正在使用的(鉴于 ngrx 是最流行的状态管理库)。

我的应用程序充满了半成品(仅仅是由于技术限制,有些只能在核心中正确完成)“polyfills”和对大多数这些问题的黑客,希望有一天我能够投入一个有角度的本地替代品。 但不知何故,它似​​乎不会到来。

所以是的,假设一篇博客文章解决了一些最大的反应性/开发人员体验相关问题的状态,将会受到真正的赞赏。

我们尝试自己编写此功能的第一个版本怎么样? 我是
疫情期间无聊死了,谁想和我一起?

很多人已经这样做了,例如。 https://github.com/insidewhy/observable-input

其中大部分的问题在于,要有效地执行它们,您需要修改编译器或 angular 的其他内部部分。

那不算。 我的意思是让我们对角度做出适当的贡献
核。 毕竟,我们确实有可用的源代码。 实际上,我认为在这种情况下我们甚至不需要接触编译器。

@ganqqwerty在编写该库之前,我尝试在 Angular 中实现它。 不过 Angular 非常复杂。 公平地说,代码比 React 及其疯狂的 500 行长函数更具可读性和可理解性,但它是“复杂的”。 如果我认为这是值得的,我仍然会很高兴为实现这一目标付出努力。 但正是出于上面@fxck重复的原因,我认为它不值得。 Angular 团队不听取社区的意见,他们不断地关注优化和性能,而不关心 Angular 的总体可用性。 当一个框架迫使我为五年前演示的简单问题实施样板时,我并不关心它是否表现良好。 所以我决定不再关心 Angular,实现我需要的最小努力来支持我已经在工作的项目,并在所有未来项目中随时随地避免它。

我认为这不仅仅是 Angular 的问题,而是 Google 的许多产品的问题。 这个问题和@fxck上面列出的其他问题只是谷歌地方性问题的更多例子。 如果您查看 Dart 或 GoLang,您还会看到开发人员提出了众所周知的常见问题,并且这些问题会持续 5 年以上。

我同意应该添加这个,但是哇,对这个线程的仇恨有点多,我的意思是我们正在谈论添加一些可以节省几行代码的东西。 您现在可以使用带有@Input装饰的 setter 和您自己的主题来拥有可观察的输入:

https://github.com/angular/angular/issues/5689#issuecomment -507001686

如果这太多样板,我们已经有一些简单的第三方选项:

https://github.com/Futhark/ngx-observable-input

我对 Angular 的开发很满意,当然不会因为这样一个小(虽然理想)的特性而放弃这个功能非常齐全且可靠的框架

一般的问题主要是从人们如何看待它的角度来看。 对于某些人来说,只需几行代码,更改/添加任何内容看起来很简单,但实际上,它的范围更大,副作用也很多。 所有这些都必须考虑在内,而且一如既往,话题线很长,每件事都有一定的成本价格、附加值和优先级。

我想指出,我个人并不一定期望所有这些问题都能立即得到解决。

但我希望,现在,Ivy 已经离开并在野外一段时间了,可以发布某种官方更新,例如,前 25 名投票最多的问题。

我的意思是,我认为这种规模的项目不可能在项目管理中不考虑优先事项,并为所有前 25 个(或其他)最高投票的问题制定某种计划。 其中大部分甚至有一个effort标签。 只需发表一篇博文,让社区知道您了解这些,并且您已经为它们制定了某种计划。

我当然也不会放弃 Angular,尽管它有所有缺点,但我仍然比所有其他替代方案更喜欢它。 但我认为可以公平地说它缺乏“社区关系管理”(特别是在 github 上)。

// 编辑

你们好心人介意填写这个民意调查吗? 鉴于您当前的情况和考虑的所有因素,这些问题中哪些对您影响最大/最能改善您的开发人员体验https://forms.gle/cprhx239kuqwWd5M8

@fxck谢谢你写的。 我开始怀疑是否每个人都刚刚接受了这样一个事实,即 Angular 团队会在他们想要的时候做他们想做的事。 虽然这当然是他们的权利,但我也认为当人们不断忽视开发人员的声音时,有必要把他们叫出来。 我同意我认为没有人期望立即解决问题,但忽略它们 4-5 年或只是等到问题自动锁定对于任何项目来说似乎都非常不专业,更不用说谷歌运行的项目了。 他们通过在一堆未解决的问题中添加“由 Ivy 修复”标签来争取一些时间,然后什么也不做。

我当然也不会放弃 Angular,尽管它有所有缺点,但我仍然比所有其他替代方案更喜欢它。 但我认为可以公平地说它缺乏“社区关系管理”(特别是在 github 上)。

我认为这句话完美地概括了我的想法。

@chriszrc我并没有真正将此线程中的任何内容解释为“仇恨”。 更像是人们终于发泄了他们对 Angular 的挫败感,他们经常一次忽略问题多年。 它甚至不是专门针对这个问题,而是关于维持某种形式的社区互动的原则。 到目前为止,Angular 团队是否被认为比其他任何人都优越,以至于他们觉得他们可以忽略开发人员的请求,因为他们知道什么对每个人都是最好的?

@chriszrc我在这个线程中没有看到任何仇恨,只是一堆可以理解的沮丧。 我认为将这个请求和相关的失望等同于“节省几行代码”是夸大其词。 不仅仅是这个问题让社区感到沮丧, @fxck上面提出的大量流行问题也被忽视了。

大家好,请原谅我们在这个线程上保持沉默。 有一段时间我们收到了两个正交请求:

  1. 框架中更好的一流 RxJS 支持
  2. Angular 中的 RxJS 更少,可能使其成为可选的

这两个请求都在我们的关注范围内,并且各有优缺点。 例如,如果我们采用第一个方向,我们将使刚接触 RxJS 富有表现力、语义丰富的 API 的工程师更难访问该框架。 许多人担心在他们的 Angular 应用程序中到处使用 RxJS,这使得具有不同经验水平的团队成员难以阅读源代码。

同时,我们已经有很多反应式 API,但是 Angular 提供的一些原语是完全命令式的,而且事情并没有很好地联系起来。 也就是说,改进反应性支持是有意义的,但这应该成为 Angular 核心的一部分还是委托给我们正在密切合作的社区项目(例如 ngrx - Angular 的反应性扩展)?

我们有很多方法可以解决这个问题,但我们需要考虑:

  • 向后兼容性 - 不仅适用于源代码,还适用于培训资源
  • 学习曲线
  • 与当前 API 的一致性
  • 项目间的一致性
  • 性能 - 运行时和初始加载时间

我们正在对 GitHub 的一些主要问题进行优先排序,查看我们可以解决的最需要和最有影响力的改进。 我想说这是最具挑战性的一个,所以很难确定约会对象。

@mgeev感谢您的回复。 如果 rxjs 和反应式 api 将被拆分为另一个扩展(不是我个人的投票),那么不将它与特定的 state-store/redux 实现结合是否更有意义? 我很快从 ngrx 转移到 ngxs(感觉更少样板,而且装饰器更“有角度”),最后到了 Akita,这是我目前最喜欢的,也是我开发最多的。 或者,如果它确实与 ngrx 混为一谈,至少可以轻松添加我们需要的部分,因此我们不会被锁定使用 ngrx 或包含这些依赖项-

如果 rxjs 和反应式 api 将被拆分为另一个扩展(不是我个人的投票)

我并不是说这就是将要发生的事情。 在我的评论中,我只是分享了一个不完整的替代方案列表,以便我可以展示项目的范围和我们的不同考虑。

@mgeev确实,感谢您的回复。

然而,我确实想知道,如果 Angular get 的反应性部分以某种方式“降级”到社区项目中,这是否意味着反应性将成为 Angular 的二等公民?

@mgechev我觉得你的痛苦一下子被两个方向撕裂了。 我们的团队实际上并不反对拆分命令式/反应式部分。

以下是我们对这种拆分的要求:

  • 使用命令式和反应式需要感觉本土化,也不应该感到被束缚
  • 与特定的状态管理解决方案无关以启用反应式(完全披露我们现在已将所有 50 多个项目迁移到使用 akita)
  • 两个选项需要同时在一个版本中发布(无滞后/延迟)
  • 不可能有“最喜欢的”,Angular 所做的任何事情都需要在两个世界中都能工作,同时感觉自己是本地人
  • 由 angular 团队支持的对这两种方法的全面官方支持(根据我们的经验,这是客户硬停止客户要求)

基于以上:

  • 有两个主要选项:

    • 核心命令式 + 响应式插件

    • 核心反应式 + 插件命令式

  • 包装反应式使其看起来势在必行是非常直接的,反之则不然
  • 因此,我们会投票支持在核心中使用反应式并使插件包含命令式 API
  • 由于零延迟 + 无收藏要求以及所需的官方支持,我不知道该插件如何成为社区项目

我明白以上是我们对世界的看法,还有很多其他的。 我写这篇文章的原因是:

  • 如果您将反应式提取到社区项目中,我们将不得不将 50 多个解决方案迁移到另一个框架(这将再次由客户驱动,而不是我们的选择)
  • 所以我会要求让我们(社区)尽快知道你在哪里登陆,这样我们就有时间在另一个框架上重新培训我们的开发人员并迁移这些项目

请添加功能

@mgechev我不明白你的担忧。 这个提议是关于将这两个世界与一个统一和直接的新手开发者界面相结合。 这不是关于选择一个或另一个,而是启用组合。 例如,开发人员 x 决定构建一个很棒的反应式组件。 开发人员 y 决定在他不那么被动的项目中使用它。 为什么不? 为什么他需要依赖任何ngrx胶水?
这两个世界之间的区别在于,您是在最终智能组件中存储/管理状态还是在中央状态存储中存储/管理状态,这就是 ngrx 应该做的,但对于组件,我觉得框架本身应该是推动者。

有一段时间我们收到了两个正交请求:

  1. 框架中更好的一流 RxJS 支持
  2. Angular 中的 RxJS 更少,可能使其成为可选的

无论哪种方式我都可以,但这有时会给 Angular 一种脱节的感觉,这是除了包含电池的框架之外的最后一件事。

就我个人而言,我很想知道您如何为所有 Angular 包(HTTP、路由器、表单)提供非 RxJS 版本。 我怀疑这些没有好的非 RxJS 版本,这就是为什么他们首先使用 RxJS。

也就是说......除非我遗漏了一些东西,(2)是不可行的。


PS 我不会将这些请求描述为“正交”; 更像是“相反”。

Angular 中的 RxJS 更少,可能使其成为可选的

出于好奇@mgeev ,是否有任何 Github 问题? (我没见过。)或者这更像是 Google 员工的事情?

@pauldraper我在 github 上至少看到过一个问题,有人抱怨 rxjs 有多么复杂,并希望它完全是可选的。 我个人喜欢 rxjs,但我现在和两个团队一起工作,它给初级和高级开发人员带来了恐惧和厌恶。 此外,正交是比对立更好的术语,因为可以同时实现两个请求(假设每个解决方案都可以通过提供多个 API 选项来反应性和命令性地使用事物而不会减损另一个解决方案)。

对于在 github 上抱怨 rxjs 的每一条评论,可能有成千上万的开发人员在毫无问题地使用和享受它。

image

image

是我在上面发布的表格的结果(将近 200 人回复了),因为我在 Angular discord 上讨论它们。

不出所料,形式是最大的痛苦,但显然@brandonroberts@MikeRyanDev 最终可能会做饭。

对于在 github 上抱怨 rxjs 的每一条评论,可能有成千上万的开发人员在毫无问题地使用和享受它。

尽管我喜欢 rxjs,但我并不那么乐观。 我与许多开发人员一起工作,他们筋疲力尽,不得不花时间学习具有陡峭学习曲线的东西是一场噩梦。 很多人比编码更关心赚钱 :D 或者激进的初级开发人员认为他们在一天内学不到的东西一定是垃圾。

我与许多开发人员一起工作,他们筋疲力尽,不得不花时间学习具有陡峭学习曲线的东西是一场噩梦。 很多人更关心赚钱而不是编码

那些是 Angular 试图迎合的开发人员,他们筋疲力尽,不想学习? :) 无论如何,现在谁在乎呢。 Angular 已经存在一段时间了,它不会去任何地方,如果你添加可观察的生命周期、可观察的输入、可观察的事件,它绝对不会失去任何吸引力。 只需添加这三个小东西,人们就会高兴一段时间,就是这样(然后使用 https://indepth.dev/component-features-with-angular-ivy/ 完成基本包)。

angular_rxjs

这个就说明了一切。 恕我直言,Angular 应该保持一致并使用更多的RxJS:根本没有 RxJS 就不是 Angular。

那些是 Angular 想要迎合的开发者? :)

是的,只迎合想要使用 rxjs 编写代码的开发人员是自命不凡的。 尽管我更喜欢使用 rxjs,但我希望 Angular 能够满足这两种开发人员的需求。 为什么 Angular 应该只迎合我们而不是那些有不同想法的人? 尤其是当我相信我们是少数,而不是多数的时候。

再次,“完全反应式”,因为添加可观察的生命周期、输入、事件不会以任何方式影响你正在谈论的那些,但会让其他组非常高兴。

无论您是否喜欢响应式编程,组件功能都非常有用。

@fxck我从来没有不同意这一点。 我只是在回复你之前的评论,指出有很多人不想使用 rxjs,其中许多人已经打开了 github 问题来抱怨它。

尽管 angular 决定如何开发核心 - 无论是否响应,在理想的世界中,都会有相反的包构建在顶部以满足相反的需求(例如@angular/reactive@angular/imperative - 具有更好的名称希望)。

这两个进入社区对我来说对某些人来说是降级的,而对我来说,Angular 拥有成为一个伟大的反应性框架的前景是首先使用它的一个原因。


附带说明一下,我认为保持 angular 反应性和命令式扩展会更容易,因为这更容易桥接,而不是将命令式桥接到反应式 - 就核心和扩展包的简单性而言。

免责声明:我基于假设制定的最后一点 - 如果编写核心命令更容易,我不会感到惊讶 - 扩展反应会更复杂。

出于好奇@mgeev ,是否有任何 Github 问题? (我没见过。)或者这更像是 Google 员工的事情?

不是谷歌内部的事情。 你会惊讶(就像我加入团队时一样),外部和内部需求之间的交集有多大。 主要区别在于构建系统和依赖项管理,除此之外,Google 员工与非 Google 员工的要求几乎相同。

我们与数以千计的开发人员保持联系——许多外部公司、内部团队和个人贡献者。 在这两种情况下,有些人喜欢全面使用 RxJS,有些人认为这对他们来说不是正确的选择。 两个阵营都有很好的论据。

这不是我们想要仓促做出的决定,特别是考虑到有完善的变通方法/社区解决方案 在社区中完成的工作帮助我们收集额外的数据点并为当前变量集建立最佳解决方案。

我认为在开发 Angular 应用程序时采用一种统一的反应式或命令式方法(这是每个项目的选择)将非常有益。 混合和匹配的能力无助于开发体验和创建非 DRY 代码(例如,非反应性输入)。

没有什么是孤立发生的,所以我完全理解企业环境很难重新训练大量开发人员进行反应式编程。 因此,放弃命令式确实会将 angular 移出公司的最佳位置,这实际上是不会发生的,因为它构成了像 angular 这样的框架的庞大关键用户群。

也就是说,我认为在命令式接口之上构建反应式接口是不现实的。 另一个方向非常简单,因为您始终可以将 observable 的输出写入一个变量中,然后可以命令性地使用该变量。 事实上,这就是 angular 在许多地方已经在做的事情。

因此,我投票支持让 angular 在其核心完全反应,然后在顶部创建命令式“包装器”。

这种方法的主要问题很可能是命令式用户的迁移步骤更大。 这在企业环境中是一个 nono,这可能正是 angular 团队试图阻止的,因此核心建议 todo 势在必行。

@mgechev既然这个问题已经被提出过几次,那么听到你对反应式核心/命令式扩展的想法以及它是否有任何立足点会很有趣?

在这两种情况下,有些人喜欢全面使用 RxJS,有些人认为这对他们来说不是正确的选择。 两个阵营都有很好的论据。

太好了,很高兴知道。 定义全面使用 RxJS? 添加可观察的生命周期、可观察的输入和可观察的事件是如何适应的? 不,这些都没有真正有“完善的解决方法/社区解决方案”。

@fxck这些都没有真正有“完善的解决方法/社区解决方案”。

这是真的。 我编写了其中一个解决方案,它仅在一个依赖模板中糟糕的类型检查的大黑客中起作用。 我也检查了该线程中发布的其他一些解决方案,每个解决方案都至少存在以下问题之一:

  • 订阅泄漏
  • 对用户的样板要求
  • hacks 和我在图书馆中使用的一样糟糕,如果不是更糟的话。

就我而言,不可能很好地做到这一点。

可观察到的事件甚至更糟,根本没有那么多社区解决方案,它们通常需要额外的构建步骤(在某些情况下会破坏 monorepo 设置中的 SSR)和在此之上的 hack(如果您使用类扩展,例如获取生命周期可观察事件,因为嘿,angular 本身不支持) https://github.com/typebytes/ngx-template-streams/issues/8

生命周期事件可以说是“最简单的”,但仍然必须依赖类扩展或自定义装饰器,这些都不是理想的。

@fxck据我所知,如果不使用父类(eugh)或用户必须从他们自己的销毁生命周期事件中调用的方法(甚至更糟),就不可能清理生命周期事件订阅。

生命周期事件可以说是“最简单的”,但仍然必须依赖类扩展或自定义装饰器,这些都不是理想的。

我也曾尝试使用自定义装饰器实现生命周期事件(以使其具有反应性),这很痛苦。 〜没有课程扩展我不知道没有100 hacks是否可能〜。 到目前为止,用户扩展 angular 的可能性非常差,它依赖于黑客或大量样板来实现。

@tonivj5如果组件功能是公共 api,您是否认为这是在没有继承的情况下实现生命周期的潜在方式?

@ntziolis 哈哈有趣,你应该提到特性,我实际上正在研究 ngx-sub-form 的生命周期部分,特性正是我认为会有所帮助的东西。 同时,用ivy修补组件工厂是非常可行的。 我认为这在 ngx-sub-form 之外非常有用,所以我想我会将这个逻辑拆分成一个单独的库

@ntziolis在这一点上,我不确定我们是否会看到 Ivy 承诺真正兑现。

你检查过角效应吗? (不是 ngrx/effects) https://dev.to/stupidawesome/reactive-adventures-in-angular-introducing-angular-effects-1epf使用此工具,您可以编写完全响应式的应用程序。
包含父组件和子组件之间的双向可观察状态
https://dev.to/stupidawesome/exploring-the-angular-effects-api-2gol
9.1.0 版本将引入基于 Vue 3 的 Composition API 的组合/钩子模型。
我认为这是 angular 与反应性一起工作的真正下一步。

@mgeev

这不是我们想要仓促做出的决定,特别是考虑到有完善的变通方法/社区解决方案

只是好奇,我们是谁,有多少 Google 员工积极参与 Angular 决策过程?

你表示这不是一个应该仓促的决定,但我想知道你对不仓促的定义是什么? 这是其中一个在其上贴上“由常春藤固定”标签并忽略它直到更多开发人员开始再次制造噪音类型情况的人吗? 是否有针对任何反应性工作的目标版本,或者是否必须先发布其他较新的编译器版本?

在任何给定时间通常有 2500 个未决问题的事实是否曾出现在内部讨论中?

@ntziolis ,正如@zakhenry所说,我认为features会有所帮助

@ntziolis我已经对生命周期进行了其他尝试,并且我有一个有效的 POC (没有类扩展,也没有实现生命周期接口)。 它依赖于私有 API (:exclamation:)

@tonivj5 哈哈,我还没有机会阅读你的,但我的解决方案也https://github.com/cloudnc/ngx-observable-lifecycle

我正在研究的库的想法是拥有一个公共基础,其他库可以与之挂钩,以创建自己的生命周期感知功能。 它应该能够这样工作,如果有多个函数想要访问同一个钩子,那么只有一个钩子装饰在组件 def 上。

我已经完成了所有工作,只需要对 CI 和单元测试进行排序。

在任何给定时间通常有 2500 个未决问题的事实是否曾出现在内部讨论中?

哈哈,自 2019 年年中以来没有 2500 个未解决的问题。

$ github_issue_stats history -i2m -n20 -sangular/angular

+-------------------------+--------------------+
|         period          |       issues       |
+-------------------------+--------------------+
| This month (2020-05)    | 2855 (+137, -112)  |
| Last month (2020-03)    | 2830 (+495, -550)  |
| 2 months ago (2020-01)  | 2885 (+601, -575)  |
| 3 months ago (2019-11)  | 2859 (+437, -352)  |
| 4 months ago (2019-09)  | 2774 (+411, -305)  |
| 5 months ago (2019-07)  | 2668 (+441, -369)  |
| 6 months ago (2019-05)  | 2596 (+488, -349)  |
| 7 months ago (2019-03)  | 2457 (+425, -373)  |
| 8 months ago (2019-01)  | 2405 (+428, -330)  |
| 9 months ago (2018-11)  | 2307 (+425, -391)  |
| 10 months ago (2018-09) | 2273 (+515, -466)  |
| 11 months ago (2018-07) | 2224 (+641, -541)  |
| 12 months ago (2018-05) | 2124 (+690, -624)  |
| 13 months ago (2018-03) | 2058 (+605, -444)  |
| 14 months ago (2018-01) | 1897 (+773, -679)  |
| 15 months ago (2017-11) | 1803 (+815, -979)  |
| 16 months ago (2017-09) | 1967 (+671, -431)  |
| 17 months ago (2017-07) | 1727 (+664, -518)  |
| 18 months ago (2017-05) | 1581 (+854, -548)  |
| 19 months ago (2017-03) | 1275 (+987, -796)  |
| 20 months ago (2017-01) | 1084 (+671, -505)  |
+-------------------------+--------------------+

根据我的口味,我更喜欢开放源代码项目,在有明确的社区讨论/共识之前,让问题保持开放讨论,而不是关闭每个不经讨论就打开的问题的项目。 这也是维护者在关闭接受提案的任何可能性之前向社区解释他们的担忧的一步。

@agalazis这个问题已经开放了 4 1/2 年,在过去的 3 年里几乎被忽略了。

@agalazis这个问题已经开放了 4 1/2 年,在过去的 3 年里几乎被忽略了。

有一个 Angular 团队成员解释了为什么它在 5 天前仍然开放

@etay2000有些决定有时很艰难,因为它们会影响整个项目的发展,这是可以接受的。随着时间的流逝,开发人员可以意识到在不久的将来他们不能依赖该功能,但如果有可能,为什么要过早地杀死它? 我确实相信最简单的事情是拒绝提案并关闭问题,但它没有发生是有原因的。

@beeman

有一个 Angular 团队成员解释了为什么它在 5 天前仍然开放,所以你根本不对。

你真的很喜欢那些不喜欢的表情吗? 但这真的是一种解释吗? 4 年多之后,他说他们不想仓促做出决定。

@agalazis
我同意有些决定很艰难,但是推迟做出这些艰难决定的可接受的时间范围应该是多少? 并非所有 2855 个未决问题都涉及这些艰难的决定,但其中很多也已经存在了很长一段时间。

@etay2000接受与否,这是开放源代码的人,开放问题的人远不止维护者。 例如,Typescript 项目有将近两倍的待解决问题,我在那里做的另一个提案也花了很长时间。 我不介意,因为那里的讨论带来了许多替代方案,这些替代方案不像拥有该功能那样花哨,但设法看到了很多其他观点

@agalazis你是对的,我想我(错误地)对 Google 维护的开源项目的期望比我对较小项目的期望更高。

@beeman在此处插入大拇指向下的表情符号:-------->

@etay2000你应该永远记住,每一个如此重要的决定都会影响成千上万的事物、项目和人。 更重要的是,一个不完美的决定和引入公共 API 会在短时间内导致一些重大变化,这只是一场噩梦。 你几乎可以在每个 PR 上看到每个步骤是如何被审查的,如果不完美,它只是等待、数周、数月甚至数年。

@agalazis你是对的,我想我(错误地)对 Google 维护的开源项目的期望比我对较小项目的期望更高。

我不知道,看看围棋和飞镖。 谷歌实际上因为忽略了五年来非常受欢迎的社区请求而闻名:D 一般来说,他们对语言/框架/等的方法。 很保守。 你可以看到新的语言和想法不断涌现,它们对领域产生了革命性的影响,而谷歌多年来仍然没有采用它们,例如空安全在 Dart 之前很长一段时间就受到了 Kotlin、TypeScript 和 Scala 的影响。 我想这种方法有利有弊,但至少对我来说,我倾向于尽可能避免谷歌开发的东西,因为它并不真正符合我的精神。

(对不起,大家,但我必须这样做。)

@beeman在此处插入大拇指向下的表情符号:-------->

@etay2000我不知道这种评论有什么帮助,如果不是在这里违反行为的话。 我很确定每个人都知道如何竖起大拇指,那么讽刺有什么意义呢?

提醒一下,没有人被迫使用 Angular,你可以尝试找到另一个让人们更喜欢你的框架。

@brunojcm我猜派对已经结束了,评论警察来了。 关键是他拒绝了我所有的其他评论,但我没有意识到讽刺是一种违反行为的行为。

提醒一下,没有人被迫使用 Angular,你可以尝试找到另一个让人们更喜欢你的框架。

我真正想回应的方式肯定会违反这里的行为准则。

我完成了,每个人都可以回到他们定期安排的节目中。 我会再过 4 到 5 年再回来看看结果如何。

@tonivj5 @zakhenry高兴听到这可能是一条大道。

@mgechev我知道这甚至不是初步讨论,但很想听听您对以下方面的想法:

  • 反应式核心 + 非反应式扩展与相反的方式。
  • 对于这个问题,更具体的是有任何现实的机会以任何方式公开功能 api。

    • 即使只是需要注意将来会发生重大变化,只要通用 func 仍然可以访问。

    • 正如@zakhenry为生命周期事件所做的那样,我看到只有一个或非常有限数量的项目在顶部构建 API,其他人可以使用这些 API 而不是直接访问这些 API

    • 然后,您可以与这些包装器团队合作,让他们尽早了解即将发生的重大更改(非常类似于您对反应式扩展的建议)

我想你们可以直接应用Vue 3.0的reactive module(ref或reactive)来解决这个问题。

@AnonymousArthur如果你在谈论包@vue/reactivity那么它与我们在这里讨论的完全无关,因为它提供了对属性的反应性并且与反应性流无关(这就是 RxJs)。

@gund是的,我同意。 然而,这个对话需要相当长的时间(4.5 年)并且还没有完成,所以我很快就弹出了一个想法(可能不是),不想写订阅/取消订阅或 ngOnChange 处理程序。 RxJS 确实提供了反应性功能,这个对话(没有阅读所有内容)正在讨论用 Observable 包装 Input 值作为语法糖以使其可观看,我认为这是一个很棒的想法。

我认为这个想法真的很有用, @robwormald一开始的原型看起来很棒,但我认为它低估了潜在的好处以及影响需要多么小。

让我提议对该解决方案稍作调整以修复一些缺点,但方法基本相同:

Angular 会定义一个装饰器@OInput()和一个类型EventReceiver<T> ,它扩展了Observable<T>并且还添加了一个.value getter(比如BehaviorSubject<T> )。

父组件方面,如@robwormald的示例一样,没有任何变化。 如果你想在那里传递一个 Observable 值,你仍然需要| async 。 或者你可以传递一个不可观察的类型,这也很好(一如既往)。

子组件方面,这是我建议的样子(与该建议略有偏差):

@Component({ selector: "child" })
class Child {
  // Notice, this parallels the patterns of `@Output()`.
  @OInput() foo = new EventReceiver<MyType>();

  constructor() {
    // The reactive way to listen to input changes in the class.
    this.foo.subscribe((v) => {
      console.log('foo changed', v);
    });
    // Or you can bind to `foo | async` in the template.
  }

  // But this would also support less reactive patterns in parallel,
  // both for ease of migration and for cases where developers don't
  // want to migrate fully. 
  ngOnChanges(changes: SimpleChanges) {
    if (changes['foo']) {
      console.log('foo changed', changes['foo'].currentValue); // Unchanged
      console.log('foo changed', this.foo.value);  // Previously this would have been just `this.foo`
    }
  }
}

现在这里是这种方法的好处,我觉得有点被低估了:

  • 如果需要,这可以在父组件和子组件中实现有效的完全反应式设计。
  • 在父组件中,维护 Angular 现有的输入设计模式,以便没有组件被迫进入或必须围绕它进行设计。
  • 父和子上下文是完全独立的,父不需要知道子是使用Input还是OInput ,消除了此更改导致跨组件错误的风险。
  • 如果需要,部分迁移单个子组件相对容易,因为 Angular 像以前一样将所有这些都连接到现有的生命周期事件。
  • 由于 Angular 实现了这个特性,所有EventReceiver都可以在内部通过takeUntil(ngOnDestroyEvent)包含一个管道,所以大多数情况下不需要记住在他们的组件被销毁时取消订阅,因为这些 observables 会自动完成. (是的,减少了内存泄漏!)
  • 在这个子组件中,它的外观和功能与今天的@Output()模式非常相似,因此提供了一些很好的并行性和很好地连接它们的潜在能力。

    • 旁注:作为后续行动,我认为提出@InputOutput()会很酷,它使用ReplaySubject<T>(1)来更无缝和一致地连接双向绑定的行为。 但这有其自身的挑战和论据,所以我不是在这里提出这个建议。

  • 没有新类型的样板来完成这项工作,这就像@Output()一样简单。
  • Angular 会无缝地将switch()更改为由父级输入的Observable更改,因此较弱的反应模式(例如不坚持使用相同的 Observable 实例的较弱的响应模式不需要特殊情况( switch() ed)在子组件中。

关于我选择的名称OInput旁注:显然这是有争议的。 然而,我个人喜欢这个名字,因为它是“Observable Input”的缩写,并且因为它看起来有点像“输出”,这就是它的反映。

亲爱的 Angular 团队。 请给我们一些在 2020 年值得期待的东西 :-)

另外@jcomputer解决方案正是我想要的。 不太喜欢不同的装饰器名称,但也许添加一个类似于@ViewChild具有{ read }的参数是可行的。 例如:

@Input({ observable: true }) 
@Input({ asObservable: true }) 
@Input({ asSubject: true })

在 2020 年有一些值得期待的事情就这么多了。:D 当然,除非这(以及上面列出的其他问题)被视为错误而不是新功能。 :)

https://twitter.com/ThomasBurleson/status/1283902827467886592
image

@fxck我在这里可能有一个不受欢迎的意见,但老实说,如果 2020 年是 Angular 团队决定消除表单、路由器等上的许多错误的一年,我根本不会因为0 个功能而生气:耸肩:。

image

也就是说,也许这更像是与最近发生的事件有关的问题,我认为 Angular 团队目前正在发生一些复杂的事情。 我希望他们能够留住迄今为止一直在积极构建 Angular 的优秀人才: heart:。 但那是另一个故事了。

抱歉跑题了。 飞走了

@maxime1992对我来说也很好。

我在这里可能有一个不受欢迎的意见,但老实说,如果 2020 年是 Angular 团队决定消除表单、路由器等上的许多错误的一年,我根本不会因为0 个功能而生气

但让我感到紧张的是,Angular 团队内部存在长期不健康的人力资源流程,在没有我们的情况下影响着我们。 尤其是从对这一领域进行大量投资的角度来看。

从我的 POV 来看,他们主要通过 BOT 关闭问题来解决问题 - 没有响应就离开并由 bot 关闭。,.. :-/

@montella1507不,这不完全正确。 机器人只锁定已经关闭的问题。 并且 Angular 团队成员在许多情况下已经付出了很多努力,当他们试图解释在输入新问题时需要描述和重现的内容时,这已经是事实。 这是一个非常耗时的过程,需要很大的耐心。

对于那些指责 Angular 团队并抱怨没有快速获得功能和更改的人:
我明白你的痛苦。 我也很想在 Angular 中看到这个功能,我也会改变很多我个人不喜欢的东西。

但也许 Angular 并不意味着成为一个拥有所有闪亮的新玩具并在每个版本中获得大量功能的框架。 这对我来说很好。 必须有针对更多企业级并提供稳定性和可靠性的框架。 Angular 做得很好,至少从我的经验来看是这样。

我更喜欢Vue。 我将它用于我的个人项目。 但是我的项目很快就会开始变得过时,我有升级到新组件风格的冲动。 此外,当我进行升级时,事情总是以最奇怪的方式破裂。 生态系统似乎也很难保持兼容性。

相比之下,Angular 成熟而稳定。 它不会经常更改,但这也意味着随着依赖项的更新,您在重构代码方面的工作会减少,这对商业产品很有价值。 你的知识也能保持更长时间的相关性,而不是试图跟上,你可以深入。

不过,@manfreed 的人并不是要一个闪亮的新玩具,他们是要对框架设计中的疏忽进行更正。 目前,我们有时被迫使用 observables,有时无法使用 observables 来实现关键功能。 那些讨厌 observables 的人感到沮丧。 那些喜欢 observables 的人会感到沮丧。 我们中的许多人对我们放弃并转向其他框架的情况感到非常失望。 我很想继续使用 Angular,有点方向,它可能是一个真正伟大的框架。 但是关于管理不善、缺乏实用主义、疏忽的博客文章,客观上都令人失望。

@insidewhy要有耐心,路上总会有一些高峰和低谷,但这并不意味着其他人也没有,或者有一些主要的错误。 这仅意味着事情可以比实际情况更快、更有效地推进。

路线图中没有提到任何特定的上述问题,这让我感到难过,我真的希望会有一些东西,提到,承认......这个讨论是在 Angular discord 之后进行的——

image

我给了他们一个怀疑的好处,并在那个中等路线图文章的评论中询问,Minko 的回答:

image

他几乎重申了他几个月前在这里所说的话,这些与 rxjs 相关的问题都不会很快得到解决,所以是的。

我已经完成了我的观点,但让我最后一次尝试..我不是要求将 Angular 变成Cycle.js ,取代当前的 API,我所要求的只是三个小反应性樱桃在当前 API 之上。

  • 可观察的生命周期
  • 可观察的输入
  • 可观察的模板事件

这些都没有破坏向后兼容性,增加学习曲线,与其他 API 不一致,我可以想象几乎不会影响性能,所以与其他任务相比,它们为何如此棘手,需要更长时间和仔细的计划。 一次加一个,表明你在乎。 请。

@fxck :+1:当然。

  • 可观察的生命周期
  • 可观察的输入
  • 可观察的模板事件

@insidewhy要有耐心

当比赛中有很多竞争对手都在争夺注意力时,要求人们超过 5 年的耐心并不是那么务实。

@ insidewhy它更普遍的意思。

@fxck谷歌(根据我的最后一次统计)有 4000 个内置角度且性质高度相似的应用程序,在这些项目和同行评审之间切换(我想)非常无缝。 即使在我的公司,Angular 的巨大好处之一是,我什至无需审查另一个团队的代码,我就知道它处于同行审查不会引发整个调查的状态。

现在假设我们将这些有用的 RxJS 快捷方式中的每一个都实现到核心中,然后会发生什么? 我们可能最终会得到程序性偏见与反应性偏见,而没有明确指导哪种偏见对于任何特定情况是正确的。 在阅读了迄今为止的讨论之后,角度团队的答案不是“这是一个低优先级”(正如您的流程所暗示的那样),它实际上似乎更接近于“我们不想选边站”。

这里的 Angular 社区中的高级 RxJS 用户是少数,因为在您熟悉了 Typescript、DI、模板和 Angular 的各种库之后,您每天使用该框架已有将近 1 年的时间,现在您已经准备好了完全理解 RxJS。 让高层人员在同行评审中因反应性偏见而离开会更加疏远人们,而这实际上是 Angular 最不需要的。

Angular 的目标(如我所见)是减少和调低“使用 X 技术,因为它更好,这是一个简短的中篇小说为什么”的对话,并在同行评审中保持关于正在实施的业务逻辑的对话。 我们真的想在“高级”角度和“初学者”角度之间对我们的组件进行分层吗?

这可以说是我们在状态管理中看到的相同论点,为什么 Angular 应该从许多可能的模式中确定哪一个是正确的?

让我为您引用一位有角的团队成员。

image

顺便说一句,这些(可观察的生命周期、可观察的输入、可观察的模板事件)绝对没有任何先进之处。

这可以说是我们在状态管理中看到的相同论点,为什么 Angular 应该从许多可能的模式中确定哪一个是正确的?

状态管理与此之间的区别在于,状态管理可以由社区来完成,angular core 内部没有任何东西可以阻止它。 不幸的是,Angular 没有提供正确实现我提到的这三个的方法。 (编辑:更正了评论链接)

我全心全意地为社区留下尽可能多的东西,包括表单、路由器、flex-layout、universal 等包,让 Angular 团队专注于核心、cli 和组件。 但为此,他们需要为社区提供足够的入口点,例如允许更高阶的组件。 但是他们多年来都没有这样做(通常说首先需要常春藤)并且现在拒绝在他们的计划中承认/谈论这一点。

在阅读了迄今为止的讨论之后,角度团队的答案不是“这是一个低优先级”(正如您的流程所暗示的那样),它实际上似乎更接近于“我们不想选边站”。

是的,这很可怕。

@fxck我非常认为我们对此意见一致。 我会说,每次我在论坛上甚至和我的工程师同事提到 RxJS 时,我能引用的大部分引语都是“我仍然不明白这一点,我如何学习更多?” 或者“我发现运营商的数量太多了。” 或“我在此 PR 中阅读此管道时遇到问题”。 每 1 个精通 RxJS 的工程师,似乎就有 15 个精通 Angular 的工程师皱着眉头。

是的,这很可怕。

这有点夸张,我们“不需要” @angular/core来拥有这些模式,事实上,这个问题中有许多变体和库,现在可以在任何项目中使用。 使用前面提到的选项的唯一保留是,您可能在将来必须迁移到 Angular 提供的作为标准@angular/core替代品的东西。 我们真的很难npm uninstall并修复所有打字稿错误吗?

影响所有 Angular 项目的重大架构决策,如果它们不起作用,则需要无数小时来记录和迁移,对我来说这似乎更可怕,我们以前曾经做过:

这有点夸张,我们不需要“@angular/core”来拥有这些模式,事实上,这个问题中有许多变体和库,现在可以在任何项目中使用。

不,不是真的,每个解决方案都需要黑客或依赖可能改变的内部 API。 这就是重点.. 阅读@insidewhy所说的https://github.com/angular/angular/issues/5689#issuecomment -630661006 以及我在下面所说的关于模板事件流的内容。

// 编辑更正的链接以进行评论

不,不是真的,每个解决方案都需要黑客或依赖可能改变的内部 API。

您认为哪些内部 API 或类型应该公开/通用以允许社区解决这个问题? 如果这个设计因为没有明确的前进路径而处于停滞状态,那么要求我们被允许设计我们自己的而不是不断敲击同一个鼓似乎是合适的。

阅读@insidewhy他试图在核心内部完成的另一条评论。

image

@fxck我不打算讨论在核心中实现这一点的尝试历史,或者这个问题是如何开放 5 年的,现在是什么阻止我们开放内部结构以允许社区自己开发设计?

@insidewhy ,我想知道这是否是您的库所依赖的类型检查黑客。 显然它不再使用 TS 4.0

属性“serviceStackId$”在初始化之前使用。ts(2729)

image

@insidewhy ,我想知道这是否是您的库所依赖的类型检查黑客。 显然它不再使用 TS 4.0

不,它在模板编译器中。 这是另一回事。 该死。 呃,好吧。 如果您想尝试修复它,我可以将您添加到维护者列表中。

@fxck实际上,您应该在 ngOnInit 中分配该管道运算符,因为在运行构造函数之后才会注入基于装饰器的属性,因此这实际上是您的代码的合法问题。

@insidewhy我肯定在 TS4 之前工作过。 并检查这个

function ObservableInput() {
  return (target: any, propertyKey: any) => {
    Object.defineProperty(target, propertyKey, {
      set(value: any) {
        console.log(target, propertyKey, value);
      },
      get() {
        return 'ObservableInput modified value';
      },
    })
  }
}

class Foo {
    @ObservableInput()
    bar = 'original bar value';

    baz = this.bar;

    constructor() {
        console.log('loggging this.baz', this.baz);
    }
}

new Foo();

它记录

记录 this.baz ObservableInput 修改值

所以我认为它应该仍然有效,只有在 TS4 中编译器才会抱怨,因为它没有意识到在该装饰器中有一个get的值。

我正在使用装饰器来感知 Input 道具的变化并制作 Input 道具的可观察版本。 请参阅此代码和框演示

这是它的工作原理:

// subjectize.ts
export function Subjectize(keyToWatch: string): PropertyDecorator {
  return (proto: any, propKey: string) => {
    const internalKey = Symbol(keyToWatch);
    Object.defineProperties(proto, {
      [keyToWatch]: {
        get() {
          return this[internalKey];
        },
        set(value) {
          this[internalKey] = value;
          this[propKey].next(value);
        }
      }
    });
  };
}
// counter.component.ts
import { Component, Input } from "@angular/core";
import { ReplaySubject } from "rxjs";
import { Subjectize } from "./subjectize";

@Component({
  selector: "app-counter",
  templateUrl: "./counter.component.html",
  styleUrls: []
})
export class CounterComponent {
  @Input()
  count: number;

  @Subjectize("count")
  count$ = new ReplaySubject(1);
}

正如@wmaurer指出的那样, @Subjectize可以被视为完成工作的“糖”。

值得阅读 angular 的官方指南Intercept input property changes with a setter ,它解释了我们可以使用getter/setter感知输入的变化。

@hankchiutw该解决方案看起来类似于我的@BindObservable装饰器: https :

谢谢@hankchiutw

由于您使用 1 初始化 ReplaySubject,因此我选择使用 BehaviorSubject。
我也直接在 Subjectize 中初始化了这个值。

export function Subjectize<T>(keyToWatch: string): PropertyDecorator {
  return (target: Object, propKey: string) => {
    const internalKey = Symbol(keyToWatch);
    Object.defineProperties(target, {
      [keyToWatch]: {
        get(): T {
          return this[internalKey];
        },
        set(value: T) {
          this[internalKey] = value;

          if (this[propKey]) {
            this[propKey].next(value);
          } else {
            this[propKey] = new BehaviorSubject(value);
          }
        }
      }
    });
  };
}

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'rol-bla',
    templateUrl: 'bla.html',
    styleUrls: [ 'bla.scss' ]
})
export class BlaComponent implements OnInit {
    @Input() world: World;
    @Subjectize<World>('world') world$: BehaviorSubject<World>;

    // ...
}

我希望我可以避免在“Subjectized”属性上写类型,因为它应该能够从相关属性中猜出它:(知道怎么做吗?

梦想是能够做到这一点并自动创建相关的主题道具(在我的情况下为 world$)作为 BehaviorSubject并正确初始化。

export class BlaComponent implements OnInit {
    @Input() @Subjectize() world: World;

    // ...
}

@mparpaillon您提出的设计几乎是在https://github.com/PSanetra/bind-observable 中完成的
唯一的区别是@BindObservable()装饰器在底层使用了 ReplaySubject。 因此,如果您希望该主题包含初始值,您还需要显式初始化绑定属性(如果它只是未定义,因为属性类型可能不允许未定义或空值)。

Stackblitz 示例

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponentComponent {
  @Input()
  @BindObservable()
  counter: number;
  counter$: ReplaySubject<number>;
}

谢谢@PSanetra,但我更喜欢我的解决方案(实际上是汉克解决方案)

@mparpaillon我在另一个演示中为你的梦想尝试了一些东西(ha)。

用法看起来像

export class CounterComponent {
  @Input()
  @Subjectize()
  count: number;

  @Input()
  @Subjectize("myCount$")
  anotherCount: number;
}

您可以在其中选择指定主题化的道具名称。

虽然它在技术上是可行的,但我担心这对开发人员来说可能是模棱两可的(因为在后台创建了一个新的类成员)。

谢谢@hankchiutw ! 不过,Typescript 似乎不允许我使用 count$ 或 myCount$。

Capture d’écran 2020-11-12 à 10 11 50

你也可能是对的歧义......再次感谢

不过,Typescript 似乎不允许我使用 count$ 或 myCount$。

您将班级成员声明为“count”和“anotherCount”,这就是您无法访问“myCount$”和“count$”的原因。 它们根本不存在,因为您没有在任何地方声明它们。

我知道……这就是重点。 我正在寻找一种从装饰器中声明它们的方法。 @hankchiutw提供了一个解决方案,我只是说它不能正常工作

@mparpaillon看看我的解决方案: https :

@Futhark哦,太热了! 谢谢

此页面是否有帮助?
0 / 5 - 0 等级