I have spent the better part of a full day looking through this site and the rest of the inter webs to piece together something that is probably a no-brainer for all of you top dogs. There is nothing I found that was all encompassing and overall most of the samples are missing some level of clarity.
SO I wanted to trying and accomplish an MVVM pattern and simply take JSON results from a webservice and populate a list view :)
The webservice returns this
[{"total_bulls":"651","GenericName":"Aripiprazole","brandName":"Abilify","drugCat":"Atypical Antipsychotic","bullID":2793,"fastURL":"http:\/\/got*****.com\/drug-bulletin\/abilify\/","litAlertLvl":"High"},{"total_bulls":"651","GenericName":"Zafirlukast","brandName":"Accolate","drugCat":"Leukotriene Antagonist","bullID":2794,"fastURL":"http:\/\/got****.com\/drug-bulletin\/accolate\/","litAlertLvl":"Withdrawn"},{"total_bulls":"651","GenericName":"Albuterol Sulfate Inhalation Solution","brandName":"AccuNeb","drugCat":"Bronchodilator","bullID":2855,"fastURL":"http:\/\/go***.com\/drug-bulletin\/accuneb\/","litAlertLvl":"Low"},{"total_bulls":"651","GenericName":"Quinapril Hydrochloride","brandName":"Accupril","drugCat":"ACE Inhibitor","bullID":2661,"fastURL":"http:\/\/go****.com\/drug-bulletin\/accupril\/","litAlertLvl":"Low"},{"total_bulls":"651","GenericName":"Quinapril HCl\/Hydrochlorothiazide","brandName":"Accuretic","drugCat":"ACE Inhibitor\/Thiazide Diuretic","bullID":2813,"fastURL":"http:\/\/got****.com\/drug-bulletin\/accuretic\/","litAlertLvl":"High"}]
I want the ListView to display the proper data and trigger a click action. The problems i ran into surrounded getting the results from the call to the webservice to populate the listview.
I can manually populate the model like this:
const viewModel = observableModule.fromObject({
bulletins: []
// Setting the listview binding source
/*
bulletins: [
{
"total_bulls": "651",
"GenericName": "Aripiprazole",
"brandName": "Abilify",
"drugCat": "Atypical Antipsychotic",
"bullID": 2793,
"fastURL": "http://g****s.com/drug-bulletin/abilify/",
"litAlertLvl": "High"
}, {
"total_bulls": "651",
"GenericName": "Zafirlukast",
"brandName": "Accolate",
"drugCat": "Leukotriene Antagonist",
"bullID": 2794,
"fastURL": "http://g****.com/drug-bulletin/accolate/",
"litAlertLvl": "Withdrawn"
}, {
"total_bulls": "651",
"GenericName": "Albuterol Sulfate Inhalation Solution",
"brandName": "AccuNeb",
"drugCat": "Bronchodilator",
"bullID": 2855,
"fastURL": "http://go****.com/drug-bulletin/accuneb/",
"litAlertLvl": "Low"
}
]
*/
});
However trying to do this with the JSON results from the call proved to be challenging.
After many hours of trial and error I came to this working pattern. Improvements on this are welcome.
Spin up a vanilla JS Core Nativescript 'Drawer Navigation' template project from either Sidekick or from here https://market.nativescript.org/plugins/tns-template-drawer-navigation and add these scripts (I put the first 3 in a folder named "bulletins" and the last one in a folder named "services").
I also added the list-view plugin.
bulletins-page.xml
<Page class="page" navigatingTo="onNavigatingTo"
xmlns="http://schemas.nativescript.org/tns.xsd">
<ActionBar class="action-bar">
<!--
Use the NavigationButton as a side-drawer button in Android
because ActionItems are shown on the right side of the ActionBar
-->
<NavigationButton ios:visibility="collapsed" icon="res://menu" tap="onDrawerButtonTap"></NavigationButton>
<!--
Use the ActionItem for IOS with position set to left. Using the
NavigationButton as a side-drawer button in iOS is not possible,
because its function is to always navigate back in the application.
-->
<ActionItem icon="res://navigation/menu" android:visibility="collapsed" tap="onDrawerButtonTap" ios.position="left">
</ActionItem>
<Label class="action-bar-title" text="Bulletins"></Label>
</ActionBar>
<GridLayout class="page-content">
<Label class="page-icon fa" text=""></Label>
<Label class="page-placeholder" text="<!-- Page content goes here -->"></Label>
</GridLayout>
<ScrollView>
<StackLayout>
<ListView items="{{ bulletins }}" itemTap="onItemTap" loaded="{{ onListViewLoaded }}"
separatorColor="orangered" rowHeight="100" height="500" class="list-group" id="listView" row="2">
<ListView.itemTemplate>
<!-- The item template can only have a single root view container (e.g. GriLayout, StackLayout, etc.) -->
<StackLayout class="list-group-item">
<Label text="{{ GenericName || 'Downloading...' }}" textWrap="true" class="title" />
<Label text="{{ brandName || 'Downloading...' }}" textWrap="true" class="title" />
</StackLayout>
</ListView.itemTemplate>
</ListView>>
</StackLayout>
</ScrollView>
</Page>
bulletins-page.js
const app = require("tns-core-modules/application");
const BulletinsViewModel = require("./bulletins-view-model");
const listViewModule = require("tns-core-modules/ui/list-view");
function onNavigatingTo(args) {
const page = args.object;
//bind the page to this the viewModel Function
page.bindingContext = new BulletinsViewModel();
//now call the function that GETS the data from the API AFTER the model is declared
BulletinsViewModel.showBulletins()
}
exports.onNavigatingTo = onNavigatingTo;
function onListViewLoaded(args) {
const listView = args.object;
}
exports.onListViewLoaded = onListViewLoaded;
function onItemTap(args) {
const index = args.index;
console.log(`Second ListView item tap ${index}`);
}
exports.onItemTap = onItemTap;
function onDrawerButtonTap(args) {
const sideDrawer = app.getRootView();
sideDrawer.showDrawer();
}
exports.onDrawerButtonTap = onDrawerButtonTap;
bulletins-view-model.js
const observableModule = require("tns-core-modules/data/observable");
const SelectedPageService = require("../shared/selected-page-service");
const bulletinService = require("~/services/bulletin-service");
function BulletinsViewModel() {
SelectedPageService.getInstance().updateSelectedPage("Bulletins");
//declare the viewmodel
const viewModel = observableModule.fromObject({
//declare the properties of this viewmodel
bulletins: []
});
//declare a function to be called LATER during the navigation to the view
BulletinsViewModel.showBulletins = function () {
//call the fetch function and pass it the users info
bulletinService.allBulletins({
user: 'admin',
password: this.password
}).then((r) => {
console.log(r);
//pass the response to the properties of the model
viewModel.bulletins = r;
})
.catch((e) => {
console.log(e);
alert("Unfortunately we could not find any bulletins");
});
}
return viewModel;
}
module.exports = BulletinsViewModel;
bulletin-service.js
exports.allBulletins = function () {
return new Promise((resolve, reject) => {
fetch("https://got****.com/wp-admin/admin-ajax.php?action=all-bulletins-paged")
.then((response) => response.json())
.then((r) => {
if (r.total_bulls == 0) {
//console.log('No Bulletins Found' + r.total_bulls);
reject(r);
}
//console.log('JSON Bulletins Found' + JSON.stringify(r));
resolve(r);
}).catch((err) => {
console.log(err);
reject(err);
});
});
};