Saving user settings in a device’s local storage is a common requirement for mobile applications. Because of that, we made the creation of a settings page with NativeScript straightforward. In this blog post you’ll learn how to build a settings page using the latest bits of NativeScript—XML declaration, data-binding, and CSS styling. To separate the UI code from the app’s logic, we will follow the MVVM pattern. To demonstrate this, we will use the real-life Fitness application, which source code can be found at http://github.com/nativescript/sample-fitness.
Once you have your project up and running by either using the Telerik Platform or the NativeScript CLI, we will start by defining the view-model for the settings page. It will hold all the setting needed for our soon-to-be-fitness-application: name, weight, height and some notification preferences.
We will use the application-settings module for saving and retrieving the settings from the device’s local storage. We will use a different string key for each property.
The settingsViewModel will also be Observable object to support two-way binding .
JS (view-model.js):
var
observable = require(
"data/observable"
);
var
appSettings = require(
"application-settings"
);
var
dialogs = require(
"ui/dialogs"
);
var
NAME =
"settings-name"
;
var
HEIGHT =
"settings-height"
;
var
WEIGHT =
"settings-weight"
;
var
VIBRATE =
"settings-vibrate"
;
var
SOUND =
"settings-sound"
;
var
SOUND_VOLUME =
"settings-sound-value"
;
var
settings =
new
observable.Observable();
Object.defineProperty(settings,
"name"
, {
get:
function
() {
return
appSettings.getString(NAME,
"John Doe"
); },
set:
function
(value) { appSettings.setString(NAME, value); },
enumerable:
true
,
configurable:
true
});
Object.defineProperty(settings,
"height"
, {
get:
function
() {
return
appSettings.getString(HEIGHT,
"180"
); },
set:
function
(value) { appSettings.setString(HEIGHT, value); },
enumerable:
true
,
configurable:
true
});
Object.defineProperty(settings,
"weight"
, {
get:
function
() {
return
appSettings.getString(WEIGHT,
"80"
); },
set:
function
(value) { appSettings.setString(WEIGHT, value); },
enumerable:
true
,
configurable:
true
});
Object.defineProperty(settings,
"vibrateEnabled"
, {
get:
function
() {
return
appSettings.getBoolean(VIBRATE,
true
); },
set:
function
(value) { appSettings.setBoolean(VIBRATE, value); },
enumerable:
true
,
configurable:
true
});
Object.defineProperty(settings,
"soundEnabled"
, {
get:
function
() {
return
appSettings.getBoolean(SOUND,
true
); },
set:
function
(value) { appSettings.setBoolean(SOUND, value); },
enumerable:
true
,
configurable:
true
});
Object.defineProperty(settings,
"soundVolume"
, {
get:
function
() {
return
appSettings.getNumber(SOUND_VOLUME, 100); },
set:
function
(value) { appSettings.setNumber(SOUND_VOLUME, value); },
enumerable:
true
,
configurable:
true
});
settings.promptName =
function
(args) {
console.log(
"in promptName"
);
dialogs.prompt(
"Enter your name:"
, settings.name).then(
function
(promptResult) {
console.log(
"prompt result:"
+ promptResult.result);
if
(promptResult.result) {
settings.set(
"name"
, promptResult.text);
}
});
}
exports.settingsViewModel = settings;
Now that we have our view-model, it’s time to create the UI for the settings.
We will add two files to our project:
First, we will create a page with an empty StackLayout in it and handle the loaded event. In the pageLoaded function we will set the bindingContext of the page to the view-model we created earlier:
XML(main-page.xml):
<
Page
xmlns
=
"http://www.nativescript.org/tns.xsd"
loaded
=
"pageLoaded"
>
<
StackLayout
>
...
</
StackLayout
>
</
Page
>
JavaScript(main-page.js):
var
vmModule = require(
"./view-model"
);
var
viewModel = vmModule.settingsViewModel;
function
pageLoaded(args) {
var
page = args.object;
page.bindingContext = viewModel;
}
Now let’s define the profile settings section containing the name, weight and height. We will use binding syntax to bind the text values of the fields to the view model:
<GridLayout cssClass=
"field-group"
columns=
"auto, 50, *"
rows=
"auto, auto, auto"
>
<!-- Name -->
<Label
text=
"Name"
/>
<Button col=
"1"
colSpan=
"2"
text=
"{{ name }}"
tap=
"{{ promptName }}"
/>
<!-- Height -->
<Label
text=
"Height"
row=
"1"
/>
<TextField row=
"1"
col=
"1"
text=
"{{ height }}"
keyboardType=
"number"
/>
<Label col=
"2"
row=
"1"
text=
"cm"
/>
<!-- Weight -->
<Label row=
"2"
text=
"Weight"
/>
<TextField row=
"2"
col=
"1"
text=
"{{ weight }}"
keyboardType=
"number"
/>
<Label row=
"2"
col=
"2"
text=
"kg"
/>
</GridLayout>
settings.promptName =
function
(args) {
console.log(
"in promptName"
);
dialogs.prompt(
"Enter your name:"
, settings.name).then(
function
(promptResult) {
console.log(
"prompt result:"
+ promptResult.result);
if
(promptResult.result) {
settings.set(
"name"
, promptResult.text);
}
});
}
The result (we will fix the styles later on):
Next section holds settings for notification vibration and sound. We will use a Switch and a Slider controls:
XML:
<GridLayout cssClass=
"field-group"
columns=
"*, auto"
rows=
"auto, auto, auto"
>
<!-- Notifications -->
<Label cssClass=
"field"
text=
"Vibrate"
/>
<Switch col=
"1"
cssClass=
"field-value"
checked=
"{{ vibrateEnabled }}"
/>
<!-- Notifications -->
<Label row=
"1"
cssClass=
"field"
text=
"Sound"
/>
<Switch row=
"1"
col=
"1"
cssClass=
"field-value"
checked=
"{{ soundEnabled }}"
/>
<Slider row=
"2"
colSpan=
"2"
maxValue=
"100"
value=
"{{ soundVolume }}"
isEnabled=
"{{ soundEnabled }}"
/>
</GridLayout>
The result:
All the functionality is in place. What is left is to add some CSS styling to the page.
Tip: If there is a CSS file with the same as the XML file with the UI declaration, it will get applied to the page automatically. Also you can create a file named app.css and define styles in it which can be used from any page inside the application.
We will need to add some cssClass attributes in the XML declaration.
...
<!-- Height -->
<Label cssClass=
"field"
text=
"Height"
row=
"1"
/>
<TextField row=
"1"
col=
"1"
cssClass=
"field-value"
text=
"{{ height }}"
keyboardType=
"number"
/>
<Label col=
"2"
row=
"1"
cssClass=
"field-unit"
text=
"cm"
/>
<!-- Weight -->
<Label row=
"2"
cssClass=
"field"
text=
"Weight"
/>
<TextField row=
"2"
col=
"1"
cssClass=
"field-value"
text=
"{{ weight }}"
keyboardType=
"number"
/>
<Label row=
"2"
col=
"2"
cssClass=
"field-unit"
text=
"kg"
/>
...
.field, .field-value, .field-unit, .field-dialog-button {
font-size
:
16
;
vertical-align
:
center
;
}
.field, .field-unit {
color
:
#3c3c3c
;
}
.field-value, .field-dialog-button {
color
:
#808080
;
text-align
:
right
;
}
After adding header labels and a little more styling we get this:
You can check out the final application here - http://github.com/nativescript/sample-fitness