1

I'm trying to get my head around observables in Angular 6, but I'm new to anything more recent than AngularJS and seriously struggling right now. I think I've gone through every tutorial I can find and searched extensively here, so hopefully I'm not missing anything, but happy to take friendly pointers...

What I'm trying to do is this:

  1. GET request to a backend API, returns a JSON array of items, and display that array in a component as a data list
  2. Allow a second component to POST new items to the backend, and also add that item to the list of items above

I'm guessing I'm describing an observable, because #1 needs to be asynchronous, but all the examples I've seen seem to only deal with one of the two steps, not both together. What am I missing?

1
  • It's not really all that different from the Angular JS HTTP library except instead of returning a promise, the Angular HTTP library returns an Observable. At a very basic level, where you would call .then on the promise, you would call .subscribe on the Observable Commented Jul 6, 2018 at 22:18

3 Answers 3

2

@1: GET request to a backend API, returns a JSON array of items, and display that array in a component as a data list

let's call this one ComponentA:

export class ComponentA {        
    arrayOfItems:Type[];
    constructor(private http:HttpClient) {}

    getDataFromAPI():Observable<Type[]> {
        this.http.get<Type[]>('api-get-url')
            .subscribe(
                (items) => {
                    //print them if you want;console.log('response from the api', items);
                    this.arrayOfItems = items;
                }
            )
    }
}

@2.1: Allow a second component to POST new items to the backend (omitted a part of the question on purpose)

export class ComponentB {  
    // let's assume this array is already populated
    // via you 2-way bindings or whatever means 
    newItems:Type[];
    constructor(private http:HttpClient) {}

    postDataToAPI():Observable<any> {
        this.http.post('api-post-url', this.newItem)
            .subscribe(
                response => console.log('response from the api', response);
            )
    }
}

@2.2 and also add that item to the list of items above

Everything ^ was straightforward, but now you gotta stop and think: I have something 'here' and something 'there'. How can I ... connect them? Ha! Connect! So I need some sort of mechanism to connect the 2 components. What do you do? You use a service! But a service whose purpose is to store a shared data set. You can call it DataStoreService.

Let's (re)write some code:

@Injectable()
export class DataStoreService() {
    items:Type[] = [];
    constructor(private http:HttpClient) {}

    getDataFromTheAPI():Observable<Type[]> {
            this.http.get<Type[]>('api-get-url')
                .subscribe(items => this.items = items)
    }

    postDataToTheAPI(itemToAdd:Type):Observable<any> {
            this.http.post('api-post-url', itemsToAdd)
                .subscribe(
                    (response) => {
                        // check if all is good, and add it to the 'items' array
                        addItem(itemToAdd);
                    }
            }
        )
    }

    getItems() {
        return this.items;
    }

    addItem(item:Type) {
        this.items.push(item);
    }
}

Now, your Component A changes to:

export class ComponentA implements OnInit{
    constructor(private dataStoreService: DataStoreService) {}

    ngOnInit(): void {
        this.dataStoreService.getDataFromTheAPI(); 
        // ^ will request the data from the API and store it 
        // inside the service
    }

    // and here you get a reference to that data
    get itemsReferenceFromService() {
        return this.jobsStoreService.getJobsArray();
    }
}

And ComponentB to:

export class ComponentB {
    newItem:Type;
    constructor(private dataStoreService: DataStoreService) {}

    // when you do this, if successful, addItem method
    // will also be called that will 'and also add that item to the list of items above'; as componentA only has a reference to the data in 
    // the service, the new item will be displayed it it
    this.dataStoreService.postDataToTheAPI(newItem);
}

Hope this answers your question. If you have any other doubts, say it loud. Info like urls were omitted.

A further improvement is to have another service that only handles the API calls, and keep the DataService clean just for storing purposes. That will ease testing.

Be aware that the lifespan of any Service/Component in angular is up untill the end user refreshed the page. The service described is not a persistence mechanism.

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

5 Comments

oh, and you can use 'itemsReferenceFromService' in your ComponentA to display the data. without the (), just bind it to 'itemsReferenceFromService'
@Rollo, if the answer answers your question, make sure to mark it as accepted by clicking on the Tick icon at its top left
This answer was perfect and gave me exactly what I was looking for. Thanks for deciphering what I was asking so expertly and so quickly! Turns out I do need to read up on 2-way data binding, but that should be less of a headache...
Great! Always happy to help a fellow Angularian!
What should getDataFromTheAPI() and postDataToTheAPI() be returning? I initially got an error because they weren't returning anything, so I tried changing to ` getDataFromTheAPI():Observable<Type[]> { return this.http.get<Type[]>('api-get-url') .subscribe(items => this.items = items) } ` and ` getDataFromTheAPI():Observable<Type[]> { this.http.get<Type[]>('api-get-url') .subscribe(items => this.items = items) return this.items; } ` but neither seemed to work? Sorry for the formatting...
0

That because it is two different things.

The first is an request that you are expecting data from. so you subscribe to it.

$newsletter = NewYorkTimesService.getData(); // this does some http stuff.
$newsletter.subscribe(newspaper => readData(newpaper) /*or whatever you want to do with json */ );

The second is a total different call (and different observable) to send data, where you are only really subscribing to know if it was successful.

$paymentResults = NewYorkTimesService.makePayment(paymentJSON); //this does http stuff too.
$paymentResults.subscribe(() => /*I guess it was successful */ , (error)=> /*it failed */);

The only thing they share is that they might be of the same called from the same service.

Comments

0

Here is a basic example of doing it in Angular 5. I think the only thing that may be different between 5 and 6 is where the libraries you are importing come from.

@Injectable()
export class ItemsService {

  constructor(private http: HttpClient) { }

  private backendURL= "http://myurl.endpoint.com/";

  getItems(): Observable<IItem[]> {
    return this.http.get<IItem[]>(backendURL);
  }
  saveItems(items: IItem[]): Observable<IItem[]> {
    let options = { headers: new HttpHeaders({'Content-Type': 'application/json'})};
    return this.http.post(backendUrl, items, options);
  }
}

In your Component class you import the component and subscribe to it.

export class ItemComponent {

    itemList: Item[];

    constructor(private itemService: ItemService) { }

    ngOnInit() {

    this.itemService.getItems().subscribe(data => this.itemList = data);

    }  
}

Here is a pretty good tutorial that goes over the basics https://angular.io/tutorial

2 Comments

getItems in the service is missing a closing brace, and in your subscribe, you need this.itemList instead of itemList
@user184994 Thanks for pointing it out I fixed it. trying to juggle between C#, Java and Typescript. hah

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.