Download as pdf or txt
Download as pdf or txt
You are on page 1of 32

 

Table of Contents 
Outline 1 

How to 2 
Creating Local Storage Entities 2 
Fetch Data from Local Storage on Screens 9 
Movies Screen 9 
People Screen 16 
MovieDetail Screen 17 
PersonDetail Screen 32 
AddCastAndCrew Screen 33 

Outline 
In this exercise, we will change the OSMDb Mobile application to include Local Storage. This 
exercise is divided in two different sections. 

First, we will create the Local Storage data model, by creating the Local Storage Entities that we 
will need. Our Local Storage data model will include: 

● LocalMovie Entity with the same structure as the Movie Entity. 


● LocalPerson Entity with the same structure as the Person Entity. 
● LocalPersonMovieRole Entity with the same structure as the PersonMovieRole Entity. 
● LocalMovieGenre and LocalPersonRole Entities with just an ID and a Label. 
● The rating and respective UserMovieRatingIdentifier represented as attributes in the 
LocalMovie Entity. 

Then, we will need to go to the five different Screens of the application and change the data 
sources to use Local Storage, except for the average rating of a movie, in the MovieDetail 
 


 

Screen. This means that in every other scenario, the Screens should be changed to include the 
Local Storage as data source of the widgets and also in other relevant scenarios such as Input 
Parameters and Local Variables.  

At the end of this exercise, the application will not be working properly, since it is not fully 
tailored to work offline, as well as it will not have any data, since the Local Storage Entities were 
not populated with data yet. 

How to 
In this section, we’ll describe, step by step, the exercise ​4.3 - Local Storage Exercise​. 

Creating Local Storage Entities 


We will start this exercise by creating the Local Storage Entities in the​ OSMDb_Core_Mobile 
module. To do that, we will leverage the Service Studio accelerator to create a new Entity from 
the structure of an existing Database Entity. 

1. Create the LocalMovie Entity, with the exact same structure as the respective Database 
Entity. All the attributes of the Movie Entity are used in our application, namely to display 
the details of a movie, so it is important that the Local Storage Entity has a similar 
structure. 

a. Open the ​OSMDb_Core_Mobile​ module and switch to the Data tab. 

b. Under Entities, right-click on the Local Storage section and select the option​ Add 
Entity from Database... 


 

c. In the new dialog, select the ​Movie E


​ ntity and click ​OK​. 

d. In the next dialog, select all the attributes from the Movie Entity and click A
​ DD​. 

Service Studio will warn you that we should avoid mirroring the server data 
model and prefer a lightweight local storage, which is a very important advice, 


 

since the data synchronization procedure can become a heavy operation if all 
data is replicated locally. However, in our scenario, we are talking about an Entity 
with not a lot of attributes, and all the attributes are needed to display the 
relevant information about the movie.  

e. The Local Storage Entity will be automatically generated with the name 
LocalMovie​. Set its P
​ ublic ​property to ​Yes​, keeping the ​Expose Read Only 
property to ​Yes​. 


 

2. Create the ​LocalPerson a


​ nd ​LocalPersonMovieRole E
​ ntities in Local Storage, with the exact 
same structure of the ​Person a
​ nd ​PersonMovieRole E
​ ntities, respectively. Set them as 
Public and exposed as read-only. 

3. Create the ​LocalPersonRole a


​ nd ​LocalMovieGenre ​Entities in Local Storage, using just the 
Id and Label attribute of the respective Database Entities, P
​ ersonRole ​and ​MovieGenre​. 
In this case we only need the ​Id ​and the L
​ abel l​ ocally. The Id will distinguish each Role 
and Genre, while the Label is important to display the Role and Genre to the end user. 
These Entities should also be P
​ ublic ​and exposed as read-only. 

a. Right-click the Local Storage section and select A


​ dd Entity From Database... 


 

b. Select the ​MovieGenre S


​ tatic Entity and click ​OK​. 

c. In the next dialog select the ​Label a


​ ttribute and click A
​ DD​. 

d. In the automatically created ​LocalMovieGenre ​Entity, set its ​Public ​property to ​Yes 
and keep the E
​ xposed Read Only​ as ​Yes​. 


 

e. Repeat the previous steps using the P


​ ersonRole ​Entity and its I​ d ​and L
​ abel 
attributes. 

f. In the new Entity, ​LocalPersonRole​, set the ​Public p


​ roperty to ​Yes ​and maintain the 
Expose Read Only​ property as ​Yes. 

4. Finally, let’s add two new attributes to the L


​ ocalMovie E
​ ntity, to represent the rating that 
the user gives to a movie and its Database Identifier. In this case, we do not need the 
UserId, since on the device we already know which user it is, as well as the MovieId 
becomes unnecessary if we just add the attributes to the LocalMovie itself. Make sure 
the attributes are ​non-mandatory​ and are created with U
​ serMovieRating Identifier​ and 
Integer d
​ ata types​ ​respectively. 


 

5. Publish the ​OSMDb_Core_Mobile​ module. 

Fetch Data from Local Storage on Screens 


At this point, all Screens from the OSMDb Mobile application fetch data from the Database. In 
this second part of the exercise, we will change that and make sure the Screens, for most use 
cases, use local storage data, regardless if they are offline or online. The only exception is the 
average rating of a movie that needs to be always fetched from the server.Also, we will revisit 
the S
​ aveOnClick ​Actions on the Detail Screens, to guarantee that the Local Storage data is 
updated as well. This is important since the source of data, regardless if the app is online or 
not, will be the local storage Entities. So, if we only update the Database on the server, the 
changes are not reflected locally.  

Movies Screen 
Let’s start by the Movies Screen and replace all the Database Aggregates by Local Storage ones, 
fixing all the errors that appear. Do not forget to also adjust the sorting functionality to use the 
Local Storage Entities. 

1. In the ​OSMDb_Mobile​ module, reference the Entities added in the previous section. 


 

2. In the Movies Screen, replace the existing ​GetMovies A


​ ggregate with a similar 
Aggregate, but using the respective Local Storage Entity as Source. After finishing, delete 
the GetMovies Aggregate and rename this new Aggregate to be called G
​ etMovies​. Adjust 
the widgets to use the new data source and fix any other errors that appear.  

a. Open the ​OSMDb_Mobile​ module, switch to the Interface tab and open the 
Movies ​Screen. 

b. Right-click on the Movies Screen and select F


​ etch Data from Local Storage​.  

 
 
 
 
 
 


 

c. Click on the new Aggregate and select the ​LocalMovie E


​ ntity. 

d. Open the ​Filters s​ ection of the Aggregate and add the following conditions: 

LocalMovie.Title like ​"%"​ + Client.MovieSearchText + "​ %"​ or 


LocalMovie.PlotSummary like ​"%"​ + Client.MovieSearchText + "​ %" 

Client.MovieGenreId = NullIdentifier() or LocalMovie.GenreId = Client.MovieGenreId 

NOTE:​ These filters are used in the G


​ etMovies ​Aggregate to support the search 
functionality in the Movies Screen. The first one filters the movies with title and 
plot summary that contains the keyword submitted by the end-user. The second 
one filters the movies by their genre. The filters use Client Variables to guarantee 
that when the user changes Screen and comes back, the search filter is still being 
applied. 

10 
 

e. Open the ​Sorting ​section and select A


​ dd Dynamic Sort​. Select the L
​ istSort ​Local 
Variable. This variable helps support the sort mechanism implemented in the 
Movies Screen. 

f. Delete the G
​ etMovies ​Aggregate and rename the ​GetLocalMovies A
​ ggregate to 
GetMovies​. Service Studio should report several errors, which is normal, since we 
deleted an important data source from the Screen. Let’s fix them. 

g. In the TrueChange section, on the bottom left of Service Studio, expand indicating 
the errors. 

h. Double-clicking on the first error takes us to the Movies Screen, in particular to 
the List of movies, and the Expression editor opens with the first error.  

i. This Expression is used to display the title of the movie in the List, but the error 
indicates that the expression cannot identify the element Movie. That happens 

11 
 

because we deleted the old G


​ etMovies ​Aggregate and replaced it with a Local 
Storage Aggregate with the same name. However, the Source of the new 
GetMovies Aggregate is the ​LocalMovie E
​ ntity, not the Movie Entity. 

NOTE:​ More errors would appear if we did not change the name of the Aggregate 
to be the same as the one that was deleted. If that would be the case, we would 
need for instance, to also change the Source property of the List, to bind it to the 
new Aggregate, as well as in other widgets on the Screen. Changing the name to 
match the name of the original Aggregate deals with all those mappings 
automatically for us.  

j. Replace Movie by ​LocalMovie ​in the expression 

GetMovies.List.Current.LocalMovie.Title 

k. Repeat this process for the remaining errors, by replacing the occurrences of 
Movie by ​LocalMovie​, since all the issues have the same cause. 

3. Now, it is time to replace the G


​ etMovieGenres ​Aggregate, following the exact same 
strategy as in the previous step. Create a Local Storage Aggregate to fetch data from the 
LocalMovieGenre Entity, delete the Database Aggregate and fix all the errors that may 
appear. 

a. Create the G
​ etLocalMovieGenres ​Aggregate using the LocalMovieGenre Entity as 
Source. 

12 
 

b. Delete the G
​ etMovieGenres ​Aggregate and rename the new Aggregate as 
GetMovieGenres​. 

c. Service Studio only indicates one error. Double-clicking on it leads us to the 


dropdown to search for movie genres, which is still using the MovieGenre instead 
of the LocalMovieGenre. Replace the occurrence of MovieGenre by 
LocalMovieGenre​. 

LocalMovieGenre.Id 

d. Now that the issue is fixed, a new error appears, this time on the Variable 
property of the Dropdown 

This happens because the Dropdown is using a Client Variable, MovieGenreId, of 
type MovieGenre Identifier. But now, since we’re using the Local Storage Entity, 
the Variable field expects a L
​ ocalMovieGenre Identifier​.  

13 
 

Switch to the Data tab, select the M


​ ovieGenreId ​Client Variable and change its 
Data Type​ to​ LocalMovieGenre Identifie​r. 

4. We do not have any more errors in Service Studio, but there is still something else we 
need to fix. The sorting by title and year still uses the Database Entity, however since the 
sorting is defined with a text, for instance “​ {Movie}.[Title]”, ​Service Studio does not 
recognize it as an error. 

14 
 

a. In the Movies Screen, select the Link around the S


​ ort By Title​. This Link triggers a 
Client Action, ​SortOnClick​, that expects the sort that was selected by the user, 
through the S
​ electedSort I​ nput Parameter. Set the value of the SelectedSort to 
{LocalMovie}.[Title] 

b. Apply the same logic to the S


​ ort by Year​, using ​“{LocalMovie}.[Year]” 

5. Publish the module to save the most recent changes. 

People Screen 
In the People Screen, we have a Database Aggregate to fetch all the people stored in the Person 
Database Entity. Following the same strategy used in the Movies Screen, let’s replace the 
Database Aggregate by a Local Storage Aggregate, to fetch the people from the LocalPerson 
Entity. As above, this will cause some errors that we need to fix along the way, and do not 
forget to also adjust the sorting criteria. 

15 
 

1. Create a Local Storage Aggregate with the LocalPerson Entity as Source and set a 
Dynamic Sort using the ListSort Local Variable.  

2. Delete the G
​ etPeople ​Aggregate and rename the new Aggregate as ​GetPeople​. 

3. There are 6 errors that need to be fixed. Double-clicking on the first one leads us to the 
List widget in the People Screen, to display all people in the database. As it happened in 
the Movies Screen, the error is caused by having the Expressions still referring to a 
Person record, instead of LocalPerson. Fix the first error by changing the Expression 
value to 

GetPeople.List.Current.LocalPerson.Name 

and apply the same logic for the remaining errors. 

4. Adjust the ​Sort by Name​ and ​Sort by Surname​ Links to use the LocalPerson Entity, 
instead of the Person Database Entity. 

5. Publish the module to save the latest changes. 

MovieDetail Screen 
The third Screen we will work on is the MovieDetail Screen. This one is probably the most 
complex, since it has multiple Aggregates to work on. Don’t forget that we are not going to 
change the GetAverageRating Aggregate, since the average rating will only be available when 
the app is online. Let’s start by the GetMovieById Aggregate. 

Also, we need to make sure that the ​SaveOnClick ​and the S


​ tarClickedHandler ​Actions 
consider local storage. When the app is online, the movie information should be saved as well 
in Local Storage, while the user rating should always be saved locally, even if the app is offline. 

16 
 

1. Create a Local Storage Aggregate similar to the G


​ etMovieById ​Aggregate. Delete the 
existing Database Aggregate and rename the new Aggregate to G
​ etMovieById​. Fix all the 
errors that appear. 

a. Open the MovieDetail Screen and create a new Local Storage Aggregate, with the 
LocalMovie E
​ ntity as Source and the following Filter 

LocalMovie.Id = MovieId 

b. Delete the G
​ etMovieById ​Aggregate and rename the new Aggregate as 
GetMovieById​. This will lead to 11 errors. 

c. Double-clicking on the first error leads to the ​SaveOnClick ​Action, which has the 
logic to create / update a new movie in the database, as well as the Form 
validations. The error appears exactly in the validations logic, but has a similar 
cause to most of the errors we fixed so far. Replace the Movie reference by 
LocalMovie, keeping the rest of the Condition as it was. 

d. Apply the same logic to the second If of the logic flow in the SaveOnClick Action. 

17 
 

e. Continuing on the SaveOnClick Action flow, the next error appears in the 
MovieCreateOrUpdate Action call, because we are passing to the server a Movie 
record. Now, since the Screen will use a LocalMovie instead, we also need to 
change it from Movie to LocalMovie. 

In the properties of the MovieCreateOrUpdate Action call we can see that 


OutSystems automatically mapped the attributes of a LocalMovie to the Movie 
attributes.  

18 
 

This happens because, despite all our changes, the Server Action still expects a 
Movie record, which makes sense since it is a Server Action and it does not have 
access to Local Storage. 

So, OutSystems maps the attributes of the Movie Entity to the attributes of the 
record of the LocalMovie Entity automatically. This way, we can use the 
LocalMovie record in our Screen and easily transform it to a Movie record when 
calling the Server Action. 

This mapping can be changed at any time and can always be done manually 
between other Entities. As long as the attributes have the same data type, the 
matching will be possible.  

19 
 

f. Double-clicking on the next errors leads us to the ​MovieDetail S


​ creen and to its 
Title. Replace the Movie by ​LocalMovie i​ n the Expression. 

g. Fix three more errors by applying the same logic to the following input fields for 
movie title, year and plot summary. 

20 
 

h. Apply the same logic to the dropdown. However, here the error does not 
disappear. 

If we look at the List property of the Dropdown, it is set to the output List of the 
GetMovieGenres Aggregate, which is still a Database Aggregate. So, when we 
change the Variable property to consider a LocalMovieGenre Identifier, the error 
changes to a ​type mismatch error​, since the Variable is still expecting a 
MovieGenre Identifier data type. We will fix this later when we work on the 
GetMovieGenres Aggregate. 

i. Fix the remaining errors in the input fields for the gross takings and availability in 
DVD, using the same strategy. 

2. Create a Local Storage Aggregate similar to the G


​ etMovieGenres ​Aggregate. Delete the 
existing Database Aggregate and rename the new Aggregate to G
​ etMovieGenres​. Fix all 
the errors that appear. 

a. Create the new Local Storage Aggregate with the LocalMovieGenre as Source. 
Delete the G
​ etMovieGenres ​Aggregate and rename the new one to 
GetMovieGenres​. 

21 
 

b. Notice that there is one error in the Dropdown in the MovieDetailScreen, 


however it is not the same one as we had in the previous step.  

The List property does not have an error anymore. The error now is in the 
Options Value property, which is still using the MovieGenre.Id, instead of the 
LocalMovieGenre.Id. 

c. Change the property to​ LocalMovieGenre.Id  

3. Now we will work on the G


​ etProductionTalent a
​ nd on the G
​ etCastCrew A
​ ggregates. 
The first Aggregate returns all the people that have the director and producer role in the 
movie being displayed on the MovieDetail Screen, while the second Aggregate does the 
same but for Actors and Crew members. Just like in the previous steps, replace those 
two Aggregates by similar Local Storage Aggregates and fix all the errors that appear. 

a. Create a Local Storage Aggregate with three Sources: ​LocalPersonMovieRole​, 


LocalPerson a
​ nd ​LocalPersonRole​. 

22 
 

b. Add the following two filters to the Aggregate: 

LocalPersonMovieRole.MovieId = MovieId 

LocalPersonRole.Label = "Director" or LocalPersonRole.Label = "Producer" 

These filters are similar to the ones in the GetProductionTalent Aggregate. 


However, since there are no Static Entities in Local Storage, we cannot use the 
Entities.PersonRole.Director​ or ​Entities.PersonRole.Producer​ to access in runtime the 
Id of the role we want to search for. So, locally, we need to use the Label of the 
LocalPersonRole. 

c. Delete the G
​ etProductionTalent ​Aggregate and rename the new one to 
GetProductionTalent​. This will cause two errors. 

d. Double-clicking on the first error leads us to the Production Talent List, which is 
still referring to Person and PersonRole records. Change it to LocalPerson and 
LocalPersonRole respectively. 

  

e. Fix the last error by changing the Expression to use ​LocalPersonMovieRole​. 

f. Create a new Local Storage Aggregate with three Source: 


LocalPersonMovieRole​, ​LocalPerson a
​ nd ​LocalPersonRole​. 

g. Add the following two Filters: 

LocalPersonMovieRole.MovieId = MovieId 

LocalPersonRole.Label = "Actor" or LocalPersonRole.Label = "Crew" 

23 
 

Following the same logic as above, we are adapting the Filters of the GetCastCrew 
Aggregate to adapt to the Local Storage nature of this new Aggregate. 

h. Delete the G
​ etCastCrew ​Aggregate and rename the new one to G
​ etCastCrew​. This 
will cause two errors. Fix them using the same strategy as above. 

4. Finally, we have the G


​ etUserMovieRatingsByMovieId ​Aggregate, which fetches the 
rating that the user logged in gave to the movie currently being displayed in the 
MovieDetail Screen. Here, the scenario is a bit different because we do not have a similar 
Local Storage Entity to the UserMovieRating Database Entity. We have an attribute on 
the LocalMovie Entity that stores the rating. Let’s adjust the Screen to work without that 
Aggregate. 

a. Delete the G
​ etUserMovieRatingsByMovieId ​Aggregate. This will cause four 
errors. 

b. The first error leads us to the S


​ tarClickedHandler ​Action that is triggered when a 
user clicks on a star to rate a movie. This Action then sends the new rating to be 
saved in the Database and refreshes the GetAverageRating Aggregate to calculate 
the new average. 

24 
 

The UserMovieRatingCreateOrUpdate Action call has the purpose of sending the 


rating information to the server, to then be stored in the UserMovieRating Entity. 
However, in this mapping, the value for the Id is causing an error since the 
Aggregate was deleted. 

c. Replace the ​Id v


​ alue with  

GetMovieById.List.Current.LocalMovie.RatingId 

Do not forget that the LocalMovie Entity has an attribute for the ​RatingId​, so it is 
fetched already on the GetMovieById Aggregate.  

d. The next errors appear in the Assign right below the Server Action call.  

Here, we were using the output of the Aggregate to save in memory the current 
Id of the Rating and the rating itself, so that the Screen would re-render to display 
the new rating given by the user. So, we need to change the Assign to use the 
RatingId a
​ nd ​Rating a
​ ttributes fetched by the GetMovieById Aggregate. 

25 
 

e. Change the assignments to be: 

GetMovieById.List.Current.LocalMovie.RatingId = 
UserMovieRatingCreateOrUpdate.UserMovieRatingId 

GetMovieById.List.Current.LocalMovie.Rating = SelectedStar 

f. The last error takes us to the actual Block that is displaying the stars in the 
MovieDetail Screen. Replace the existing Expression by 

GetMovieById.List.Current.LocalMovie.Rating 

NOTE:​ There could be some other tweaks done to the logic, but at this time we 
are just focusing on displaying the data on Screens using Local Storage. We will 
get back to this in future exercises. 

5. Change the ​SaveOnClick A


​ ction on the MovieDetail Screen to also include the logic to 
create or update the movie in Local Storage. Since the Local Storage Entities are not 
Public, we need to create a C
​ lient Action​ on the OSMDb_Core_Mobile module and 
expose that as Public. In the SaveOnClick Action, this new Action should only be called if 
the create / update movie in the database is successful.  

a. In the OSMDb_Core_Mobile module, create a new​ Client Action​ and call it 
LocalMovieCreateOrUpdate.​ Set the Action as P
​ ublic​. 

26 
 

b. Add one Input Parameter and three Output Parameters to the Action. The Input 
Parameter should be called ​LocalMovie​, with​ Data Type​ set to ​LocalMovie​. The 
Output Parameters should be called L​ ocalMovieId​, ​Success ​and M
​ essage​, with D
​ ata 
Types​ set to L​ ocalMovie Identifier, Boolean ​and​ Text ​respectively. 

 
c. Drag a ​Run Client Action​ node and drop it on the Action flow. Choose the 
CreateOrUpdateLocalMovie ​Entity Action. 

d. Set the value for the ​Source o


​ f the CreateOrUpdateLocalMovie Action to be 
LocalMovie (​ the input parameter). 

e. Drag an ​Assign n
​ ode and drop it below the CreateOrUpdateLocalMovie Action. 
Set the Assignments to be: 

LocalMovieId = CreateOrUpdateLocalMovie.Id 

Success = True 

Message = “Local Movie created or updated successfully” 

27 
 

We will not do any verification in this Action, so the Action will be successful. 
Nevertheless, the logic can then be adjusted to include any verification you find 
relevant. 

 
f. Publish the module. 

g. After publishing, switch to the OSMDb_Mobile module and add a dependency to 
the recently created LocalMovieCreateOrUpdate Client Action. 

h. In the MovieDetail Screen, open the ​SaveOnClick ​Action. Drag a ​Run Client 
Action​ node and drop it on the True branch of the ​MovieCreateOrUpdate.Success 
If.  

28 
 

i. Select the ​LocalMovieCreateOrUpdate ​Client Action in the new dialog. 

j. Set the ​LocalMovie ​value to G


​ etMovieById.List.Current.LocalMovie 

6. Change the ​StarClickedHandler A


​ ction on the MovieDetail Screen to also include the 
logic to create or update the movie rating, regardless if the app is online or offline. We 
can take advantage of the L
​ ocalMovieCreateOrUpdate ​Action to do that. 

29 
 

a. Open the ​StarClickedHandler A


​ ction. Drag a ​Run Client Action ​and drop it​ ​after 
the Assign node.  

b. Select the ​CreateOrUpdateRating A


​ ction, under the O
​ SMDb_Core_Mobile 
module. 

30 
 

c. Set the ​LocalMovie ​Input Parameter of the Action to 


GetMovieById.List.Current.LocalMovie 

PersonDetail Screen 
We will now move on to the P
​ ersonDetail ​Screen, where we will apply the same strategy of 
replacing the Database Aggregates by Local Storage Aggregates, and saving the data also in 
Local Storage. 

1. Create a new Local Storage Aggregate with the ​LocalPerson E


​ ntity as Source and with 
the following Filter 

LocalPerson.Id = PersonId 

2. Delete the G
​ etPersonById ​Aggregate and rename the new Aggregate as G
​ etPersonById​. 

3. Fix all the errors that appear by replacing references to the Person Entity by 
LocalPerson. 

4. Change the ​SaveOnClick A


​ ction on the PersonDetail Screen to also include the logic to 
create or update the movie in Local Storage. Follow the same strategy applied in the 
MovieDetail Screen.  

5. Publish the module to save the changes. 

31 
 

AddCastAndCrew Screen 
Finally, we just have the AddCastAndCrew Screen left, which has three Aggregates.  

1. Let’s replace all of them by similar Local Storage Aggregates and fix all the errors. 

2. This Screen has a particular case of a Local Variable with ​Data Type​ set to 
PersonMovieRole​. Change its n
​ ame ​to L​ ocalPersonMovieRole ​and its D
​ ata Type​ also to 
LocalPersonMovieRole​. 

3. Change the ​SaveOnClick A


​ ction on the AddCastAndCrew Screen to also include the logic 
to create or update the movie in Local Storage. Follow the same strategy applied in the 
MovieDetail Screen.  

4. All is set! Publish the module to save the latest changes. 

5. Open the application on the device to test it. At this point you should see no movies on 
the Movies Screen. That does not mean we did something wrong. It’s just that we do not 
have yet the local storage with data. We will solve this issue in the next exercise, when 
we implement the synchronization procedure. 

6. If by any chance you test your application without network connection, you will get an 
error. This is normal at this point and it will also be addressed later in the Offline 
exercise. 

32 

You might also like