Teddy
Teddy

Reputation: 2347

Convert Array to Observable, perform transformations and return again as Array?

In ngOnInit() I have "return x" which I want to put into Observable, then perform transformations and return again in the same format.

Here is the working plunker: http://plnkr.co/edit/z26799bSy17mAL4P5MiD?p=preview

import {Component} from '@angular/core'
import { Observable } from 'rxjs'
import * as Rx from 'rxjs/Rx'

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <h2>{{name}}</h2>

      <button (click)="addToArray()">Add</button>
      <ul>
        <li *ngFor="let item of data$ | async">{{ item }}</li>
      </ul>

    </div>
  `,
  directives: []
})
export class App {

  data = ["one","two","three"]
  data$: Observable<Array<string>>;

  constructor() {
    this.name = 'Angular2 array to observable example'
  }

  ngOnInit() {
    this.data$ = Rx.Observable.of(this.data)
      .map(data => {
        let x = data
        x.push("4")

        ///
        ///  TRANSFORM X IN THIS SECTION OF THE CODE
        ///  HERE BY PUTTING IT INTO OBSERVABLE
        ///  PERFORMING TRANSFORMATIONS AND
        ///  RETURNING THE DATA TO BE RENDERED IN TEMPLATE
        ///

        return x
      })
  }

  addToArray() {
    this.data.push('more numbers')
  }      
}

Upvotes: 1

Views: 6540

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

There is an adjusted and wirking plunker

I would implement that with an EventEmitter and few operators, mostly

adjusted code

  data = ["one","two","three"]
  data$: Observable<string[]>;
  protected emitter = new EventEmitter<string[]>(); 

  constructor() {
    this.name = 'Angular2 array to observable example'
    this.data$ = this.emitter
      .startWith(this.data)
      .scan((orig, item) => orig.concat(item))
  }

  ngOnInit() {
    // this.data$ = Rx.Observable.of(this.data)
    //  .map(data => {
    //    let x = data
    //    x.push("4")
    //    return x
    //  })
  }

  addToArray() {
    //this.data.push('more numbers')
    this.emitter.emit("forth")
  }

Check it here

EXTEND

Much more complex plunker

There is much more complex solution.. just profiting from Observable and its Operators. It is ready to add and delete items:

  data = ["one","two","three"]
  data$: Observable<string[]>;
  protected emitter = new EventEmitter<string[]>(); 
  protected toDelete = new Rx.BehaviorSubject<string[]>([])
    .scan((orig, item) => orig.concat(item));

  constructor() {
    this.name = 'Angular2 array to observable example'
    this.data$ = this.emitter
      // start
      .startWith(this.data)
      // return array
      .scan((orig, item) => orig.concat(item))
      // adjust each source string with a prefix
      .map((coll: string[]) => {
        let adjusted: string[] = []
        coll.forEach(item => {
          adjusted.push("x" + item)
        })
        return adjusted;
      })
      // now consume also array of items to be deleted
      .combineLatest(this.toDelete)
      // just those which were not delted
      .map(([all, toDelete]:[string[], string[]]) =>{
        let result = all.filter( function( el ) {
          return toDelete.indexOf( el ) < 0;
        });
        return result;
      })
  }

  counter: int = 0;
  addToArray() {
    this.emitter.emit(`other${++this.counter}`)
  }

  deleteFromArray(removeString) {
    this.toDelete.next(removeString)
  }

Check it in action here

Let's do another EXTEND

There is a final plunker with lot of data: string\[\] array handling

We can now even track the changes and let them adjust original data array, and even use the RESET function, to start from new begining. This is the adjusted code:

  data = ["one","two","three"]
  data$: Observable<string[]>;
  protected emitter: EventEmitter<string[]>;
  protected toDelete: Rx.BehaviorSubject<string[]>;

  constructor() { 
    this.initEmitters();  
    this.data$ = this.createObservable(this.data);
  }

  initEmitters() {
    this.emitter = new EventEmitter<string[]>(); 
    this.toDelete = new Rx.BehaviorSubject<string[]>([])
      .scan((orig, item) => orig.concat(item));
  }

  createObservable(initData)
  {
    let observable = this.emitter
      // start
      .startWith(initData)
      // return array
      .scan((orig, item) => orig.concat(item))
      // adjust each source string with a prefix
      .map((coll: string[]) => {
        let adjusted: string[] = []
        coll.forEach(item => {
          adjusted.push("x" + item)
        })
        return adjusted;
      })
      // now consume also array of items to be deleted
      .combineLatest(this.toDelete)
      // just those which were not delted
      .map(([all, toDelete]:[string[], string[]]) =>{
        let result = all.filter( function( el ) {
          return toDelete.indexOf( el ) < 0;
        });
        return result;
      })

      observable
        .subscribe((currentData) => {
          this.data.length = 0;
          [].push.apply(this.data, currentData)
        });

      return observable;
  }

  counter: int = 0;
  addToArray() {
    this.emitter.emit(`other${++this.counter}`)
  }

  deleteFromArray(removeString) {
    this.toDelete.next(removeString)
  }

  resetArray() {
    this.initEmitters();  
    this.data$ = this.createObservable(['ten','eleven'])
  }

Test that array vs obesrvable in action here

Upvotes: 1

Related Questions