Angular: Propuesta: entrada como observable

Creado en 8 dic. 2015  ·  183Comentarios  ·  Fuente: angular/angular

Lo siento, no soy bueno en el inglés.

@Input valores de propiedad
Y si la propiedad de entrada se cambió en el componente secundario (tiene la propiedad como propiedad propia), su detector de cambios nunca lo notará.

Meta

  • Los datos de entrada de los padres y la propiedad de entrada del hijo deben sincronizarse.
  • Los desarrolladores deben comprender que las propiedades de entrada se cambian de forma asincrónica.

    Propuesta

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

El código anterior no funciona. Actualmente, para recibir la entrada como Observable<T> , debo escribirlo como se muestra a continuación.

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

Ejemplo: http://plnkr.co/edit/BWziQygApOezTENdTVp1?p=preview

Esto funciona bien, pero no es esencial. Los datos de entrada de los padres son originalmente datos simples.

Creo que esta propuesta nos hace felices.

Gracias.

core inputs / outputs feature Needs Design

Comentario más útil

Estimado equipo de Angular. Danos algo que esperar en 2020 :-)

Además, la solución @jcomputer es exactamente lo que querría. No soy un gran admirador de un nombre de decorador diferente, pero tal vez sea factible agregar un parámetro similar a cómo @ViewChild tiene { read } . p.ej:

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

Todos 183 comentarios

Hola @ laco0416 , tu inglés está bien, ¡no te preocupes!

Me gusta mucho esta idea y es algo que hemos discutido antes. También coincide muy bien con https://github.com/angular/angular/issues/4062 (eventos de vista de observación) y https://github.com/angular/angular/issues/5467 (eventos de niños observables de los padres)

Es importante recordar que no todos querrán usar Observables (¡estas personas se están perdiendo!), Por lo que debemos proporcionar opciones para ambos casos de uso, por lo que es poco probable que hagamos @Input() directamente en un Observable. Creo que tener algo como @ObserveInput() podría funcionar, y tendremos una discusión _ después_ de que enviemos la versión beta sobre algunas de estas características más interesantes, creo.

Mientras tanto, aquí hay una implementación básica (y _mu_ experimental !!! NO hagas esto de verdad) de esta idea. ¿Es esto conceptualmente lo que estabas pensando? http://plnkr.co/edit/Nvyd9IPBZp9OE2widOcW?p=preview

Creo que es una muy mala idea cambiar las propiedades de entrada en un componente secundario. Las propiedades de entrada deben ser de "solo lectura". Sus datos siempre deben fluir de padres a hijos (y nunca en la dirección contraria).

@alexpods, creo que la idea aquí es exactamente eso: es _escuchar_ el cambio en las propiedades de entrada _ como_ un Observable, que no emite valores en sentido ascendente, lo cual está absolutamente bien en lo que a mí respecta.

@robwormald

tu inglés está bien, ¡no te preocupes!

¡Gracias! Estoy tan aliviado.

¡Tu @ObserveInput es lo que solo quiero!
Además, @Input no tiene cambios importantes. Creo que es una muy buena solución.

@alexpods Yo también en absoluto.

está escuchando el cambio en las propiedades de entrada como un Observable, sin emitir valores en sentido ascendente, lo cual está absolutamente bien en lo que a mí respecta.

Pienso de la misma manera que Rob.

@ laco0416 Ooh, perdón por el malentendido. La frase "si se cambió la propiedad de entrada en el componente secundario" me confundió.

No sé si debería comentar aquí o si debería abrir una nueva edición. Avíseme si agrego una solicitud en el lugar equivocado.

He estado intentando (pero, hasta ahora, fallando) escribir un decorador de este tipo, y luego me topé con el plunkr de @robwormald , que funciona _casi_ perfectamente (pero no del todo).

Lo que me entusiasmó con este enfoque fue el hecho de que se está aprovechando del gancho del ciclo ngOnChanges vida
Lo que me gustaría ver es alguna forma de que _todos_ los ganchos del ciclo de vida se expongan como Observables, es decir, como onChanges$: Observable<{[key: string]: SimpleChange}> , onInit$: Observable<{}> , etc.

Tener todos los ganchos del ciclo de vida disponibles como Observables me ayudará a Rx todas las cosas ;-)

¿Alguna actualización sobre esto?

AFAIK, No.
@robwormald ¿tienes alguna noticia?

Sé que esto es antiguo, ¡pero sería genial! @robwormald ¿ Alguna palabra sobre esto?

Me encantaría proporcionar valores cambiantes de propiedad @Input como Observable, tal como lo hace el decorador @ObserveInput @robwormald. No siempre es factible que los componentes principales pasen Observables, especialmente cuando está migrando una aplicación existente (Angular 1). Sin embargo, no poder "contener" Observables dentro de un solo componente hace que aprovechar el poder y la elegancia de RxJS sea mucho más difícil.

Desafortunadamente, Rob no bromeaba cuando dijo que no usara esta versión de @ObserveInput . El problema con el que me encuentro es que los Observables se crean en un "nivel de clase" (si esa terminología tiene sentido) y, por lo tanto, se comparten en todas las instancias del componente. Esto no es bueno, obviamente. La creación de los Observables en el momento de la creación de instancias tampoco funcionó para mí. Parece que Angular no conecta correctamente la detección de cambios en ese caso.

¿Alguien ha logrado una mejor implementación de @ObserveInput o hay alguna noticia sobre el soporte oficial?

@lephyrus Si bien un @ObserveInput sería muy agradable, no es estrictamente necesario para obtener un Observable de cambiar los valores de propiedad @Input . El decorador sería simplemente muy elegante "azúcar".

Ya existe un evento de ciclo de vida ngOnChanges . Dentro de ngOnChanges podemos usar un rxjs Subject , para crear un observable de cambios, es decir:

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

O, si desea algo más reutilizable, puede crear una clase base que su componente 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);
    }
}

... que podría usarse de la siguiente manera:

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

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

Estoy usando este enfoque hasta que esté disponible @ObserveInput decorador oficial

Gracias, @wmaurer. Su ReactiveComponent es muy bienvenido, y usa un código impecable y una escritura segura para arrancar, ¡realmente agradable! Es importante destacar que también se comporta bien bajo prueba y aún permite usar la estrategia de detección de cambios OnPush . Ahora estoy feliz de esperar el "azúcar oficial". (También estoy seguro de que aprenderé algo cuando descubra por qué tuviste que usar la lógica Observable.create() ; todavía no he tenido tiempo para investigarlo). Nuevamente: merci gäll! :guiño:

@lephyrus de nada , gärn gescheh ;-)

Usé Observable.create() para conseguir un Observer para poder hacer un next() . Podría haber usado un Subject que es tanto un Observable como un Observer , pero creo que, en general, es una mala práctica 'exponer' un Subject ( Observer ).

@ laco0416 cierra a favor de https://github.com/angular/angular/issues/13248 ?

@DzmitryShylovich No. La característica propuesta en este número es el paso de datos de solo lectura y controlado por eventos.

@ObserveInput será genial! : +1:

Gracias @wmaurer por un buen ejemplo. Aunque tengo una pregunta. Me gustaría poder usar un objeto en lugar de una cadena como observable.

P.ej

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

Sin embargo, no se llama a this.updateChart y ngOnChanges. ¿Cómo puede expandir su muestra de una simple cadena para observar un objeto en su lugar?

@ChrisWorks también debería funcionar con objetos:

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

Hago esto muy a menudo, así que si esto no funciona, supongo que hay un problema con la entrada a su componente en alguna parte.

Hola @wmaurer , gracias por la respuesta. ¿Podría ampliar su muestra con una versión funcional en la que utilice un objeto "config"? Simplemente no puedo hacer que el mío funcione. ¿Un repositorio de Git de muestra? :)

@ChrisWorks realmente debería funcionar como está. observePropertyCurrentValue no diferencia entre una entrada de cadena y un objeto.
Aquí hay un proyecto ng2 beta 0 realmente antiguo que hice donde las entradas son de todos los tipos diferentes, no solo cadenas:
https://github.com/wmaurer/todomvc-ng2-reactive
por ejemplo, https://github.com/wmaurer/todomvc-ng2-reactive/blob/master/src/app/todo-item/todo-item.component.ts

+1 ¡Un caso de uso tan fundamental, no puedo creer que aún no se haya resuelto!

Finalmente obtuve la mejor solución y funciona sin repetición y con compilación AOT :) El secreto es combinar @Input() con un segundo decorador.

El decorador:

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

Uso:

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

EDITAR: Ahora he hecho de esto una biblioteca. Feliz de recibir ideas para mejorarlo. Agregar un nuevo método que complementa un @Input con un Observable siguiente. Ver https://github.com/ohjames/observable-input

Si tuviera que hacer algo así, preferiría optar por un solo sujeto análogo a ngOnChanges: Subject<SimpleChanges>
Aún puede filtrar y asignar a una sola entrada específica si lo desea.

1 El sujeto por entrada parece mucho sin hablar de eliminar un accesorio de clase para crear getter & setter y que el tipo de su entrada es incorrecto y hacer input = value de hecho emitirá el valor en el observable y usted no completando tu asignatura.

Puede encontrar un ejemplo de implementación de mi idea aquí , el enfoque del decorador es experimental, pero creo que debería ser bastante seguro usar el enfoque de herencia (tenga en cuenta que lo hice ahora para mostrar aquí, no se usa).

@ghetolay , no importa si el asunto está completo, cuando la vista se cierra, no hay más suscripciones a la transmisión y el GC puede eliminarla. Emitir el valor de lo observable es básicamente el objetivo de esto. El compilador angular actualmente no verifica los tipos de propiedad de entrada, con suerte, para cuando lo haga, habrá algo más oficial disponible;)

changes$ filtra la interfaz BehaviorSubject al componente, idealmente debería escribirse como Observable<...> , aparte de eso, parece razonable, aunque un poco más detallado.

En cuanto al tema de muchos temas ... ya sea que esté utilizando n sujetos o 1 , todavía tiene n suscripciones. Cuando tiene 1 sujeto, termina agregando un operador de mapa y un operador de filtro a cada suscripción, la sobrecarga de rendimiento de esto no sería mucho menor que simplemente usar n sujetos, incluso podría ser peor.

Ignorando la escritura de los parámetros de entrada en sí, la versión basada en decoradores proporciona una API más segura para los consumidores de los observables que los tipos SimpleChange basados ​​en any .

@ohjames

cuando la vista está cerrada, no hay más suscripciones a la transmisión y el GC puede eliminarla

Supone que todas las suscripciones estarán vinculadas a la vista y obtendrán el mismo ciclo de vida que la vista. No estoy haciendo ninguna suposición sobre cómo se consumirá el Observable.

Emitir el valor de lo observable es básicamente el objetivo de esto. El compilador angular actualmente no verifica los tipos de propiedad de entrada, con suerte, para cuando lo haga, habrá algo más oficial disponible;)

Me refería al usuario, el usuario aún puede acceder y configurar la propiedad. Por lo tanto, establecerá la propiedad como si fuera un número, excepto que se escribe como Observable y esto nunca establecerá la propiedad, sino que la hará emitir. Esto me confunde mucho.

En cuanto al tema de muchos temas ... ya sea que esté usando n temas o 1, todavía tiene n suscripciones. Cuando tiene 1 tema, termina agregando un operador de mapa y un operador de filtro a cada suscripción, la sobrecarga de rendimiento de esto no sería mucho menor que simplemente usar n temas, incluso podría ser peor.

No entraremos en este tipo de debate aquí porque pronto perderemos el enfoque sobre el propósito principal.

Actualicé para no exponer el Subject y construí algo para agregar escritura a los cambios.
No estoy usando un BehaviorSubject sino un Subject simple porque no veo los Observable como portadores de valor sino como cambios con el tiempo.
Acabo de encontrar un defecto: cuando utilizo la tubería asíncrona, el primer valor no se emite porque la tubería asíncrona se suscribirá después de la primera emisión. Actualizaré mi código más tarde para manejar correctamente ese caso.

Aquí está el enlace nuevamente: https://stackblitz.com/edit/angular-observableinput?file=observablechanges%2Fimpl.ts

Supone que todas las suscripciones estarán vinculadas a la vista y obtendrán el mismo ciclo de vida que la vista. No estoy haciendo ninguna suposición sobre cómo se consumirá el Observable.

De acuerdo, veo su punto, completar los observables obligará a cerrar todas las suscripciones, pero no es algo que realmente me preocupe. En nuestra aplicación, el 99% de las suscripciones se realizan a través de la tubería asíncrona y en las pocas que no tienen que cerrarse manualmente de todos modos, ya que a menudo obtienen observables externos de Redux / etc a través de combineLatest / switchMap.

No entraremos en este tipo de debate aquí porque pronto perderemos el enfoque sobre el propósito principal.

Bueno, planteaste una crítica inválida, por lo que era necesario contrarrestarla.

~ Además, el uso de su clase a través de la herencia no funcionará, el código generado por el compilador AOT no llama a los métodos del ciclo de vida en las clases principales, consulte https://github.com/angular/angular/issues/12756#issuecomment -260804139, Tendremos que agregar ngOnDestroy() { super.ngOnDestroy() } y otro por ngOnChanges a todos los consumidores de la clase. ~ <- parece haberse solucionado a partir de Angular 4.4 con ciertas limitaciones.

He revisado mi preocupación sobre el uso de la tubería asíncrona y, dado que todavía tiene acceso a la entrada, no creo que el uso tenga ningún sentido. Si solo desea vincular el valor de entrada en la plantilla, puede vincular directamente a la entrada sin necesidad de usar ningún observable y asíncrono aquí. Y si desea componer un nuevo observable a partir de él, entonces debería poder obtener exactamente lo que desea fácilmente (probablemente usando startWith alguna parte).

Entonces creo que mi declaración anterior está bien:

No estoy usando un BehaviorSubject sino un Subject simple porque no veo los Observable como portadores de valor sino como cambios con el tiempo. Si necesita acceder a un valor de entrada, la propiedad aún existe y puede hacer referencia a ella sincrónicamente.

Este código es muy experimental, es solo una especie de poc y solo he jugado con él en ese stackblitz.
Por lo tanto, no debería haber dicho que era bastante seguro, lo siento, y es posible que no funcione con AOT como ha dicho.
Solo lo probé y ambos enfoques funcionan actualmente con AOT (angular v4.3.6, cli v1.4.1).

Mi punto principal aquí es que pasar de un changes observable que contiene todos los cambios de entrada le brinda la posibilidad de hacer cualquier cosa que necesite usando operadores: reaccionar cuando cualquier entrada cambió, cuando solo 1 entrada cambió, cuando un número arbitrario de entrada cambiado, cuando cambió de un valor específico a otro, etc.
Puede componer el observable que desee.

Además, solo hay 1 Sujeto para administrar para la implementación y 1 propiedad para usar para el usuario vs 1 por Entrada.

Lo que podría cambiar sería simplemente emitir nuevos valores y no SimpleChanges objeto ya que el usuario probablemente pueda usar pair y map para obtener el mismo tipo de estructura que SimpleChange . Pero actualmente lo que tenemos es SimpleChanges por lo que sería una tontería descomponerlo solo para recomponer algo similar más tarde.

PD:

Bueno, planteaste una crítica inválida, por lo que era necesario contrarrestarla.

inválido para usted .

No es un punto de vista personal cuando nos referimos al desempeño, estos son hechos objetivos medibles. Siente que 3 sujetos equivalen a más gastos generales que 6 operadores adicionales, seguro que tal vez tenga razón, pero no hay una base racional para creer eso hasta que realice algunas mediciones.

En cuanto a reaccionar a muchas entradas ... seguro que puede usar switchMap o combineLatest o cualquiera de los otros operadores diseñados para trabajar con múltiples observables. Está haciendo declaraciones muy audaces y amplias sobre esto, si realmente desea continuar esta conversación, salgamos de este rastreador de problemas.

Aquí hay una versión con diferentes inconvenientes y ventajas, pero requiere una clase base:

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

y para usar:

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

No estoy seguro de haber entendido mal el problema, pero ¿la tubería asíncrona no resuelve este problema?
[inputVar]="observableValue | async" ..

@najibla ha entendido mal, las entradas no son observables, por lo que la tubería asíncrona no ayuda.

@ohjames bueno en mi caso, funcionó usando la tubería asíncrona para un observable. Y cuando actualizo el valor observable del padre, el valor se refleja en el componente secundario.

@najibla, este problema de github se trata de convertir las propiedades @Input en observables, esto está tenuemente relacionado con el caso que estás describiendo.

es decir, no debería necesitar convertir las propiedades de entrada en un observable en la vista, ya sea que un componente responda al cambio de propiedad de entrada usando un observable o no, debería aislarse en un componente en sí. De lo contrario, debe cambiar la interfaz de su componente si decide consumir una propiedad como observable, lo que está en desacuerdo con el enfoque habitual de "observable primero" de angular.

@icepeng En realidad, no lo has

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

... entonces proporciona una forma de acceder a prop dentro de AppChildComponent como Observable aunque no sea un observable (o si fuera un observable, obtendría un observable de observables ). Ahora, por supuesto, si tenía prop$ en primer lugar como en su ejemplo, entonces puede que no parezca sensato (ya puede pasar el observable con entradas ahora). Sin embargo, proporciona mucho valor cuando la entrada pasada no es ya observable (por ejemplo, un index de un ngFor ).

El problema de transmitir un observable con una entrada (que es totalmente posible en este momento) es que no funcionaría del todo con OnPush .

@fxck Bueno, tienes razón en que ngOnChanges no se llamará cuando el observable emite (porque la entrada sigue siendo la misma), pero siempre que uses la tubería asíncrona para suscribirte a un observable pasado a través de @Input luego OnPush funcionará bien ya que la tubería asíncrona activa manualmente el detector de cambios del componente secundario.

Sí, estaba dirigido a @icepeng.

@ohjames @fxck No sabía que era posible transmitir lo observable en este momento, lo siento por la información incorrecta.

Dado que es posible pasar directamente observable, no estoy seguro de que @ObserveInput sea ​​útil.
Creo que si el valor que se puede cambiar no es Observable, ya es un problema.
Solo una opinión personal.

@icepeng Considere esto:

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

¿Cómo hacer que index consumible como un observable aquí sin saltar por los aros?

O también considere que está escribiendo una biblioteca de terceros y no quiere obligar a los consumidores de su biblioteca a pasar observables a su componente (muchos usuarios no se sienten cómodos con las transmisiones y evitan rxjs).

Hay muchos otros casos en los que esto es útil.

@ohjames index sería un valor estático para cada child-component , así que no creo que tenga que ser observable. Sin embargo, ahora entiendo por qué la gente quiere esta función. Y su solución parece buena para escribir bibliotecas de terceros.

BehaviorSubject enfoque @NgChanges() decorador

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

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

¿Es esto posible de implementar?

Sería preferible un ReplayObservable con una longitud de búfer de 1, no hay necesidad de la semántica del valor de BehaviourSubject cuando también tiene acceso a la propiedad.

En cuanto a si es posible implementarlo, consulte el historial de problemas para ver muchas implementaciones y opciones diferentes. Ninguno es genial y algunos están rotos, para hacerlo correctamente necesitamos el PR vinculado o algo similar.

mis 2 centavos: como object.observe se ha depreciado a favor de los proxies, mi solución fue extender un proxy (sí, no puedo saber, por lo que lo devolví del constructor) y luego escuchar cualquier clave perteneciente a este objeto como un Observable a través del método $ get (keyName). Puede usarlo con cualquier objeto, no solo con la clase angular uno.

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

Ejemplo:

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 , ¿hay algo de tracción en este tema? Sin duda, sería de gran ayuda para facilitar el uso.

@bryanrideshark La mejor posibilidad de que esto sea compatible es a través de https://github.com/angular/angular/issues/10185 Creo que me sorprende que las relaciones públicas no estén vinculadas a este problema.

Esto no sucederá hasta después de que enviemos a Ivy.
El miércoles 21 de febrero de 2018 a las 7:56 a.m., James Pike [email protected] escribió:

@bryanrideshark https://github.com/bryanrideshark La mejor oportunidad de
este apoyo es a través de # 10185
https://github.com/angular/angular/issues/10185 Creo que me sorprendió que
Las relaciones públicas no están vinculadas a este tema.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/angular/angular/issues/5689#issuecomment-367372931 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAgpkvtGA4w8uHgiz0QsdYwBgqgM2EpAks5tXDyWgaJpZM4Gwr8f
.

Tu inglés es excelente y tu angular es mayor.

@ pldin601 Su método no es compatible con el código compilado AOT, por lo que provocará grandes pérdidas de memoria para el código de producción de la mayoría de las personas. En su lugar, consulte mi paquete observable-input , que funciona bien con AOT.

@ pldin601 el problema con su código es que agrega dinámicamente el método de ciclo ngOnDestroy vida ngOnDestroy si el compilador puede inferir estáticamente su existencia. Entonces, su método solo funcionará si cada componente que usa el decorador también define un ngOnDestory (puede estar vacío si no es necesario). En todos los demás casos, filtrará las suscripciones, lo que evitará que los componentes se recolecten como basura.

Supongo que eso sucederá después de que se complete la sección 3.2 (y espero que esta necesidad se tenga en cuenta).
https://is-angular-ivy-ready.firebaseapp.com/#/status

¿Ivy será parte de 7.0.0?

He implementado un decorador que puede vincular una propiedad a una propiedad complementaria observable. Se puede utilizar en propiedades de entrada angular, pero también en cualquier otra propiedad. Está inspirado en el decorador @ObservableInput() de @ohjames , pero evita posibles problemas causados ​​por desajustes de tipos entre los captadores de propiedades y los definidores mediante el uso de una propiedad diferente para proporcionar el Observable.

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

Ejemplo:

class MyClass {

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

}

const myInstance = new MyClass();

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

myInstance.myProp = 'newValue'

Este código imprime los valores 'initialValue' y 'newValue' en la consola.

Cualquier actualización sobre cuándo se lanzará hasta ahora, el paquete observable-input parece el más elegante sin romper nada + sin subclases. @ohjames ¿

Creo que el soporte oficial sería más maduro, garantizará un soporte continuo en todas las versiones y una experiencia de desarrollo más fluida / alentará a los desarrolladores a utilizar rxjs * completos y optimizar con la detección de cambios de inserción (esta debería ser una característica fundamental innovadora en mi opinión)

* sin comprometer la compatibilidad con entradas no observables o sin necesidad de un texto estándar adicional para entradas híbridas, es decir: el setter llama a continuación sobre el tema

Se utilizó entrada observable en angulares 4, 5 y 7 con y sin aot. Parece que trabaja adecuadamente. Estoy bastante seguro de que sabía vagamente lo que estaba haciendo cuando lo escribí.

Espero que lo agreguen al marco y fomenten su uso 2 descargas semanales npm no es suficiente en comparación con las ventajas que ofrece xD si lo agregan Apuesto a que la gente verá que es simple seguir ese enfoque

Propongo seguir la API para la entrada asíncrona con el usuario definido observer

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

los usuarios pueden usarlo como

@AsyncInput()
asynchronusProperty1 = new Subject();

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

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

Angular internal puede simplemente llamar observer.next(newValue) siempre que cambie el valor.

¡No olvides también el soporte de @HostBinding !

Esto debería combinarse con la inyección @Input del constructor para hacerlo aún más asombroso. De esta manera, podríamos arreglar "strictPropertyInitialization" de una manera muy elegante:

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

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

}

@benneq solo vale la pena señalar que podría ser tan pequeño como:

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

@ maxime1992 sí, aunque necesitaría un decorador separado para las entradas como observables para distinguir entre el caso de que los observables pasen de un componente a otro.

@benneq @ maxime1992 Los nombres de los parámetros no forman parte de los metadatos del decorador, ¿cómo se asignan?

@trotyl Supongo que podrías usar @Input('paramName') private myParam: Observable<Data>

@trotyl @benneq sí mi mal. No importa: zipper_mouth_face:

No sé si alguien ya publicó esta solución, pero esta es una solución bastante buena

@ElianCordoba No se trata de HTML <input> . Se trata de @Input() angulares

Ese es un tema diferente, pero igualmente doloroso, @ElianCordoba. https://github.com/angular/angular/issues/13248

Creo que esto es un pseudo-requisito. En primer lugar, el decorador de entrada puede aceptar un valor de tipo observable; en segundo lugar, si el componente requiere un observable como entrada, debe arrojar un error explícitamente cuando se recibe un valor no observable en lugar de convertirlo silenciosamente en un valor de tipo observable.

Es bastante similar a la conversión de tipo implícita en javascript, puede confundirse incluso causar un error y es difícil de encontrar.

No se trata de silenciar la entrada incorrecta. Se trata de exponer una interfaz común entre los componentes (que es una entrada no observable). Esto le da la libertad de introducir la funcionalidad rx sin romper su interfaz en el mundo exterior. Otro beneficio es principalmente la elevación de la mutabilidad y el estado interno, ya que todo será una transformación de la entrada que hace que la detección de cambios de empuje desde ese punto en adelante sea una obviedad.

Por el contrario, exponer una interfaz que requiere observables suena torpe y obliga a las personas a proporcionar (valor) solo porque su componente lo dice me suena extraño.

Además, no se trata de una conversión silenciosa, es una API para suscribirse a los cambios a través de rxjs. Dado que angular.http y el enrutador proporcionan observables, es muy incómodo que el manejo de cambios de entrada no lo haga, unificar los mundos de devoluciones de llamada y RxJs requiere demasiada placa de caldera.

No es que no me gusten algunas de las soluciones inteligentes anteriores, pero a veces solo una ligera reorganización de la forma 'estándar' de hacerlo es suficiente para resolver el problema real aquí, que es la falta de claridad / torpeza. Además, todavía espero encontrar una forma "oficial" posterior a la hiedra de hacer esto algún día.

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

Luego, puede poner algo como combineLatest(this.service.magicInput$, this.inputs, config$)...... para combinar entradas con RxJS.

O BehaviorSubject o ReplaySubject funciona. BehaviorSubject suele ser más seguro y predecible.

  • BehaviorSubject: utilícelo si tiene valores predeterminados
  • ReplaySubject: el observable no se emitirá si olvida establecer el valor de entrada

Esto no solo ayuda con la claridad del código, sino que debido a que estoy agrupando todas las entradas, puedo ver fácilmente qué es una entrada observable 'pura' simplemente escribiendo this.inputs. y obteniendo autocompletar.


Puede ir más allá con la seguridad de tipos con esto (principalmente esto es solo por diversión).

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

Entonces no es necesario especificar explícitamente el tipo de propiedad @Input .

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

Podría haber creado una interfaz ObservableInputs para que el componente aplique inputs pero decidí no hacerlo, ya que no se compilará de todos modos si lo arruinas.

@simeyla Demasiado

Decidí poner mi propio decorador allí. Es mi primer paquete npm, así que estoy seguro de que me falta algo, pero aquí está: https://www.npmjs.com/package/@ng-reactive/async -input

Instalación

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

Uso

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 realmente agradable, no veo ninguna razón por la que algo como esto no deba incorporarse. Para su información, el enlace de github en npm para el paquete está desactualizado, va aquí:

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

Veo que este es un problema bastante antiguo, pero la función es bastante sorprendente y estoy realmente emocionado de verlo en Angular.

Lo que es aún mejor es que con las entradas observables no necesita OnChanges hook; puede usar el operador pairwise rxjs para obtener los valores anteriores y actuales de la entrada observable:

@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

Es mi implementación personal, funciona a la perfección y es bastante impresionante, si podemos tenerlo incorporado, eso es realmente genial.

He publicado la solución que he usado personalmente en mis proyectos como un paquete npm:

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

Instalación

npm install ngx-observable-input

Uso

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

    ...
}

Veo que este es un problema bastante antiguo, pero la función es bastante sorprendente y estoy realmente emocionado de verlo en Angular.

Lo que es aún mejor es que con las entradas observables no necesita OnChanges hook; puede usar el operador pairwise rxjs para obtener los valores anteriores y actuales de la entrada observable:

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

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

Este es un problema que también me ha estado molestando por un tiempo, así que investigué un poco y se me ocurrió la misma solución que mencionas aquí (@gund).

Implementé un pequeño ayudante de decorador ( @ObservableInput ) que extiende internamente el decorador @Input y permite este tipo de composición. Puede consultarlo aquí (también como paquete npm ). He estado probando con ejemplos simples, pero no tuve tiempo de usarlo para proyectos extendidos. Cualquier comentario es bienvenido :)

Aquí un ejemplo de uso:

@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}`)
  );
}

Y este componente se puede utilizar así:

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

@aleics ¿Estás seguro de que esto va a funcionar con el compilador AOT?

@aleics ¿Estás seguro de que esto va a funcionar con el compilador AOT?

No, no está listo para usar, por supuesto, ya que la propiedad @Input no está definida explícitamente. Pero puede definir su módulo con CUSTOM_ELEMENTS_SCHEMA , como cuando usa Componentes Web, y luego debería compilarse.

Este problema ha estado abierto durante 5 años 🤦‍♂. Creo que es necesario tener esto para facilitar un enfoque de programación reactiva.

Ivy finalmente está aquí, así que supongo que alguien tiene tiempo para trabajar en esto. ¡Por favor por favor por favor!

Dios mío, suscribo que esto es muy necesario. Los datos de entrada deben emitirse como una fuente reactiva, y debe usar una solución alternativa para que funcione en un ngOnChanges

Realmente esperando esto

Me doy cuenta de que Angular es de código abierto, pero ¿es incorrecto o ingenuo de mi parte pensar que un proyecto ejecutado por Google debería poder resolver problemas muy destacados y solicitados en menos de 5 años? ¿Hay algunos problemas financieros que no conozco que impidan que Google contrate más desarrolladores para trabajar en Angular?

@ etay2000 No lo sé, creo que es un poco gracioso cómo puedes hacer el 75% de las cosas en Angular con rxjs y luego, para algo tan fundamental y de uso común como entradas de componentes, de repente te ves obligado a entrar en el procedimiento mundo. Siempre tiene esta parte importante de su proyecto que no puede funcionar con el mundo de los observables, por lo que siempre está obligado a usar el texto estándar para pegarlos nuevamente. Nunca podrá lograr un enfoque completamente funcional para su proyecto. Entonces, este problema que estuvo abierto durante 4 años me recuerda la falta de pragmatismo que me alejó de Angular hacia otros marcos, por eso es bastante útil. Si desea usar rxjs para todo, tal vez cycle.js sea una mejor solución, y si no quiere verse obligado a usar rxjs en absoluto, tal vez Vue lo sea. Tener que usar observables la mitad del tiempo y no poder usarlos la otra mitad simplemente no tiene ningún sentido. Integrar una abstracción muy poderosa con una alta curva de aprendizaje y luego no integrarla por completo es simplemente desconcertante.

Siento que la cadena de priorización es así

requisitos de los usuarios internos de Google -> cualquier cosa que tenga que ver con que el compilador sea más rápido -> cualquier cosa que tenga que ver con que el tamaño de la aplicación sea más pequeño -> cualquier cosa que no moleste a los recién llegados -> lo que los desarrolladores realmente quieren

Y aunque los líderes tecnológicos dirían que están de acuerdo en que es importante, el hecho es que Igor y Misko tienden a pensar en la mayoría de los problemas en términos de compilación de plantillas, enlaces de vista y renderizado. No importa lo que los consumidores de Angular digan que es importante para ellos, la solución siempre es cambiar el sistema de plantillas, escribir un nuevo renderizador o mejorar el compilador.

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

Solo mire algunos de los problemas más votados.

Propuesta: Entrada como observable # 5689

Creado hace 5 años, última respuesta de un miembro del equipo relevante - hace 5 años, estado actual desconocido.

Admite transmisiones de eventos en frío en las plantillas n. ° 13248

Creado hace 4 años, última respuesta de un miembro relevante del equipo - hace 1 años, estado actual - esperando que Ivy aterrice.

Propuesta: proporcionar enlaces del ciclo de vida de los componentes como elementos observables n. ° 10185

Creado hace 4 años, última respuesta de un miembro del equipo relevante - hace 3 años, estado actual desconocido.

Propuesta: se necesita la capacidad de agregar directivas a los elementos del host en la declaración del componente. # 8785

Creado hace 4 años, última respuesta de un miembro del equipo relevante - hace 2 años, estado actual desconocido.

as local-val sin * ngIf # 15280

Creado hace 4 años, última respuesta de un miembro del equipo relevante - hace 2 años, estado actual desconocido.

Soporte para proyección de contenido dinámico # 8563

Creado hace 4 años, última respuesta de un miembro del equipo relevante - hace 3 años, estado actual desconocido.

ng-content contenido predeterminado # 12530

Creado hace 4 años, última respuesta de un miembro del equipo relevante; nunca, estado actual desconocido.

Luego hay cosas como las características de los componentes, que permitirían componentes de orden superior, luego, por supuesto, hay muchos más problemas con la mayoría de votos a favor, pero esperaba que se abordaran (es decir, que se aborden, no necesariamente esperando que se implementen inmediatamente después de Ivy ), porque tienen que ver con el uso diario del marco Y la reactividad, que, creo que nadie diría, es lo que la mayoría de la gente está usando (dado que ngrx es la biblioteca de administración estatal más popular).

Mi aplicación está llena de "polyfills" a medias (simplemente debido a limitaciones técnicas, algunos solo se pueden hacer correctamente en el núcleo) y hackeos para la mayoría de estos problemas, con la esperanza de que algún día pueda incluir un nativo angular reemplazo. Pero de alguna manera no parece venir.

Entonces, sí, digamos que una publicación de blog que aborde el estado de algunos de los problemas más importantes relacionados con la experiencia de reactividad / desarrolladores sería realmente apreciada.

¿Qué tal si intentamos codificar una primera versión de esta función nosotros mismos? estoy
muriendo de aburrimiento durante la cosita del covid, ¿quién quiere acompañarme?

Varias personas ya lo hicieron, por ejemplo. https://github.com/insidewhy/observable-input

El problema con la mayoría de estos es que para hacerlo de manera eficiente, necesita modificar el compilador u otras partes internas de angular.

Eso no cuenta. Quiero decir, hagamos una contribución adecuada al ángulo
centro. Después de todo, tenemos el código fuente disponible. En realidad, no creo que necesitemos tocar el compilador en este caso.

@ganqqwerty Traté de implementarlo dentro de Angular antes de escribir esa biblioteca. Sin embargo, Angular es extremadamente complicado. Para ser justos, el código es mucho más legible y comprensible que React y sus locas funciones de quinientas líneas de largo, pero es "sofisticado". Y todavía me hubiera gustado ponerme el esfuerzo de hacer esa implementación si hubiera pensado que valía la pena. Pero no veo que valga la pena precisamente por las razones que @fxck repitió anteriormente. El equipo de Angular no escucha a la comunidad, se obsesionan constantemente con las optimizaciones y el rendimiento sin preocuparse por la usabilidad de Angular en general. No me importa un marco que funcione bien cuando me obliga a implementar un modelo estándar para problemas simples que se demostraron hace cinco años. Así que decidí dejar de preocuparme por Angular, implementar el mínimo esfuerzo que necesitaba para respaldar los proyectos en los que ya estaba trabajando y evitarlo siempre que fuera posible para todos los proyectos futuros.

Creo que esto no es solo un problema con Angular, sino con muchas ofertas de Google. Este problema y los demás que @fxck enumeró anteriormente son solo más ejemplos de problemas endémicos en Google. Si observa Dart o GoLang, también verá que los desarrolladores plantean problemas muy conocidos que se enfrentan comúnmente y que los problemas languidecen abiertos durante más de 5 años.

Estoy de acuerdo en que esto debería agregarse, pero vaya, el odio en este hilo es un poco excesivo, quiero decir, estamos hablando de agregar algo que guarde algunas líneas de código. Puede tener entradas observables ahora con un setter decorado con @Input y su propio sujeto al que empuja los valores:

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

Si eso es demasiado repetitivo, ya tenemos algunas opciones simples de terceros:

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

Estoy contento con el desarrollo de Angular, y ciertamente no abandonaría el marco increíblemente completo y confiable debido a una característica tan pequeña (aunque deseable)

El problema general es principalmente desde la perspectiva de cómo lo ve la gente. Para alguien, son unas pocas líneas de código y parece bastante simple cambiar / agregar cualquier cosa, pero en realidad, hay un alcance mucho mayor y muchos efectos secundarios. Todos ellos hay que tenerlos en cuenta y como siempre, la línea de temas es larga y todo tiene algún precio de coste, valor añadido y prioridad.

Me gustaría señalar que personalmente no espero necesariamente que todos estos problemas se resuelvan de inmediato.

Pero espero que, ahora que Ivy está fuera del camino y ha estado en la naturaleza por un tiempo, se publique algún tipo de actualización oficial sobre, digamos, los 25 temas más votados.

Quiero decir, no encuentro posible que un proyecto de este tamaño no tenga en la gestión de proyectos una prioridad considerada y algún tipo de plan para todos los 25 problemas principales (o lo que sea) votados a favor. La mayoría de estos incluso tienen una etiqueta effort . Simplemente haga una publicación en el blog, deje que la comunidad sepa que sabe acerca de estos y que tiene algún tipo de plan para ellos.

Ciertamente tampoco abandonaría Angular, a pesar de todas sus deficiencias, todavía me gusta mucho más que todas las otras alternativas. Pero creo que es justo decir que le ha faltado "gestión de relaciones con la comunidad" (específicamente en github).

// editar

¿Les importaría llenar esta encuesta, buena gente? Dada su situación actual y considerando todos los aspectos, cuál de estos problemas le afecta más / mejoraría más su experiencia de desarrollador https://forms.gle/cprhx239kuqwWd5M8

@fxck Gracias por escribir eso. Empezaba a preguntarme si todo el mundo acababa de aceptar el hecho de que el equipo de Angular hará lo que quiera, cuando quiera. Si bien ese es ciertamente su derecho, también creo que es necesario que la gente los llame cuando ignoran continuamente las voces de los desarrolladores. Estoy de acuerdo en que no creo que nadie espere una resolución inmediata de los problemas, pero ignorarlos durante 4-5 años o simplemente esperar hasta que el problema se bloquee automáticamente parece realmente poco profesional para cualquier proyecto, y mucho menos para un proyecto ejecutado por Google. Ganaron algo de tiempo agregando etiquetas 'Fixed by Ivy' a un montón de problemas abiertos, luego siguieron sin hacer nada.

Ciertamente tampoco abandonaría Angular, a pesar de todas sus deficiencias, todavía me gusta mucho más que todas las otras alternativas. Pero creo que es justo decir que le ha faltado "gestión de relaciones con la comunidad" (específicamente en github).

Creo que esa frase resume perfectamente mis pensamientos.

@chriszrc Realmente no interpreté nada en este hilo como 'odio'. Más bien, las personas finalmente desahogan sus frustraciones con Angular que regularmente ignora los problemas durante años. Ni siquiera se trata de este tema específicamente, se trata más del principio de mantener alguna forma de interacción comunitaria. ¿Se considera que el equipo de Angular es tan superior a cualquier otra persona que sienten que pueden ignorar las solicitudes de los desarrolladores porque saben qué es lo mejor para todos?

@chriszrc No veo ningún odio en este hilo, solo un montón de frustración comprensible. Creo que equiparar esta solicitud y la decepción relacionada con "guardar algunas líneas de código" es una exageración. No es solo este problema lo que frustra a la comunidad, sino la importante colección de problemas populares que @fxck planteó anteriormente y que se han descuidado.

Hola a todos, disculpen el silencio en este hilo. Hay dos solicitudes ortogonales que hemos estado recibiendo durante un tiempo:

  1. Mejor soporte RxJS de primera clase en el marco
  2. Menos RxJS en Angular, lo que potencialmente lo hace opcional

Ambas solicitudes están en nuestro radar y ambas tienen sus pros y sus contras. Por ejemplo, si vamos en la primera dirección, haremos que el marco sea menos accesible para los ingenieros nuevos en la API expresiva y semánticamente rica de RxJS. A muchas personas les preocupa el uso de RxJS en todas partes en sus aplicaciones angulares, lo que hace que el código fuente sea difícil de leer entre los miembros del equipo con diferentes niveles de experiencia.

Al mismo tiempo, ya tenemos muchas API reactivas, pero algunas de las primitivas que ofrece Angular son completamente imperativas y las cosas no se conectan muy bien. Dicho esto, tiene sentido mejorar el soporte de reactividad, pero ¿debería ser parte del núcleo de Angular o delegarse a un proyecto comunitario (ngrx, la extensión reactiva de Angular, por ejemplo) con el que estamos colaborando estrechamente?

Hay muchas formas en que podemos resolver este problema, pero debemos considerar:

  • Compatibilidad con versiones anteriores, no solo para el código fuente, sino también para los recursos de capacitación.
  • Curva de aprendizaje
  • Coherencia con las API actuales
  • Coherencia entre proyectos
  • Rendimiento: tiempo de ejecución y tiempo de carga inicial

Estamos en el proceso de priorizar algunos de los principales problemas de GitHub, analizando las mejoras más solicitadas e impactantes que podemos abordar. Yo diría que este es uno de los más desafiantes, por lo que es difícil comprometerse con una cita.

@mgechev Gracias por responder. Si los rxjs y las apis reactivas se van a dividir en otra extensión (no en mi voto personal), ¿posiblemente tendría más sentido que eso no se vincule con una implementación de state-store / redux en particular? Pasé rápidamente de ngrx a ngxs (que parecía mucho menos repetitivo y mucho más "angular" con los decoradores) y finalmente a Akita, que es mi favorito actual y con el que más he estado desarrollando. O si se agrupa con ngrx, al menos haz que sea realmente fácil agregar las partes que necesitamos, para que no estemos bloqueados en el uso de ngrx o en la inclusión de esas dependencias.

Si los rxjs y las apis reactivas se van a dividir en otra extensión (no es mi voto personal)

No estoy diciendo que eso sea lo que va a pasar. En mi comentario solo estoy compartiendo una lista incompleta de alternativas para que pueda mostrar el alcance del proyecto y las diferentes consideraciones que tenemos.

@mgechev de hecho, gracias por la respuesta.

Sin embargo, me pregunto, si la parte reactiva de Angular se "degrada" de alguna manera al proyecto comunitario, ¿no significaría eso que reactivo se convertirá en un ciudadano de segunda clase de Angular?

@mgechev Siento que tu dolor se desgarra en dos direcciones a la vez. Nuestro equipo en realidad no se opone a dividir las piezas imperativas / reactivas.

Estos serían nuestros requisitos para tal división:

  • Usar tanto el imperativo como el reactivo necesita sentirse nativo y ninguno debe sentirse atornillado
  • No se vincula con una solución de administración de estado específica para habilitar reactivo (divulgación completa, hemos migrado todos los proyectos de 50+ para usar akita por ahora)
  • Ambas opciones deben lanzarse en una sola versión al mismo tiempo (sin demora / demora)
  • No puede haber un "favorito", cualquier cosa que haga angular debería funcionar en ambos mundos mientras se siente nativo.
  • Soporte oficial completo para ambos enfoques respaldado por el equipo angular (según nuestra experiencia, este es un requisito del cliente de parada estricta)

Basado en lo anterior:

  • Hay 2 opciones principales:

    • core imperativo + complemento reactivo

    • core reactivo + complemento imperativo

  • Envolver reactivo para que parezca imperativo es bastante sencillo, no al revés
  • Por lo tanto, votaríamos por usar reactivo en el núcleo y hacer que el complemento contenga las API imperativas
  • Debido a la demora cero + el requisito de no favoritos, así como al soporte oficial requerido, no veo cómo el complemento puede ser un proyecto comunitario

Entiendo que lo anterior es nuestra visión del mundo y hay muchas otras. La razón por la que escribo esto es:

  • Si aterriza en la extracción de reactivo en un proyecto comunitario, tendremos que migrar más de 50 soluciones a otro marco (nuevamente, esto será impulsado por el cliente, no nuestra elección)
  • Así que pediría que nos (la comunidad) sepamos lo antes posible dónde aterriza en esto para que tengamos tiempo para volver a capacitar a nuestros desarrolladores en otro marco y migrar esos proyectos.

agregue la característica por favor

@mgechev No entiendo tus preocupaciones. Esta propuesta trata de combinar los dos mundos con una interfaz uniforme y directa para desarrolladores novatos. No se trata de elegir uno u otro, sino de habilitar la combinación. Por ejemplo, el desarrollador x decide construir un componente reactivo impresionante. Y el desarrollador y decide utilizarlo en su proyecto no tan reactivo. ¿Por qué no? ¿Por qué necesita depender de cualquier pegamento ngrx?
La diferencia entre los dos mundos es si almacena / administra el estado en su último componente inteligente frente al almacén de estado central y de eso se trata ngrx, pero para los componentes creo que el marco en sí mismo debe ser el habilitador.

Hay dos solicitudes ortogonales que hemos estado recibiendo durante un tiempo:

  1. Mejor soporte RxJS de primera clase en el marco
  2. Menos RxJS en Angular, lo que potencialmente lo hace opcional

Estaría bien de cualquier manera, pero esto a veces esto a veces le da una sensación inconexa a Angular, que es lo último que haría excepto de un marco con baterías incluidas.

Personalmente, tengo curiosidad por ver cómo tendrías versiones que no sean RxJS para todos los paquetes de Angular (HTTP, Router, Forms). Sospecho que no hay buenas versiones de estos que no sean RxJS, por lo que tienen RxJS en primer lugar.

Es decir ... a menos que me falte algo, (2) no es factible.


PD: Yo no describiría esas solicitudes como "ortogonales"; más como "opuesto".

Menos RxJS en Angular, lo que potencialmente lo hace opcional

Por curiosidad @mgechev , ¿hay algún problema con Github para eso? (No he visto ninguno.) ¿O es más una cosa de Google?

@pauldraper He visto al menos un problema en github en el que alguien se queja de lo horriblemente complejo que es rxjs y quiere que sea completamente opcional. Personalmente, me encantan los rxjs, pero ahora he trabajado con dos equipos en los que trajo miedo y odio a los desarrolladores junior y senior. Además, ortogonal es el término mejor que opuesto, ya que es posible implementar ambas solicitudes al mismo tiempo (dado que cada solución se puede trabajar sin que una se desvíe de la otra proporcionando múltiples opciones de API para consumir cosas de manera reactiva e imperativa).

Por cada comentario que se queja de rxjs en github, probablemente hay miles de desarrolladores que lo usan y lo disfrutan sin problemas.

image

image

Aquí están los resultados de ese formulario que publiqué anteriormente (casi 200 personas respondieron), ya que los estaba discutiendo en Angular Discord.

Como era de esperar, las formas son el mayor dolor en el trasero, pero aparentemente @brandonroberts y @MikeRyanDev podrían estar cocinando algo, por fin.

Por cada comentario que se queja de rxjs en github, probablemente hay miles de desarrolladores que lo usan y lo disfrutan sin problemas.

Aunque me encantan los rxjs, no soy tan optimista. Trabajé con muchos desarrolladores que están tan agotados que la idea de tener que dedicar tiempo para aprender algo con una curva de aprendizaje empinada es una pesadilla. Y muchos a los que les importa mucho más ganar dinero que codificar: D O los desarrolladores junior agresivos que piensan que todo lo que no pueden aprender en el espacio de un día debe ser basura.

Trabajé con muchos desarrolladores que están tan agotados que la idea de tener que dedicar tiempo para aprender algo con una curva de aprendizaje empinada es una pesadilla. Y muchos a los que les importa mucho más ganar dinero que codificar

¿Esos son los desarrolladores que Angular intenta atender entonces, agotados y sin querer aprender? :) Y a quién le importa en este momento de todos modos. Angular ha existido por un tiempo, no va a ninguna parte y absolutamente no perderá tracción si agrega el ciclo de vida observable, la entrada observable, los eventos observables. Simplemente agregue esas tres pequeñas cosas y la gente estará feliz por un tiempo, eso es todo (y siga con https://indepth.dev/component-features-with-angular-ivy/ para completar el paquete básico).

angular_rxjs

Este lo dice todo. En mi humilde opinión, Angular debería mantenerse consistente y usar más RxJS: sin RxJS en absoluto, no sería Angular.

¿Esos son los desarrolladores que Angular intenta atender entonces? :)

Sí, sería pretencioso atender solo a desarrolladores que quieran escribir código usando rxjs. Aunque prefiero usar rxjs, quiero que Angular se adapte a ambos conjuntos de desarrolladores. ¿Por qué Angular debería atender solo a nosotros y no a aquellos que piensan de manera diferente? Especialmente cuando creo que somos minoría, no mayoría.

Nuevamente, volverse "completamente reactivo", como agregar ciclos de vida observables, entradas, eventos no afectaría de ninguna manera a aquellos de los que está hablando, pero haría muy feliz al otro grupo.

Y las características de los componentes son útiles tanto si le gusta la programación reactiva como si no.

@fxck Nunca estuve en desacuerdo con eso por un segundo. Solo estaba respondiendo a su comentario anterior para señalar que hay muchas personas que no quieren usar rxjs y muchas de ellas han abierto problemas de github para quejarse.

A pesar de cuán angular decide desarrollar el núcleo, de manera reactiva o no, en el mundo ideal habría el paquete opuesto construido en la parte superior para satisfacer las necesidades opuestas (por ejemplo, @angular/reactive o @angular/imperative - con mejores nombres Ojalá).

Cualquiera de estos dos ir a la comunidad es para mí una degradación para algunas personas y para mí tener una perspectiva angular de ser un gran marco reactivo fue una razón para seguirlo en primer lugar.


En una nota al margen, creo que sería más fácil mantener angular reactivo y tener extensiones imperativas, ya que esto es mucho más fácil de unir, en lugar de unir imperativo a reactivo, hablando de la simplicidad tanto del núcleo como del paquete de extensión.

Descargo de responsabilidad: el último punto que formulé en base a suposiciones, no me sorprendería si es mucho más fácil escribir el imperativo central, aunque la extensión reactiva sería más compleja.

Por curiosidad @mgechev , ¿hay algún problema con Github para eso? (No he visto ninguno.) ¿O es más una cosa de Google?

No es una cosa interna de Google. Se sorprendería (al igual que yo, cuando me uní al equipo) de lo grande que es la intersección entre los requisitos externos e internos. La principal diferencia está en el sistema de compilación y la gestión de dependencias, además de que los empleados de Google tienen prácticamente los mismos requisitos que los que no son de Google.

Estamos en contacto con miles de desarrolladores: muchas empresas externas, equipos internos y colaboradores individuales. En ambos casos, hay personas a las que les encantaría ir de lleno con RxJS y otras que no creen que sea la opción correcta para ellos. Ambos campos tienen excelentes argumentos.

Esta no es una decisión que nos gustaría tomar apresuradamente, especialmente dado que existen soluciones alternativas / de la comunidad bien establecidas. El trabajo realizado en la comunidad nos ayuda a recopilar puntos de datos adicionales y establecer una solución óptima para el conjunto actual de variables.

Creo que tener un enfoque unificado reactivo o imperativo (es una elección por proyecto) al desarrollar una aplicación angular sería muy beneficioso. La capacidad de mezclar y combinar no ayuda a la experiencia de desarrollo y crea código no DRY (por ejemplo, entradas no reactivas).

Nada sucede de forma aislada, por lo que puedo entender totalmente que los entornos corporativos tienen dificultades para volver a capacitar a una gran cantidad de desarrolladores en la programación reactiva. Entonces, eliminar el imperativo realmente movería a angular fuera del punto óptimo corporativo, lo que, seamos realistas, simplemente no sucederá, ya que constituye una gran base de usuarios clave para un marco como angular.

Dicho esto, lo que no veo como realista es construir una interfaz reactiva sobre una imperativa. La otra dirección es bastante sencilla, ya que siempre puede escribir la salida de un observable en una variable que luego se puede usar imperativamente. De hecho, eso es lo que angular ya está haciendo en muchos lugares bajo la cubierta.

Por lo tanto, voto por hacer angular completamente reactivo en su núcleo y luego creo "envoltorios" imperativos en la parte superior.

Es muy probable que el problema principal con este enfoque sean pasos de migración más grandes para usuarios imperativos. Lo cual es un nono en entornos corporativos que podría ser exactamente lo que el equipo angular está tratando de prevenir, de ahí la sugerencia de que sea imperativa en el núcleo.

@mgechev Dado que esto se ha planteado un par de veces, sería interesante escuchar sus pensamientos sobre el núcleo reactivo / extensión imperativa y si tiene alguna pierna sobre la que pararse.

En ambos casos, hay personas a las que les encantaría ir de lleno con RxJS y otras que no creen que sea la opción correcta para ellos. Ambos campos tienen excelentes argumentos.

Genial, bueno saberlo. ¿Definir ir de lleno con RxJS? ¿Cómo encaja en eso agregar el ciclo de vida observable, la entrada observable y los eventos observables? Y no, ninguno de estos tiene realmente una "solución alternativa / comunitaria bien establecida".

@fxck ninguno de estos tiene realmente una "solución alternativa / comunitaria bien establecida".

Es cierto. Escribí una de estas soluciones y solo funciona debido a un gran truco que se basa en una verificación de tipo deficiente en las plantillas. También revisé algunas de las otras soluciones publicadas en este hilo y cada una de ellas tiene al menos uno de los siguientes problemas:

  • filtraciones de suscripción
  • requisitos estándar del usuario
  • hacks tan malos, si no peores, que el que usé en mi biblioteca.

En lo que a mí respecta, es imposible hacer esto bien.

Los eventos observables son aún peores, no hay muchas soluciones de la comunidad, generalmente requieren un paso de compilación adicional (que en algunos casos rompe SSR en configuraciones de monorepo) y hacks además de eso (si está utilizando la extensión de clase, por ejemplo, para obtenga eventos observables del ciclo de vida, porque oye, angular no lo admite de forma nativa) https://github.com/typebytes/ngx-template-streams/issues/8

Los eventos del ciclo de vida son posiblemente los "más fáciles", pero aún tienen que depender de una extensión de clase o decoradores personalizados, ninguno de los cuales es ideal.

@fxck Hasta donde yo sé, no es posible limpiar las suscripciones de eventos del ciclo de vida sin tener que usar una clase principal (eugh) o un método al que el usuario tiene que llamar desde su propio evento de ciclo de vida de destrucción (incluso peor).

Los eventos del ciclo de vida son posiblemente los "más fáciles", pero aún tienen que depender de una extensión de clase o decoradores personalizados, ninguno de los cuales es ideal.

También había intentado implementar eventos del ciclo de vida (para hacerlos reactivos) con decoradores personalizados y es una molestia. ~ Sin extensión de clase, no sé si es posible sin 100 hacks ~. La posibilidad de que el usuario extienda angular es hasta ahora muy pobre y depende de hacks o mucho texto estándar para hacerlo.

@ tonivj5 Si la característica del componente fuera una API pública, ¿ve esto como una forma potencial de implementar ciclos de vida sin herencia?

@ntziolis hah es gracioso, deberías mencionar las características, en realidad estoy trabajando en la parte del ciclo de vida de ngx-sub-form y las características son exactamente algo que creo que ayudaría. Mientras tanto, es muy posible parchear la fábrica de componentes con hiedra. Veo esto como muy útil fuera de ngx-sub-form, así que creo que dividiré esta lógica en una biblioteca separada

@ntziolis en este punto, no estoy seguro de si alguna vez veremos que se cumplen las promesas de Ivy.

¿Has comprobado el efecto angular? (no ngrx / effects) https://dev.to/stupidawesome/reactive-adventures-in-angular-introducing-angular-effects-1epf Con esta herramienta puede escribir aplicaciones completamente reactivas.
Estado observable bidireccional entre los componentes padre e hijo incluidos
https://dev.to/stupidawesome/exploring-the-angular-effects-api-2gol
La versión 9.1.0 introducirá un modelo de composición / ganchos basado en la API de composición de Vue 3.
Creo que es el siguiente paso para que angular funcione con reactividad.

@mgechev

Esta no es una decisión que nos gustaría tomar apresuradamente, especialmente dado que existen soluciones alternativas / soluciones comunitarias bien establecidas.

Solo por curiosidad, ¿quiénes somos y cuántos Googlers contribuyen activamente al proceso de toma de decisiones de Angular?

Usted indica que esta no es una decisión que deba apresurarse, pero me pregunto cuál es su definición de no apresurarse. ¿Es este uno de los que le ponen una etiqueta 'Fixed by Ivy' y lo ignoran hasta que más desarrolladores comiencen a hacer ruido nuevamente en situaciones de tipo? ¿Hay una versión específica en mente para cualquier trabajo reactivo o hay otras versiones más nuevas del compilador que deben ser lanzadas primero?

¿El hecho de que normalmente hay 2500 asuntos abiertos en un momento dado surge alguna vez en las discusiones internas?

@ntziolis , como dijo @zakhenry , creo que features ayudaría

@ntziolis He tengo un POC que funciona (sin extensión de clase ni implementa interfaces de ciclo de vida). Se basa en una API privada (: exclamación :)

@ tonivj5 hah No he tenido la oportunidad de leer el tuyo, pero casi tengo mi solución lanzada también :) https://github.com/cloudnc/ngx-observable-lifecycle

La idea con la biblioteca en la que estoy trabajando es tener una base común a la que otras bibliotecas puedan conectarse para crear su propia funcionalidad consciente del ciclo de vida. Debería poder funcionar de manera que si hay varias funciones que desean acceder al mismo gancho, solo hay un gancho decorado en el componente def.

Lo tengo todo funcionando, solo necesito ordenar CI y pruebas unitarias.

¿El hecho de que normalmente hay 2500 asuntos abiertos en un momento dado surge alguna vez en las discusiones internas?

Lol, no ha habido 2500 problemas abiertos desde mediados de 2019.

$ 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)  |
+-------------------------+--------------------+

A mi gusto, prefiero proyectos de código abierto que mantengan los temas abiertos a discusión hasta que haya una discusión / consenso claro de la comunidad en lugar de proyectos que cierren cada tema abierto sin discusión. También es un paso para que los mantenedores expliquen sus preocupaciones a la comunidad antes de cerrar cualquier posibilidad de aceptar una propuesta.

@agalazis Este problema ha estado abierto durante 4 1/2 años y prácticamente se ha ignorado durante los últimos 3.

@agalazis Este problema ha estado abierto durante 4 1/2 años y prácticamente se ha ignorado durante los últimos 3.

Un miembro del equipo de Angular explicó por qué esto todavía está abierto hace solo 5 días, por lo que simplemente no tiene razón.

@ etay2000 Algunas decisiones son difíciles algunas veces ya que afectan la evolución de todo el proyecto y eso es aceptable. Los desarrolladores pueden darse cuenta de que no pueden confiar en la función en un futuro cercano a medida que pasa el tiempo, pero si existe la posibilidad, ¿por qué eliminarla prematuramente? Sí creo que lo más fácil fue rechazar la propuesta y cerrar el tema, pero no sucedió por una razón.

@beeman

Un miembro del equipo de Angular explicó por qué esto todavía está abierto hace solo 5 días, por lo que simplemente no tiene razón.

Realmente te gustan esos emojis de pulgares hacia abajo, ¿eh? ¿Fue eso realmente una explicación? Después de más de 4 años, dijo que no quieren apresurar la decisión.

@agalazis
Estoy de acuerdo en que algunas decisiones son difíciles, pero ¿cuál debería considerarse un marco de tiempo aceptable para posponer la toma de esas decisiones difíciles? No todos los 2855 problemas abiertos involucran estas decisiones difíciles, sin embargo, muchos de ellos también han existido durante bastante tiempo.

@ etay2000 Acéptelo o no, esta es la gente de código abierto que abre problemas es mucho más que los mantenedores. El proyecto TypeScript, por ejemplo, tiene casi el doble de problemas abiertos y otra propuesta que hice allí también tomó mucho tiempo. No me importa, ya que la discusión trajo muchas alternativas que no son tan elegantes como tener la función, pero lograron ver muchas otras perspectivas.

@agalazis Tienes razón, supongo que (incorrectamente) esperaba más de un proyecto de código abierto mantenido por Google que de un proyecto más pequeño.

@beeman Inserta el emoji con el pulgar hacia abajo aquí: -------->

@ etay2000 Siempre debe recordar que cada decisión importante afecta a cientos de miles de cosas, proyectos y personas. Y lo que es aún más importante, una decisión no perfecta y la introducción de API públicas, que llevaría a algunos cambios importantes en poco tiempo después de eso, es solo una pesadilla. Puede ver casi en cada RP, cómo se revisa cada paso y, si no es perfecto, simplemente espera, semanas, meses o incluso años.

@agalazis Tienes razón, supongo que (incorrectamente) esperaba más de un proyecto de código abierto mantenido por Google que de un proyecto más pequeño.

No lo sé, mira Go y dart también. Google es bastante famoso por ignorar las solicitudes de la comunidad enormemente populares durante media década: D En general, su enfoque de los lenguajes / frameworks / etc. es muy conservador . Puede ver que surgen nuevos lenguajes e ideas que revolucionan áreas y Google aún no los ha adoptado durante años, por ejemplo, la seguridad nula golpeó a Kotlin, TypeScript y Scala mucho antes de Dart. Supongo que hay beneficios y deficiencias en este tipo de enfoque, pero al menos para mí, tiendo a evitar las cosas desarrolladas por Google en la medida de lo posible porque realmente no encaja con mi espíritu.

(Lo siento, todos, pero tengo que hacerlo).

@beeman Inserta el emoji con el pulgar hacia abajo aquí: -------->

@ etay2000 No sé cómo este tipo de comentario ayuda en absoluto, si no una violación de conducta aquí. Estoy bastante seguro de que todos saben cómo poner el pulgar hacia abajo, entonces, ¿cuál es el punto del sarcasmo?

Solo como recordatorio, nadie está obligado a usar Angular, puede intentar encontrar otro marco donde la gente lo complazca más.

@brunojcm Supongo que la fiesta terminó ahora que la policía de comentarios está aquí. El caso es que rechazó todos mis otros comentarios, pero no me di cuenta de que el sarcasmo era una violación de la conducta.

Solo como recordatorio, nadie está obligado a usar Angular, puede intentar encontrar otro marco donde la gente lo complazca más.

La forma en que realmente quiero responder a eso definitivamente violaría el código de conducta aquí.

Ya terminé, todos pueden volver a su programación programada regularmente. Volveré a comprobarlo en otros 4-5 años para ver cómo resultaron las cosas.

@ tonivj5 @zakhenry Me alegra saber que esto podría ser una avenida.

@mgechev Entiendo que esto ni siquiera es una discusión preliminar, pero me encantaría escuchar sus pensamientos sobre:

  • núcleo reactivo + extensión no reactiva vs al revés.
  • y más específico de este problema, ¿existe alguna posibilidad realista de exponer la API de funciones de alguna manera?

    • incluso si es solo con la advertencia de cambios importantes en el futuro, siempre que la función general aún sea accesible.

    • Como lo ha hecho

    • Luego, podría trabajar con estos equipos de envoltura para informarles sobre los próximos cambios importantes desde el principio (muy similar a su sugerencia sobre extensiones reactivas)

Creo que ustedes pueden aplicar directamente el módulo reactivo de Vue 3.0 (ref o reactivo) y resolver este problema.

@AnonymousArthur si está hablando del paquete @vue/reactivity entonces no tiene ninguna relación con lo que estamos discutiendo aquí, ya que proporciona reactividad a las propiedades y no tiene nada que ver con los flujos reactivos (que es lo que es RxJs).

@gund Sí, estoy de acuerdo. Sin embargo, esta conversación lleva bastante tiempo (4.5 años) y aún no ha terminado, por lo que simplemente se me presenta una idea (puede que no lo sea) muy rápidamente para quienes no quieren escribir subscribe / unsubscribe o ngOnChange Handler. RxJS proporciona una función de reactividad y esta conversación (no la leyó toda) está hablando de envolver el valor de entrada con el observable como un azúcar sintáctico para que se pueda ver y creo que es una idea increíble.

Creo que esta idea es realmente útil y el prototipo de @robwormald al principio se ve muy bien, pero creo que ha subestimado los beneficios potenciales y el impacto que tendría que ser mínimo.

Permítanme proponer un ligero ajuste en esa solución para corregir algunas deficiencias, pero básicamente el mismo enfoque:

Angular definiría un decorador @OInput() y un tipo EventReceiver<T> que extiende Observable<T> pero también agrega un .value getter (como BehaviorSubject<T> ).

En el lado del componente principal , como en el ejemplo de @robwormald , nada cambia. Si desea pasar un valor observable allí, aún necesita | async . O puede pasarle un tipo no observable, eso también está bien (como siempre).

En el lado del componente secundario , propongo que se vea así (una ligera desviación de esa propuesta):

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

Ahora, estos son los beneficios de este enfoque, que creo que se vendieron un poco menos:

  • Esto permite diseños completamente reactivos de manera efectiva en los componentes principal y secundario, si se desea.
  • En los componentes principales, mantiene los patrones de diseño de entrada existentes de Angular, de modo que ningún componente se vea obligado a hacerlo o tenga que diseñar a su alrededor.
  • El contexto principal y secundario son completamente independientes, el padre no necesita saber si el niño está usando Input o OInput , eliminando el riesgo de errores de componentes cruzados debido a este cambio.
  • Es relativamente fácil migrar parcialmente un solo componente secundario, si lo desea, porque Angular mantiene todos estos conectados a los eventos del ciclo de vida existentes como antes.
  • Dado que Angular implementa esta función, todos los EventReceiver s pueden incluir internamente una tubería a través de takeUntil(ngOnDestroyEvent) , por lo que la mayoría de los casos no necesitarán recordar cancelar la suscripción cuando se destruya su componente, porque estos observables se completarán automáticamente . (¡Yay, pérdidas de memoria reducidas!)
  • En este componente hijo, esto se ve y funciona de manera muy similar al patrón de @Output() actual, por lo que ofrece un buen paralelismo y una capacidad potencial para conectarlos bien.

    • Nota al margen: como seguimiento, creo que sería genial proponer un @InputOutput() que use un ReplaySubject<T>(1) para conectar los comportamientos para un enlace bidireccional de manera más fluida y consistente. Pero eso tiene sus propios desafíos y argumentos, así que NO lo propongo aquí.

  • No hay nuevos tipos de calderería para hacer que esto funcione, esto es tan simple como @Output() .
  • Angular perfectamente switch() sobre los cambios en Observable ingresados ​​por el padre, por lo que los patrones reactivos más débiles como el que no se quedan con la misma instancia de Observable no necesitan ser en mayúsculas especiales ( switch() ed) en componentes secundarios.

Nota al margen sobre el nombre OInput que elegí: Obviamente, eso está abierto a debate. Sin embargo, personalmente me gusta ese nombre porque es la abreviatura de "Entrada observable" Y porque se parece a "Salida", que es lo que refleja.

Estimado equipo de Angular. Danos algo que esperar en 2020 :-)

Además, la solución @jcomputer es exactamente lo que querría. No soy un gran admirador de un nombre de decorador diferente, pero tal vez sea factible agregar un parámetro similar a cómo @ViewChild tiene { read } . p.ej:

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

Demasiado para tener algo que esperar en 2020.: D A menos que, por supuesto, esto (y otros problemas enumerados anteriormente) cuenten como un error y no como una característica nueva. :)

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

@fxck Puede que tenga una opinión impopular aquí, pero sinceramente, si 2020 es el año en el que el equipo de Angular decide eliminar muchos errores en formularios, enrutadores, etc., no me enojaría en absoluto con 0 características : encogimiento de hombros :

image

Dicho esto, tal vez sea más un problema relacionado con los eventos recientes y supongo que están sucediendo cosas complicadas en este momento en el equipo de Angular. Espero que logren mantener a las excelentes personas que han estado construyendo Angular activamente hasta ahora: corazón :

Perdón por el tema fuera de lugar. Vuela lejos

@ maxime1992 También estaría bien para mí.

Puede que tenga una opinión impopular aquí, pero sinceramente, si 2020 es el año en el que el equipo de Angular decide eliminar muchos errores en formularios, enrutadores, etc., no me enojaría en absoluto con 0 características

Pero lo que me pone nervioso es el hecho de que hay procesos de RR.HH. no saludables a largo plazo dentro del equipo de Angular que nos afectan, sin nosotros. Especialmente desde el punto de vista de la inversión masiva en esta área.

Desde mi punto de vista, están solucionando problemas principalmente cerrándolos a través de BOT, dejando sin respuesta y cerrando por bot., ..: - /

@ montella1507 No, esto no es exactamente cierto. El bot bloquea solo los problemas ya cerrados. Y ya es cierto que los miembros del equipo de Angular tienen mucho esfuerzo en muchos casos cuando intentan explicar qué es necesario describir y reproducir cuando se ingresan nuevos temas. Es un proceso que requiere mucho tiempo y mucha paciencia.

Para aquellos que culpan al equipo de Angular y se quejan de que no obtienen funciones y cambios rápidamente:
Entiendo tu dolor. También me encantaría ver esta función en Angular, también cambiaría muchas cosas que personalmente no me gustan.

Pero tal vez Angular no está destinado a ser un marco que tenga todos los juguetes nuevos y brillantes y obtenga toneladas de características en cada lanzamiento. Y eso está bien para mí. Tiene que haber marcos que apunten a un nivel más empresarial y brinden estabilidad y confiabilidad. Y Angular lo hace muy bien, al menos desde mi experiencia.

Me gusta más Vue. Lo uso para mis proyectos personales. Pero mis proyectos pueden comenzar a sentirse obsoletos rápidamente y siento la necesidad de actualizar a un nuevo estilo de componente. Además, cuando hago actualizaciones, las cosas siempre se rompen de la manera más extraña. El ecosistema también parece tener más dificultades para mantener la compatibilidad.

Angular, por el contrario, es maduro y estable. No cambia con frecuencia, pero eso también significa que tiene menos trabajo para refactorizar su código a medida que mantiene sus dependencias actualizadas, y eso es valioso para los productos comerciales. Su conocimiento también permanece relevante durante más tiempo y, en lugar de intentar mantenerse al día, puede profundizar.

Sin embargo, la gente de

@insidewhy Ten paciencia, siempre hay algunos picos y valles en el camino, pero eso no significa que otros no los tengan también o que haya algunos errores principales. Solo significa que las cosas podrían avanzar más rápido y eficazmente de lo que realmente lo hicieron.

No se mencionó ningún problema en particular mencionado anteriormente en la hoja de ruta, lo que me entristeció, realmente esperaba que hubiera algo, mención, reconocimiento ... esta discusión siguió sobre la discordia angular:

image

Les di el beneficio de la duda y pregunté en el comentario de ese artículo de la hoja de ruta mediana , la respuesta de Minko:

image

Prácticamente reiteró lo que dijo aquí hace unos meses, ninguno de estos problemas relacionados con rxjs se solucionará pronto, así que sí.

Ya terminé de intentar hacer entender mi punto, pero déjame intentarlo una última vez ... No estoy pidiendo que Angular se convierta en Cycle.js , reemplazando las API actuales, todo lo que estoy pidiendo son tres pequeñas cerezas reactivas además de las API actuales.

  • ciclo de vida observable
  • entrada observable
  • eventos de plantilla observables

Ninguno de estos rompe la compatibilidad con versiones anteriores, aumenta la curva de aprendizaje, no es consistente con otras API y me imagino que apenas afecta el rendimiento , entonces, ¿cómo son tan complicados y requieren una planificación más larga y cuidadosa, en comparación con otras tareas que se avecinan de todos modos? Solo agregue uno a la vez, demuestre que le importa. Por favor.

@fxck : +1: seguro.

  • ciclo de vida observable
  • entrada observable
  • eventos de plantilla observables

@insidewhy Ten paciencia

Pedirle a la gente más de 5 años de paciencia no es tan pragmático cuando hay muchos competidores corriendo en la carrera que compiten por la atención.

@ insidewhy Se quiso decir de manera más general.

@fxck Google tiene (según mi último recuento) 4000 aplicaciones integradas en angular y de naturaleza muy similar, el cambio entre estos proyectos y la revisión por pares es (imagino) bastante fluido. Incluso en mi empresa, uno de los grandes beneficios de Angular es que sin que yo revise el código de otro equipo, sé que está en un estado en el que una revisión por pares no inicia una investigación completa.

Ahora digamos que implementamos cada uno de estos útiles atajos de RxJS en el núcleo, ¿qué sucede entonces? Es posible que terminemos con un sesgo de procedimiento versus un sesgo reactivo, sin una guía clara de qué sesgo es correcto para un caso particular. Después de leer las discusiones hasta ahora, la respuesta del equipo angular no es "Esta es una prioridad baja" (como sugiere su flujo), en realidad parece más cercana a "No queremos tomar partido".

Los usuarios avanzados de RxJS en la comunidad Angular aquí son una minoría vocal, porque después de asimilar las diversas bibliotecas de Typecript, DI, templates y Angular, tiene casi 1 año en su haber usando el marco a diario, y ahora está listo para comprender completamente RxJS. Permitir que las personas de alto nivel tengan sesgos de reactividad en las revisiones por pares aliena a las personas aún más, y eso es literalmente lo último que Angular necesita.

El objetivo de Angular (como yo lo veo) es reducir y desconectar las conversaciones de "Use X tech porque es mejor, aquí hay una breve novela por qué" y mantener conversaciones en revisiones de pares sobre la lógica empresarial que se está implementando. ¿Realmente queremos estratificar nuestros componentes entre angular "Avanzado" y angular "Principiante" más de lo que ya lo hemos hecho?

Podría decirse que estos son los mismos argumentos que hemos visto con la gestión de estado, ¿por qué debería Angular determinar a partir de los muchos patrones posibles cuál es el correcto?

Permítanme citarles a uno de los miembros del equipo angular.

image

Por cierto, no hay absolutamente nada avanzado sobre ninguno de estos (ciclo de vida observable, entrada observable, eventos de plantilla observables).

Podría decirse que estos son los mismos argumentos que hemos visto con la administración de estados, ¿por qué Angular debería determinar cuál es el correcto a partir de los muchos patrones posibles?

La diferencia entre la gestión estatal y esto, es que la gestión estatal podría ser realizada por la comunidad, no hay nada dentro del núcleo angular que lo impida. Desafortunadamente, Angular no proporciona formas de implementar correctamente ninguno de los tres que mencioné. (editar: enlace corregido para comentar)

Estoy de todo corazón por dejar a la comunidad tanto como sea posible, que incluye paquetes como formularios, enrutador, diseño flexible, universal, etc., deje que el equipo de Angular se concentre en el núcleo, la cli y los componentes. Pero para eso necesitan proporcionar a la comunidad suficientes puntos de entrada, como por ejemplo, permitir componentes de orden superior . Pero no lo hicieron durante años (a menudo dicen que primero se necesita a Ivy) y se niegan a reconocer / hablar de esto en sus planes ahora.

Después de leer las discusiones hasta ahora, la respuesta del equipo angular no es "Esta es una prioridad baja" (como sugiere su flujo), en realidad parece más cercana a "No queremos tomar partido".

Sí y da miedo.

@fxck Creo mucho que estamos en la misma página sobre esto. Yo diría que cada vez que menciono RxJS en foros o incluso con mis compañeros ingenieros, la mayoría de las citas que puedo citar son "Todavía no entiendo esto, ¿cómo puedo aprender más?" o "Me parece abrumador la cantidad de operadores que hay". o "Tengo problemas para leer esta canalización en este PR". Por cada 1 ingeniero RxJS bien versado, parece haber 15 ingenieros angulares bien versados ​​con el ceño fruncido.

Sí y da miedo.

Esto es un poco hiperbólico, no "necesitamos" @angular/core para tener estos patrones, de hecho, hay MUCHAS variantes y bibliotecas dentro de esta edición que se pueden usar hoy en día en cualquier proyecto. La única reticencia al usar las opciones mencionadas anteriormente es que posiblemente, en el futuro, tenga que migrar a algo que Angular proporciona como reemplazo estándar de @angular/core . ¿Es realmente tan difícil para nosotros npm uninstall y corregir todos los errores mecanografiados?

Decisiones arquitectónicas importantes, que afectan a todos los proyectos angulares, y que requieren innumerables horas para documentar y migrar si no funcionan, para mí eso parece aún más aterrador, y ya hemos estado allí antes:

Esto es un poco hiperbólico, no "necesitamos" @ angular / core para tener estos patrones, de hecho, hay MUCHAS variantes y bibliotecas dentro de este tema que se pueden usar hoy en cualquier proyecto.

No, en realidad no, cada solución requiere hacks o depende de API internas que podrían cambiar. Ese es el punto ... lea lo que @insidewhy dijo https://github.com/angular/angular/issues/5689#issuecomment -630661006 y lo que dije a continuación sobre los flujos de eventos de plantilla.

// editar enlace corregido para comentar

No, en realidad no, cada solución requiere hacks o depende de API internas que podrían cambiar.

¿Qué API o tipos internos cree que deberían hacerse públicos / genéricos para permitir que la comunidad lo aborde? Si este diseño está en un patrón de espera porque no hay un camino claro hacia adelante, entonces parece apropiado pedir que se nos permita diseñar el nuestro en lugar de golpear constantemente este mismo tambor.

Lea el otro comentario de @inside por

image

@fxck No voy a discutir la historia de los intentos de implementar esto en el núcleo o cómo este problema ha estado abierto durante 5 años, ¿qué nos impide ahora abrir los componentes internos para permitir que la comunidad desarrolle un diseño por sí mismos?

Yo @insidewhy , me pregunto si este es el truco de verificación de tipos en el que se basó su biblioteca. Aparentemente ya no funciona con TS 4.0

La propiedad 'serviceStackId $' se usa antes de su inicialización.ts (2729)

image

Yo @insidewhy , me pregunto si este es el truco de verificación de tipos en el que se basó su biblioteca. Aparentemente ya no funciona con TS 4.0

No, estaba en el compilador de plantillas. Esto es otra cosa. Maldita sea. Ah bueno. Puedo agregarte a la lista de mantenedores si quieres intentar arreglarlo.

@fxck En realidad, debería asignar ese operador de canalización en ngOnInit ya que las propiedades basadas en el decorador no se inyectarán hasta después de que se ejecute el constructor, por lo que este es en realidad un problema legítimo con su código.

@insidewhy definitivamente esto

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();

registra

registrando this.baz ObservableInput valor modificado

así que creo que aún debería funcionar, solo en TS4 el compilador se queja porque no se da cuenta de que hay un get con un valor dentro de ese decorador ...

Estoy usando un decorador para detectar el cambio de la propiedad de entrada y hacer una versión observable de la propiedad de entrada. Vea esta demostración de codesandbox .

Así es como funciona:

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

Como señaló @wmaurer , @Subjectize podría tratarse como un "azúcar" para hacer las cosas.

Vale la pena leer la guía oficial de angular Intercept input property changes con un setter , que explica que podemos sentir los cambios de entrada usando getter/setter .

@hankchiutw Esa solución se parece a mi @BindObservable decorador: https://github.com/PSanetra/bind-observable#usage

Gracias @hankchiutw

Como está inicializando ReplaySubject con 1, elegí usar BehaviorSubject en su lugar.
También he inicializado el valor directamente en 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>;

    // ...
}

Desearía poder evitar escribir el tipo en la propiedad "Subjectized", ya que debería poder adivinarlo a partir de la propiedad relacionada :( ¿Alguna idea de cómo hacerlo?

El sueño sería poder hacer eso y crear automáticamente el objeto relacionado Asunto (mundo $ en mi caso) como BehaviorSubjecty correctamente inicializado.

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

    // ...
}

@mparpaillon Su diseño propuesto es más o menos como se hace en https://github.com/PSanetra/bind-observable
La única diferencia es que el decorador @BindObservable() usa un ReplaySubject debajo del capó. Entonces, si desea que el sujeto contenga un valor inicial, también debe inicializar la propiedad enlazada explícitamente (también si no está definida, ya que podría ser posible que el tipo de propiedad no permita valores nulos o indefinidos).

Ejemplo de Stackblitz

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

Gracias @PSanetra pero prefiero mi solución (que en realidad es la solución de Hank)

@mparpaillon He probado algo para tu sueño (ja) en otra demostración .

El uso parece

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

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

donde puede elegir especificar el nombre de objeto subjetivo.

Aunque es técnicamente factible, me temo que puede ser ambiguo para los desarrolladores (ya que se crea un nuevo miembro de clase debajo del capó).

¡Gracias @hankchiutw ! Sin embargo, Typescript no parece dejarme usar count $ o myCount $.

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

También puede que tenga razón sobre la ambigüedad ... Gracias de nuevo

Sin embargo, Typescript no parece dejarme usar count $ o myCount $.

Declaraste los miembros de tu clase como "count" y "otroCount", por eso no puedes acceder a "myCount $" y "count $". Simplemente no existen, porque no los declaraste en ninguna parte.

Lo sé ... ese es el punto. Estaba buscando una forma de declararlos desde el Decorador. @hankchiutw ofreció una solución y solo digo que no funciona como está

@mparpaillon echa un vistazo a mi solución: https://github.com/Futhark/ngx-observable-input

@Futhark ¡Oh, eso está caliente! gracias

¿Fue útil esta página
0 / 5 - 0 calificaciones