Jump to content
IElite

LocalStorage

Recommended Posts

 

where is the most appropriate place to do the load and save from local storage in a project. i.e. from what event do you load from local storage and save to local storage? The Form's onactivate and ondeactivate do not seem to work

uses 
... SmartCL.Storage;

var
 JSONStr: String;
 LocalStorage: TW3LocalStorage;

procedure TForm1.Form1Deactivate(Sender: TObject);
begin
 LocalStorage.setKeyStr('Data', JSONStr);
end;

procedure TForm1.Form1Activate(Sender: TObject);
begin
  LocalStorage:= TW3LocalStorage.Create;
  try
   LocalStorage.Open('DEMO1');
  finally
   JSONStr:=  LocalStorage.GetKeyStr('Data', '');
  end;
end;
thanx
 

Share this post


Link to post
Share on other sites

If i test this with the following button on my form

 

procedure TForm1.W3Button1Click(Sender: TObject);
begin
 JSONStr:= 'Goodbye!';
end;

then run it and click button, then close out the form, nothing gets saved

 

procedure TForm1.Form1Deactivate(Sender: TObject);
begin
 LocalStorage.setKeyStr('Data', JSONStr);
end;
if i place a showmessage in the load
 
procedure TForm1.Form1Activate(Sender: TObject);
begin
  LocalStorage:= TW3LocalStorage.Create;
  try
   LocalStorage.Open('DEMO1');
  finally
   JSONStr:=  LocalStorage.GetKeyStr('Data', 'Hey!');
   ShowMessage(JSONStr);
  end;
end;

It still always shows the default value - "Hey!'

 

what am i missing?

Share this post


Link to post
Share on other sites

I think this example is working fine. BTW, a review of the whole RTL is very welcomed.
 

unit Form1;

interface

uses 
  SmartCL.Storage,
  SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms, 
  SmartCL.Fonts, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Button;

type
  TForm1 = class(TW3Form)
    procedure btnGetClick(Sender: TObject);
    procedure btnSetClick(Sender: TObject);
  private
    {$I 'Form1:intf'}
    FStorage: TW3LocalStorage;
    FColours : array of string;
    FColours2 : array of string;
    FstrColours, FstrRainfall : string;
    FRainfall : array[0 .. 11] of integer := [61, 36, 50, 42, 45, 46, 46, 44, 43, 73, 45, 59];
    FRainfallStringArray : array of string;
    FRainfall2 : array of integer;
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure FinalizeObject; override;
    procedure Resize; override;
  end;

implementation

{ TForm1 }

procedure TForm1.btnSetClick(Sender: TObject);
var
  s: String;
  i: Integer;
begin
  s := FStorage.getKeyStr('saved', 'error');
  if s = 'error' then
    begin
      FStorage.setKeyStr('saved', 'true');
      WriteLn('Saving strings and numbers.');
      FStorage.setKeyStr('Colours', FstrColours);
      FStorage.setKeyStr('Rainfall', FstrRainfall);
    end;
    /*
  else
    begin
      FstrColours := FStorage.getKeyStr('Colours', 'error');
      FColours2 := strSplit(FstrColours, ',');
      for i := 0 to 15 do
        WriteLn(FColours2[i] + ' ');

      FstrRainfall := FStorage.getKeyStr('Rainfall', 'error');
      FRainfallStringArray := strSplit(FstrRainfall, ',');
      for i := 0 to 11 do
        begin
          FRainfall2[i] := FRainfallStringArray[i].ToInteger;
           WriteLn(FRainfall2[i].ToString);
        end
    end;
    */
end;

procedure TForm1.btnGetClick(Sender: TObject);
var i: integer;
begin
  FstrColours := FStorage.getKeyStr('Colours', 'error');
  FColours2 := strSplit(FstrColours, ',');
  for i := 0 to 15 do
    WriteLn(FColours2[i] + ' ');

  FstrRainfall := FStorage.getKeyStr('Rainfall', 'error');
  FRainfallStringArray := strSplit(FstrRainfall, ',');
  for i := 0 to 11 do
    begin
      FRainfall2[i] := FRainfallStringArray[i].ToInteger;
      WriteLn(FRainfall2[i].ToString);
    end
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
end;

procedure TForm1.InitializeObject;
var
  i : integer;
begin
  inherited;
  {$I 'Form1:impl'}
  FColours := ['aqua', 'black', 'blue', 'fuchsia', 'green', 'gray',
              'lime', 'maroon', 'navy', 'olive', 'purple', 'red',
              'silver', 'teal', 'white', 'yellow'];
  FstrColours := strJoin(FColours, ',');
  FstrRainfall := '';
  for i := 0 to 10 do
    FstrRainfall += FRainfall[i].ToString + ',';
  FstrRainfall += FRainfall[11].ToString;

  FStorage := TW3LocalStorage.Create;
  FStorage.Open('Demo3');
end;

procedure TForm1.Resize;
begin
  inherited;
end;

procedure TForm1.FinalizeObject;
begin
inherited;
  FStorage.Close;
end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.

Share this post


Link to post
Share on other sites

> where is the most appropriate place to do the load and save from local storage in a project. i.e. from what event

> do you load from local storage and save to local storage? The Form's onactivate and ondeactivate do not seem to work

 

I would say, Application.ApplicationStarting for reading storage value.

For saving you can use ApplicationClosing but I would simply store it after change or at some interval, that's handling situation when user exits browser.

 

You can of course use FormActivate/Deactivate methods as well but that's a bit of overkill in multi form application when one form could be activated several times.

Share this post


Link to post
Share on other sites

Hi Igor,

 

I tried doing it at the Application level and could not figure out how to kick the ApplicationClosing off

 

procedure TApplication.ApplicationStarting;
begin
 inherited;
 ShowMessage('Starting');
end;
 
procedure TApplication.ApplicationClosing;
begin
 ShowMessage('Closing');
 inherited;
end;

Share this post


Link to post
Share on other sites

warleyalex & marcus,

 

I tried the form's  onactivate and ondeactivate events and then did not seem to work. Having said that, I would like to do it at the application level as Igor suggested - so I do not have to worry about freeing the storage when the form is deactivated

Share this post


Link to post
Share on other sites

if i select the form and double click on the OnActivate and OnDeactivate events, isn't it the same thing as assigning them at runtime

 

 
  TForm1 = class(TW3Form)
    procedure Form1Deactivate(Sender: TObject);
    procedure Form1Activate(Sender: TObject);
  private
    procedure HandleDeactivate(Sender: TObject);
    procedure HandleActivate(Sender: TObject);
  protected
  end;
procedure TForm1.HandleDeactivate(Sender: TObject);
begin
 ShowMessage('Handle Deactivate');
end;

procedure TForm1.HandleActivate(Sender: TObject);
begin
 ShowMessage('Handle Activate');
end;

procedure TForm1.Form1Deactivate(Sender: TObject);
begin
  ShowMessage('DeActivate');
end;

procedure TForm1.Form1Activate(Sender: TObject);
begin
   ShowMessage('Activate');
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
  self.OnActivate:= HandleActivate;
  self.OnDeActivate:= HandleDeActivate;
end;


Share this post


Link to post
Share on other sites

> I tried doing it at the Application level and could not figure out how to kick the ApplicationClosing off

 

It should be invoked as a result of an onunload javascript event, perhaps that event is not called for some reason.

 

For detecting FormActivated/Deactivated best to declare these in protected section of form:

    procedure FormActivated; override;
    procedure FormDeActivated; override;
 

Share this post


Link to post
Share on other sites

As Igor notes, the procedure FormActivated() and FormDeActivated() kick in to inform you of exactly that.

However these procedures should be superseded by these events firing.

 

As for WayleyX's comment "a review of the whole RTL is very welcomed".

That is exactly what is taking place.

 

The old RTL model has a good and solid foundation, for example the inheritance chain and how more and more complex behavior is incrementally built up.

However, there is still much to be desired when it comes to depth, order and "the little things".

 

Now that I think about it, are you hooking into these events on the first form?

If this is the default form, I think i know they dont fire.

 

These methods and events are invoked basically via the GotoForm() methods and the process they represent.

I dont have access to my VMWare test machines right now, and my primary installation is used for writing the next RTL.

So I am unable to check.

 

But it can be easily checked:

 

1. Add a second form.

2. Place a button on form1

3. In the OnClick event for the button, call GotoForm and navigate to the second form

 

If the events fire on the second form, then try the same in reverse:

 

1. Add a button on the second form

2. In the OnClick event for that button, use GotoForm() and navigate back to the first form

 

If the events fire in Form1, then we have solved the question.

I simply have to alter the startup code so that these methods are called regardless.

Share this post


Link to post
Share on other sites

on the application level?

 

 

unit Unit1;


interface


uses
  Pseudo.CreateForms, // auto-generated unit that creates forms during startup
  System.Types, SmartCL.System, SmartCL.Components, SmartCL.Forms, 
  SmartCL.Application, SmartCL.Storage, Form1;


type
  TApplication  = class(TW3CustomApplication)
  protected
   procedure ApplicationStarting; override;
   procedure ApplicationClosing; override;
  end;


var


 JSONStr: Variant;
 LocalStorage: TW3LocalStorage;


implementation


procedure TApplication.ApplicationStarting;
begin
 inherited;
  LocalStorage:= TW3LocalStorage.Create;
  try
   LocalStorage.Open('DEMO');
  finally
   JSONStr:=  LocalStorage.GetKeyStr('Data', 'Test');
  end;
end;


procedure TApplication.ApplicationClosing;
begin
  LocalStorage.setKeyStr('Data', JSONStr);
  LocalStorage.Close;
 inherited;
end;




end.

Share this post


Link to post
Share on other sites

Yes,  FormActivated() and FormDeActivated() work when navigating between forms.

 

I created a demo with 3 forms and added navigation code to move back and fourth between all three forms and those events were kicked off for each form in both directions

 

However, I still can't get ApplicationClosing to kick off. I also tried OnBeforeUnload

 

I really need a way to get LocalStorage to work for an app I am working on.

 

I can get the ApplicationStarting   AND   FormActivate to work. I do not mind loading my data from either of these events.  However, I can't use OnDeactivate or any other form finalization event - I need to save my data back to the LoaclStorage when the application terminates or is finalized somewhere.

 

Any more ideas?

 

 

As Igor notes, the procedure FormActivated() and FormDeActivated() kick in to inform you of exactly that.

However these procedures should be superseded by these events firing.

 

As for WayleyX's comment "a review of the whole RTL is very welcomed".

That is exactly what is taking place.

 

The old RTL model has a good and solid foundation, for example the inheritance chain and how more and more complex behavior is incrementally built up.

However, there is still much to be desired when it comes to depth, order and "the little things".

 

Now that I think about it, are you hooking into these events on the first form?

If this is the default form, I think i know they dont fire.

 

These methods and events are invoked basically via the GotoForm() methods and the process they represent.

I dont have access to my VMWare test machines right now, and my primary installation is used for writing the next RTL.

So I am unable to check.

 

But it can be easily checked:

 

1. Add a second form.

2. Place a button on form1

3. In the OnClick event for the button, call GotoForm and navigate to the second form

 

If the events fire on the second form, then try the same in reverse:

 

1. Add a button on the second form

2. In the OnClick event for that button, use GotoForm() and navigate back to the first form

 

If the events fire in Form1, then we have solved the question.

I simply have to alter the startup code so that these methods are called regardless.

Share this post


Link to post
Share on other sites

> I can get the ApplicationStarting   AND   FormActivate to work. I do not mind loading my data from either of these events.  However, I

> can't use OnDeactivate or any other form finalization event - I need to save my data back to the LoaclStorage when the

> application terminates or is finalized somewhere.

> Any more ideas?

 

It seems that onUnload event is generally having issues in browsers so most likely you'll never get applicationClosing called, I would suggest to save data to storage as soon as it's changed (it's cheap) or on some interval (each 5s if it's modified since last save).

Share this post


Link to post
Share on other sites

> then where would be a good place to call -   LocalStorage.Close();  ?

> Or do we not have to worry about closing it?

 

I don't think that's mandatory, I never called it and never experienced any data loss.

 

ok

Share this post


Link to post
Share on other sites

How do you trigger the methods TApplication.ApplicationClosing or TForm1.FinalizeObject?


you should to invoke manually: Application.terminate();


 


... but you can force the SMS app invoke this method on before unload event, this way.


 


declare this global external variable


function window: variant; external 'window' property;


 




function onbeforeunload(e: variant): variant;
begin
console.log('>>>> onbeforeunload called');

/* add here what you want before the browser window is closed.
application.terminate();
*/
result := '';
end;

procedure TForm1.InitializeObject;
var i : integer;
begin
inherited;
{$I 'Form1:impl'}
window.addEventListener('beforeunload', @onbeforeunload, false);
...
end;


Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×