Save data from a .ts file (in app)

My app stores data in app. A function allows to change boolean values in a .ts file. Is there a way to save those changes so the values are the same if the user close the app and re open it ? I mean, can I save the modified .ts file ?

The files look like this :

cards.ts

export default
  {
    "cards": [
      {
        "id": "1",
        "question": "blabla",
        "answer": "blablabla",
        "hint": "blablablabla",
        "displayed": false,
...
      },
    ]
  };

cards.interface.ts

export interface Card {
  id: string;
  question: string;
  answer: string;
  hint: string;
  displayed: boolean;
}

You have several options if your PWA needs to manage state information, for example:

Sample code:

Thanks for your help. Actually, I tried to use Ionic Storage following the official documentation. It is set properly in app.modules.ts and in my component but i have this error : “Runtime error : storage is not define”. In console : “Property “storage” is declared but never used”. Any idea ?

Use this.storage and not storage in the component

Thanks, that was the source of the problem.
Actually I dont really know how I can use Storage to save the boolean value of a specific card.
Here is the function that change the boolean value :

getRandom() {
    this.availableCards = this.cards.filter(card => !card.displayed);
    let rd = Math.floor(Math.random() * this.availableCards.length);
    this.randomCard = this.availableCards[rd];
    this.cards.find(card => card.id === this.randomCard.id).displayed = true;
  }

The app is suposed to display a random card. But first, it filters cards that have already been displayed. That’s why I need the boolean value to be saved if the app is killed.

As explained in this other thread, all the randomness should be done only once, and then all you need to save in storage is the shuffled deck (which never changes) and a single integer representing the current position.

Thanks for your reply. You are right, your solution is a smarter way to do what I am looking for. I didn’t try to implement a shuffleDeck() method because the current version “works”. I am beginner working on this app as a hobbie and did not want to rebuild it. I should probably follow your instruction if I don’t find an easy way for this version.

There isn’t really an easy way to do this, given the current design. Think of Storage as a service that allows you to store arbitrary containers of junk, and it charges you per container access.

So you have a bookshelf in your house, containing about 50 books. You can tell the Storage service to store each book in a separate container, and that way it charges you every time you need to access a book. Or you can tell it to take the entire collection at once, which makes more sense if you don’t ever need individual books. That’s what you’ve implicitly done if you’re storing that entire cards array.

If the cards array has been shuffled, that is a great design decision, because (a) you only do ever want the whole deck and (b) it can be read-only after the shuffle has been done. If, however, you are needing to modify bits of each card (the displayed value), that becomes a really bad way to store things because each time you need to modify a single boolean flag, what has to happen is:

  • retrieve the entire deck from storage
  • unmarshal it from JSON
  • make the modification
  • remarshal the whole deck
  • store the whole deck back in storage

So you have, as I see it, three options:

  • Always re-store the entire cards array into storage every time you make any modification to it at all, such as flipping a displayed flag of any card in the deck. I guess easiest to implement, but a really bad design IMHO
  • Keep your displayed flag, but instead of storing the whole deck as a unit, split it up and store each card individually. Makes startup much less efficient, but modification more so.
  • Shuffle the deck once at startup and save only the whole deck and a single position integer. Obviously, my preferred solution.

Hi rapropos, thanks for this explanation and sorry I did not answer earlier.

I made changes in the app, following your advices from this thread.
But regarding Storage, I do not understand how to use it to store the variable “i” and the cards that have been shuffled. How can I do it based on the file below ? Have you a few example I can follow to achieve that ? thanks

home.ts

import { Component, } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Storage } from '@ionic/storage';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  cards:any = [];
  i: number;

  constructor(public navCtrl: NavController, private storage: Storage,) {
    this.i = 0;
  }

  ngOnInit() {
    this.cards = [
        {id: '1', question: 'lkndlkc', answer: 'kdckdlqkn.', hint: 'oidncoisdnc.'},
        {id: '2', question: 'bla', answer: 'xnqspin', hint: 'xnsin'}, 
        {id: '3', question: 'csnd', answer: 'xnqcksjd cjspin', hint: 'xnsij dljn'}, 
        {id: '4', question: 'bqlksncla', answer: 'xnqscljqs cpin', hint: 'xnscqns ljin'}, 
        {id: '5', question: 'blalqjsx', answer: 'xnlqjsnxqspin', hint: 'xnsqlksnxin'}, 
        {id: '6', question: 'blq lksnxqsna', answer: 'knk', hint: 'xklnknsin'}, 
    ];
    console.log(this.cards);
 
}
  setI() {
    this.i++;
    console.log(this.i)
  }

 shuffleCards(cards) {
    var currentIndex = cards.length, temporaryValue, randomIndex;
    while (0 !== currentIndex) {
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;

      temporaryValue = cards[currentIndex];
      cards[currentIndex] = cards[randomIndex];
      cards[randomIndex] = temporaryValue;
    };
    console.log(this.cards);
    console.log(this.cards[1]);
  }
}

Storage will automatically serialize and deserialize objects for you, so:

this.storage.set("shuffled", this.cards);
...
this.storage.get("shuffled").then(cards => this.cards = cards);

I would do the read at app startup, and if you don’t find anything, then that would be the cue to make up (and store) a newly shuffled deck. The current position can be stored similarly and updated whenever desired.

One catch: it’s prone to race conditions to use Storage for in-app communication (as opposed to “across app runs”), so if you need to be passing these objects around to various pages, I would centralize their in-app representation in a service provider, and have only that service provider be responsible for interacting with Storage (not page classes).

Thanks for the advice. This is a simple one page app.
I tried to use storage but data is not saved when I kill the app and restart it.

What I am doing wrong here ?

ngOnInit() {
    if (this.cards.length == 0) {
    this.cards = [
        {id: '1', question: 'lkndlkc', answer: 'kdckdlqkn.', hint: 'Les chercheurs voulaient simplement vérifier sil restait du café dans la pièce dà côté.'},
        {id: '2', question: 'bla', answer: 'xnqspin', hint: 'xnsin'},
        {id: '3', question: 'csnd', answer: 'xnqcksjd cjspin', hint: 'xnsij dljn'}, 
        {id: '4', question: 'bqlksncla', answer: 'xnqscljqs cpin', hint: 'xnscqns ljin'}, 
        {id: '5', question: 'blalqjsx', answer: 'xnlqjsnxqspin', hint: 'xnsqlksnxin'}, 
        {id: '6', question: 'blq lksnxqsna', answer: 'knk', hint: 'xklnknsin'}, 
    ];
    console.log(this.cards);
    this.storage.set("shuffled", this.cards);
    alert("new cards loaded");
    }
  else {
    this.storage.get("shuffled").then(cards => this.cards = cards);
    console.log(this.cards);
    alert("nice to see you again");
  }
}

Putting the console.log statement outside the then block. If you’ve a UNIX background, think of Promises like a fork() call, and your log is in the parent, instead of the child where you want it:

this.storage.get("shuffled").then(cards => {
  this.cards = cards;
  console.log("only in here can we see " + this.cards);
});

Thanks a lot, Storage is finally working. Data won’t load when app starts on device but this is another issue.