TODO apps with basic CRUD on local-settings with NativeScript - Part 2

TODO Apps - Part 2

In the part 1 tutorial, we've learned about how to build basic MVVM pattern in NativeScript on building simple TODO apps. For this tutorial, we will add something more cool stuffs in this apps, so that it can be called a "Todo apps".

Okay, first i need to show you how the app looks like when you finished this tutorial and like before, i will not explain detail about the coding, you can read it on the NativeScript docs.

Part 2 - Steps:
  1. Layout
    On previous tutorial, we only use listView to display the todo list, now we add header that contain app name and add button, here the todo.xml (i've changed the name from todo-page.xml, it's also for the todo-page.js) looks like:
    <Page navigatedTo="load"> <StackLayout> <GridLayout rows="50" style="background-color:#000000;"> <Label text="TODO lists" horizontalAlignment="left" style="color:#ffffff" /> <Button text="ADD" horizontalAlignment="right" style="color:#ffffff" tap="{{ promptTodo }}" /> </GridLayout> <ListView items="{{ todos }}" itemTap="{{ todoListTap }}"> <ListView.itemTemplate> <GridLayout rows="50"> <Label text="{{ name }}" horizontalAlignment="left" style="{{ complete ? 'color:#cccccc' : 'color:#000000'}}" /> </GridLayout> </ListView.itemTemplate> </ListView> </StackLayout> </Page> As you can see, i add title and add button, some inline style and couple new event handlers.

  2. Model
    We need to add one flag to the todo data so we can differentiate which one the todos are finished or not. and the flag is like so: // Todo.js function Todo( name ) {
    this.name = name;
    this.complete = false; //default value
    }
    module.exports = Todo;

  3. View Model
    We will making a lot of changes on the todoData.js. The code was like this: // todoData.js var observableModule = require( "data/observable" ),
    observableArray = require( "data/observable-array" ),
    Todo = require( "../shared/models/Todo" ),
    data = new observableModule.Observable();
    data.set( "todos", new observableArray.ObservableArray([
    new Todo( "Wake up" ),
    new Todo( "Coding TODO app with NativeScript" ),
    new Todo( "Publish it on blog" ),
    new Todo( "Eat then Sleep" )
    ]));
    module.exports = data;
    And it's only load some default todo lists.

    First, we need to load UI dialogs module, ui/dialogs, for using as CRUD user interfaces and the second one we need to store the TODO list of data locally, since i've no idea yet about how to use SQLite as an offline database in NativeScript, so i will use local-settings module to replace it, and we will be fine since the todo data is simple.
    And here is how i stored it and set it up to the view from the local-settings:var todoLists = localSettings.getString('todoLists'); if(typeof todoLists == "undefined"){
    localSettings.setString('todoLists', JSON.stringify(defaultValues));
    }
    data.set( "todos", new observableArray.ObservableArray(JSON.parse(todoLists)));
    Since the local-settings is not support Array/object datatype, so i've to parse it into the string with JSON.stringify and when it come to the view, i parse it back to the json.
    And now we need to figure it out, how to add a new todo list to the application, and here the line of code that i used:data.promptTodo = function(args) { dialogs.prompt({ title: "Add Todo", message: "Enter your todo name:", okButtonText: "Add", cancelButtonText: "Cancel", defaultText: "", inputType: dialogs.inputType.text }).then(function (promptResult) { if (promptResult.result) { defaultValues = JSON.parse(todoLists); var newTodo = new Todo(promptResult.text); defaultValues.push(newTodo);
    localSettings.setString('todoLists', JSON.stringify(defaultValues)); todoLists = localSettings.getString('todoLists'); data.set( "todos", new observableArray .ObservableArray(JSON.parse(todoLists))); } }); }
    You might notice that the name promptTodo is same with the on the view (todo.xml) under the tap attribute, and yes it's an event handler that using tap gesture, so whenever the ADD button is tap, it'll run this code which is will trigger the prompt dialogs to popup and when press add button, it will save it to the local-settings, just like the one did it with the default values.

    We've add CREATE feature to the Todo apps, next we will add the rest, which is UPDATE and DELETE in one event handler, so the scenario is when the user tap on one of Todo lists, it will shows action dialogs that have DONE, EDIT , DELETE, CANCEL buttons in it and that can be achieved by binding the parent of ListView with itemTap, it can return which index list that has been clicked and the index is represent the array of the todo list data that loaded to that ListView. And here is the code looks like:
    data.todoListTap = function(args){ var actionBtns = ["Done", "Edit", "Delete"]; todoLists = localSettings.getString('todoLists'); defaultValues = JSON.parse(todoLists); var currentList = defaultValues[args.index]; if(currentList.complete){ actionBtns = ["Undone", "Edit", "Delete"]; } dialogs.action({ message: "Update Your TODO status:", cancelButtonText: "Close", actions: actionBtns }).then(function (result) { if(result=='Delete'){ dialogs.confirm({ title: currentList.name, message: "Are you sure want to delete it?", okButtonText: "Yes", cancelButtonText: "Cancel", }).then(function (res) { if(res){ defaultValues.splice(args.index,1); localSettings.setString('todoLists', JSON.stringify(defaultValues)); todoLists = localSettings.getString('todoLists'); data.set( "todos", new observableArray.ObservableArray(JSON.parse(todoLists))); } }) }else if(result == 'Edit'){ dialogs.prompt({ title: "Edit", message: "Todo edit form:", okButtonText: "Update", cancelButtonText: "Cancel", defaultText: currentList.name, inputType: dialogs.inputType.text }).then(function (promptResult){ if (promptResult.result) { defaultValues[args.index].name = promptResult.text; localSettings.setString('todoLists', JSON.stringify(defaultValues)); todoLists = localSettings.getString('todoLists'); data.set( "todos", new observableArray.ObservableArray(JSON.parse(todoLists))); } }) }else if(result == 'Done'){ dialogs.confirm({ title: currentList.name, message: "Are you sure this todo is done?", okButtonText: "Yes", cancelButtonText: "Cancel", }).then(function (res) { if(res){ defaultValues[args.index].complete = true; localSettings.setString('todoLists', JSON.stringify(defaultValues)); todoLists = localSettings.getString('todoLists'); data.set( "todos", new observableArray.ObservableArray(JSON.parse(todoLists))); } }); }else if(result == 'Undone'){ dialogs.confirm({ title: currentList.name, message: "This todo ain't done yet?", okButtonText: "Yep", cancelButtonText: "Cancel", }).then(function (res) { if(res){ defaultValues[args.index].complete = false; localSettings.setString('todoLists', JSON.stringify(defaultValues)); todoLists = localSettings.getString('todoLists'); data.set( "todos", new observableArray.ObservableArray(JSON.parse(todoLists))); } }); } }); } And there we go, your TODO apps will up and running, you can add, edit, delete the todo and it all save it in your local memory.
Repo

If you have no idea what the complete code looks like or have an error so the app can't running, you can just clone my repo https://github.com/cendekia/todo-nativescript

Happy Coding...

Cendekia P Putra

< Software Developer /> | @cendekiapp | me@cendekiapp.com

Jakarta, Indonesia http://cendekiapp.com