How to Manipulate Data in Ionic 2: Part 1



·

Most applications rely heavily on data, so it’s important that we know how best to manipulate that data in order to make it do what we want. Sometimes this data is stored on some remote server, sometimes it’s stored locally, but in both cases, we quite often make use of Angular 2’s Http service to retrieve that data.

In this tutorial, we are going to discuss how exactly the http.get() method works, and how we can hook into that process to return data in a more friendly format. We will be discussing some specifics of observables, and also introduce the concept of mapping.

We will expand on these concepts in a more general sense in Part 2 of this tutorial series, and we will also introduce two more ways to manipulate data in your applications: filtering and reducing.

An In-Depth Look at the Http GET method

It’s pretty easy to use Http in Angular 2 to fetch data, but understanding how it works is another matter. You may be familiar with using the following syntax to make a GET request:

this.http.get('location/of/data').map(res => res.json()).subscribe(data => {
	console.log(data);
});

This will make a request to the location we provide – which could be a web URL or a path to a local JSON file – and return the data we want. If you want to grab some data from somewhere else, you just need to change location/of/data and forget about the rest of the magic. This will work if your data requirements are simple, but if you want to start manipulating your data in more complex ways, it is important to understand what is happening here and what tools are available for us to use.

The first strange thing about the above request is the use of subscribe instead of then. Usually, you would expect the code to look more like this:

this.http.get('location/of/data').then((data) => {
    console.log(data);
});

However, the code above will not work. A then handler is only used on a Promise, but the get method above will return an Observable. I’m not going to go in-depth into the difference between promises and observables here, so if you would like more information on that I would recommend watching RxJS Observables vs Promises. The main difference is that an Observable can emit multiple values over time, whereas a Promise will only emit a value once.

So since we know get will return an observable, and we know we need to subscribe to an observable, it makes sense to set up our code like this:

this.http.get('location/of/data').subscribe(data => {
    console.log(data);
});

This is still missing the map section from our original code though, so what does map do? If we were to just use the code above it will still work, but we will get a Response object back from the call, most of which is not of much use to us:

Http Response

As you can see above, the JSON response is inside of the _body property which we could access if we wanted, but it is currently just a JSON string (it is not yet a Javascript object that we can easily work with). There is also a lot of other information contained in this object like the headers, HTTP response code, the URL the request was made to, and so on. We do not need any of this, we just want the data. So we use map to transform this response into something we can more easily use.

An Introduction to Mapping Observables and Arrays

In Javascript, mapping is a method available on arrays which allows you to “map” or “transform” each value in that array. Keep in mind that in the example above we are not mapping an array, we are mapping an observable – this functionality is provided by the RxJS library (which is included in Ionic 2 & Angular 2), it is not in Javascript by default.

To give you an example of how normal array mapping works (the concept is basically the same for observables), I could take an array of the following numbers:

[1, 2, 3, 4, 5]

and map the array using a function that increments each value by one, which would transform the array into the following:

[2, 3, 4, 5, 6]

This would look something like this:

[0, 1, 2, 3, 4, 5].map(function(item){
    return item+1;
});

We supply map with a function, and then the map will run that function for every value in the array. Whatever we return from that function is what the new value of the item will be – in this case, that is the same number incremented by one. If we instead did something like return 5;, it would turn every value in the array into a 5.

We are going to learn more about this in the next tutorial, but we are using a similar concept with our observable from Http as well. We use map to transform the response into something we can work with. To do that, we create a function that will be applied to each item returned from our observable (which is just the single Response object in this case). The function looks like this:

res => res.json()

which is shorthand for:

(res) => {
    return res.json();
}

This looks a little bit different to the function we created before, but that’s mainly because of the ES6 syntax we are using that is available to us in an Ionic 2 project. You could also write the above as:

function(res){
    return res.json();
}

Since our observable only returns one item, that item will be passed into this function. We can then do whatever we want to that item to modify it, and then return it. In this case we call the json() method on the item, which is a function available on the Response object returned that will parse the JSON data that was retrieved into a nice Javascript object that we can use.

The map function will return a new observable once it has completed, so we can still subscribe to the result of the map function. Combining everything we have talked about above will result in our original code:

this.http.get('location/of/data').map(res => res.json()).subscribe(data => {
    console.log(data);
});

which will return us a nice Javascript Object:

Javascript Object

If we weren’t using map, the following code would also achieve the same result:

this.http.get('location/of/data').subscribe(data => {
    console.log(data.json());
});

However, to give an example of why it’s useful to understand how mapping observables works, let’s say that the resulting object contains multiple properties (e.g. categories, users, and posts), and let’s say we only want the categories data. Now that we understand how map works, we can extend it a little further:

this.http.get('location/of/data').map(res => res.json().categories).subscribe(data => {
    console.log(data);
});

Now after the response has been converted into a Javascript object, we then grab the categories property specifically before returning it. Now the result will only contain categories and not the rest of the object. Not only is this code nice and clean, but since we handle this operation in the map, which returns an observable, we can then chain on other operators to modify the observable (like filter and reduce) before subscribing to the result if we want.

Again, the important thing to remember about the use of map here is that we are mapping an Observable, not an array. If you had an observable that returned an array of data, you might expect that using map on the observable would be able to map each value in that array of data to another value.

That is not the case with an observable though, the array that is emitted from the observable is one single item (each time the observable emits some data, that is one “item”), and so the map function only applies to the array as a whole. The idea is that if your observable then emits another array later, the same map function will apply to that array and so on for every time that the observable emits some data.

If you want to map an array of data that is returned from an observable, you should instead use the normal map function that is available directly on the array itself (not the observable) like this:

.map(res => {
    return res.json().map((item) => {
        return /* do something to the item */;
    })
})

This may look a little confusing right now, but we will be covering this in a lot more detail in the next tutorial. Remember that map is a method that is available on arrays anywhere – it doesn’t need to be inside of an observables map function, and it doesn’t even need to be inside of an Ionic or Angular project. In fact, you can paste the following directly into your browsers Console, hit enter, and it will work:

[0, 1, 2, 3, 4, 5].map(function(item){
    return item+1;
});

Mapping – as well as filtering and reducing – is generally more useful to us when we are working with arrays (as the code above does). Performing these operations on observables can be useful in more complex scenarios, but in most circumstances, you won’t use map on an observable for much more than converting the JSON string in the Response object to a nice Javascript object.

. As an aside, filtering and reducing are also available on both observables and arrays, and like map they also return a new observable, so you can create long complex chains like this:

this.http.get('location/of/data')
    .map(res => res.json())
    .filter(/*some function*/)
    .reduce(/*some function*/)
    .subscribe(data => {
    console.log(data);
});

but again, generally, it is more useful to use the filter and reduce operations directly on an array, rather than on the observable like this.

Summary

Hopefully this tutorial clears up exactly what is happening when using the get method of the Http service, and also how you can modify that process to suit your own needs.

In the next tutorial, we will continue to talk more about what we can do with mapping in a more general sense, as well as introducing filtering and reducing.

What to watch next...

  • Jean-Mouloud

    I didn’t see anything specific to Ionic…

    • That’s correct – advice in the tutorial can be applied generally to Angular 2, and the next tutorial is going to focus on mapping, filtering, and reducing arrays which is just plain Javascript.

  • Pingback: How to Manipulate Data in Ionic 2: Part 2 | joshmorony - Build Mobile Apps with HTML5()

  • Runfast Webmaster

    Hi,
    Maybe someone can help me with this.
    I’m using ionic2 Http.get method to send data to my php server and create a customer into Stripe (payment service).
    The data are sent correctly and the customer is created into stripe. When I show the activity in the console, I see stripe is returning many information in the variable $result.
    My problem is this one :
    echo $result don’t send the ‘result’ to my TS file.
    Here is the http method I’m using for my http request :

    CardIO.scan(options).then((result) => {

    if (result.cardType == ”) {//if card type is not visa or mastercard or american express we decline the card

    alert(‘use another card’)

    }

    else {//card appears as valid so we try to get a token from strip server

    (window).Stripe.card.createToken({

    number: result.cardNumber,

    exp_month: result.expiryMonth,

    exp_year: result.expiryYear,

    cvc: result.cvv

    }, (status: number, response: any) => {

    if (status === 200) {

    alert(‘Success! Card token’+ response.card.id);

    var headers = new Headers();

    headers.append(‘Content-Type’, ‘application/x-www-form-urlencoded; charset=UTF-8’);

    this.http.get(‘http://localhost/stripe/save.php?token=’+response.id+’&[email protected]’, {headers: headers})

    .map(res => res.json()).subscribe(data => {

    console.log(data);

    });

    } else {

    alert(response.error.message);

    }

    });

    Could you help me with this ?

  • Thanks Josh. I have a question. When you say “The main difference is that an Observable can emit multiple values over time” – does that mean that Observables can be used for Streaming data?

    • Yep, that’s exactly what Observables are for (creating data streams)

      • Ha, so I should be able to send data to the app from a php server using sockets (fopen()) instead of echoing it?

  • Nice tutorial, after reading this article I went out to create an app but am stack, where I I can pass data from a selected list item. I get a 404 error data not found. I have explained on stackoverflow https://goo.gl/ga9P1A.

  • Amanda

    Excellent. Thank you!!!

  • Ghulfam Hassan

    Hi i ‘m creating an app using ionic 3 and SQLite … geting follow error can anyone please suggest me solution…

    _zone_symbol__currentTask
    type: microTask
    state notScheduled
    source promise.then
    zone:angular
    cancelFun:null
    runCOunt:0

    Thanks

  • show new

    is that forking with ionic I’ve try your method in my project with ionic 3 but i see something missing and different than you
    import { RedditDataProvider } from ‘../../providers/reddit-data/reddit-data’;
    i have RedditDaraPrivder

  • rasika

    hi josh i followed ur tutorial bt its giving me error of “JSON.parse: unexpected non-whitespace character after JSON data at line 2 column 1 of the JSON data”.what excalty the error is when i remove map(res => res.json()) then i m getting the {“_id”:{“$oid”:”593e2df37fa7638406d78fdd”},”cityId”:”2092″,”cityName”:”Ahmedabad”,”createdAt”:{“$date”:”2017-06-12T06:00:19.854Z”},”__v”:0}
    {“_id”:{“$oid”:”593e2df37fa7638406d78fdf”},”cityId”:”6153″,”cityName”:”Bengaluru”,”createdAt”:{“$date”:”2017-06-12T06:00:19.854Z”},”__v”:0}