1

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')
  }      
}
4
  • 1
    What is your question? Commented Jun 24, 2016 at 5:42
  • You can look at the code, there is commented out part. Variable x should be made an observable, then .map or something alike and then returned as x array, which will propagate to the template with the return statement. Commented Jun 24, 2016 at 5:58
  • 1
    But what do you want to achieve with it? Why do you need to convert Array to Observable<Array> just to convert it back to Array? Commented Jun 24, 2016 at 6:08
  • Because I want to put that Array thru Observable machinery (rxjs) to transform the data in Array with that powerful toolset, and then when I am done, just to return it to Array which will feed the template via Observable and | async. Just before "return x" seems perfect place for that. Commented Jun 24, 2016 at 6:52

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

8 Comments

Thank you Radim. Point here is to change "this.data" and when changed, the observable catches it and runs thru its machinery of operators and then returns array which I can iterate over in template. When I use ".of" operator it works just like that, BUT I cant use powerful rxjs toolbox for data transformation. I tried to put it in map(x => x + " some text" ) but it holds entire Array, not the separate values, so that is impossible. If I use .from I dont get that update when I manipulate this.data. Point here is to have state in this.data, which runs thru Obs. on update and returns parsed data.
Teddy, give a chance to my solution. Think about it. If you want to observe array (the string[] array as is) you have to create yourself lot of stuff. Events, handlers... brand new RX JS observable. My solution is a gen++. It already profits from moving that case into Observable... and then just profiting from buil-in solution. We just consume existing array with startWith. and later, any event is converted to emit with a new item. Our scan is ready for it and returning adjusted array. And we can even add more operators... please, give a chance to my way.. and it will become your way ;) ;)
Your solution is really clever, but it does not solve my problem. I looked at the code once more. What I need is: 1. change "this.data" array, 2. use ".of" to make it synchronized Observable which is subscribed to via | async in template. 3. Put somewhere between the input "this.data" and "this.data$" ability to use rxjs transforming. This is like super crazy immutable self-updating app. I just need then to update "this.data" with right data and let RxJS do its magic and I am done. Problem with yours is that dont just add items, but also need to delete, splice and other stuff from array.
I extended my answer, created brand new, more complex plunker.. and now you have solution which is really doing a lot.. while amount of code is almost zero ;) just.. all the stuff is about observable, not about the string[]... hope that helps a bit
plnkr.co/edit/ExqxywBeud2gkHr41pCm?p=preview I added a function that resets the "this.data" array, which should propagate to the view. I need this function to work. I really like what you are using here, really fency high-level stuff, but I need to change this.data directly. If noone posts better answer, I will definitly accept yours.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.