📜 ⬆️ ⬇️

Everything you wanted to know about models and collections in Titanium

But for some reason they were afraid to ask.

Models - quite an important part of the application, because without data nowhere.

In this article I will try to highlight in detail all aspects of using models in the MVC framework for developing mobile applications Appcelerator Titanium.
')
If you have not tried to contact the models, then I hope this article will save you a couple of kilometers of nerves.

Backbone.js


So the first thing to know when working with models in Titanium is that they are based on models and collections from Backbone. This means, firstly, you can use all the properties and methods described in the Backbone documentation; secondly, it will not be very superfluous to get acquainted with her, if you have not already done so.

Just in case, I will focus on terminology: a model is a single entity, a collection is a collection of similar models.

Important: Titanium uses Backbone version 0.9.2, so some methods from the Backbone documentation may not be implemented.

Underscore.js


Collections also inherit Underscore.js methods to make a developer’s life easier. For example, the .map method

Creature


You can create a model in two equivalent ways: using Appcelerator Studio or manually.
You can manually create a custom file in the <project> / app / models / folder (which the studio actually does for you):
Model Description File Format

exports.definition = { config : { //   }, extendModel: function(Model) { _.extend(Model.prototype, { //  Backbone.Model }); return Model; }, extendCollection: function(Collection) { _.extend(Collection.prototype, { //  Backbone.Collection }); return Collection; } } 


If you are using a studio, simply call the context menu of the project and select the item “New -> Alloy Model”. When creating a dialog box prompts you to choose an adapter type: localStorage, properties or sql. Adapter types are described below.

All you need to describe in this file for basic functionality is the config block. It contains the data schema and type of sync adapter. This block should contain the following fields:

For example, we create a model of books. To do this, create the books.js file in the <project> / app / models / folder:
An example of a created model

 exports.definition = { config: { "columns": { "title": "String", "author": "String" //    sql,     SQLite keywords  }, "defaults": { "title": "-", "author": "-" }, "adapter": { "type": "sql", "collection_name": "books", "idAttribute" : "title", "db_file" : "db_books", //   ,    .   _alloy_ "db_name" : "books.sqlite", //      /app/assets.   ,    [_].sqlite } } } 


Here we have a model with two text fields: the name of the book and the author. Both fields default to "-". So far, everything is simple.

The adapter type sql says that when trying to get data, the model will use the built-in adapter to try to connect to the SQLite database on the device and retrieve data from the books table.
Own properties and methods in the model can be added as follows:
Model Extension Example

 exports.definition = { config : { //   }, extendModel: function(Model) { _.extend(Model.prototype, { //  Backbone.Model customProperty: 'book', customFunction: function() { Ti.API.info(',  '); }, }); return Model; } } 


The same goes for collections. If you add your own methods to extendModel, they will be available in the model, but the collection does not, and vice versa.

Using


There are two ways to access the model / collection:

Global singleton


In this case, the model will be available immediately everywhere, in all controllers. If it is already declared in one, then in another the method call Alloy.Models.instance will return a reference to this declared model. An example is the User model, it can be the same everywhere.

Similarly for collections.

Local link


You can create a local reference using the Alloy.createModel method. Then this model will be available only inside the controller where it was created. The method is a factory, transfer to it the model name (file name minus .js) and parameters. And that's all.

Similarly for collections.

XML


The <Model> markup element must be a direct descendant of <Alloy>. And it must have an src attribute with the value “model-filename-model-minus-.js”. Then a singleton of this model will be available in the controller in the Alloy.Models namespace:
XML

 <Alloy> <Model src="book" /> <Window> <TableView id="table" /> </Window> </Alloy> 


Controller

 var drama = Alloy.Models.book; drama.set('title', '  '); drama.set('author', ' '); 


This element also has an instance attribute. If it is true, a link to the model will be created (local, available only within one controller). By the way, in this case, the model will not have access in the Alloy.Models namespace, it will need to be addressed by id:
XML

 <Alloy> <Model id="myBook" src="book" instance="true"/> <Window> <TableView id="table" /> </Window> </Alloy> 


Controller

 var drama = $.myBook; drama.set('title', '  '); drama.set('author', ' '); 


Similarly for collections.

Sync adapters


Finally we got to the section, which was promised in the "Creation" section. To work with models it is not enough just to describe the data scheme. We still need to connect them to something. This connection is the sync adapter.
The adapter is an implementation of Backbone.sync. Since the most common task is CRUD operations on a remote server (well, personally from my experience), by default the Backbone.sync method makes RESTful JSON requests for the addresses that you specify in the Model.urlRoot and Collection.url properties. So says the official documentation. In practice, in order to make sense of the models / collections, it is very convenient to use the adapter restapi, which I will talk about later. For now, let's return to the official documentation.

Adapters can be of four types:


SQL


If the idAttribute key is not defined in the config section of the adapter, then Alloy itself will generate the alloy_id field in your table and will use it as the primary key.

Pay attention to the db_file and db_name config fields (in the "Creation" section). Here they are important.
Unlike other types, in this method Backbone.Collection.fetch can accept a whole SQL query string:
Query string in .fetch ()
 var library = Alloy.createCollection('book'); //   -  collection_name   var table = library.config.adapter.collection_name; //   library.fetch({query:'SELECT * from ' + table + ' where author="' + searchAuthor + '"'}); // prepared statement library.fetch({query: { statement: 'SELECT * from ' + table + ' where author = ?', params: [searchAuthor] }}); 


That is, if you want a simple query, pass an object with the query key in the parameters, and if you want a complex query, pass both the query and the parameters.

By the way, you can still pass the key id. In this case, the adapter itself will understand that you want to use the WHERE id =? Restriction.
id
 myModel.fetch({id: 123}); //   ,  myModel.fetch({query: 'select * from ... where id = ' + 123 }); 



properties


If your database is not SQLite, then you can create your adapter and do whatever you want with the data. To do this, create an adapter file in the app / assets / alloy / sync or app / lib / alloy / sync directory, for example, myAdapter.js, and then specify the name of this file, that is, "myAdapter", in the model's config.type.
Actually this file should export three functions:


restapi


If you need to receive data from a remote server, and you don’t want to create your own adapter, there is a wonderful restapi adapter, which is not included in the number of built-in ones, but does its job (RESTful JSON requests) with a bang.
The minimum required settings are the URL field in the config file:
Sample model with restapi adapter
 exports.definition = { config: { "URL": "http://example.com/api/modelname", //"debug": 1, "adapter": { "type": "restapi", "collection_name": "MyCollection", "idAttribute": "id" } }, extendModel: function(Model) { _.extend(Model.prototype, {}); return Model; }, extendCollection: function(Collection) { _.extend(Collection.prototype, {}); return Collection; } } 


Of course, this is not all available settings, for a full list, see the documentation. What is more important in this adapter is that the fetch method in it accepts a dictionary with three possible fields:


Conclusion


So, this is all you need to know in order to be able to work with models in Titanium at a basic level. This topic is somewhat broader, for example, I deliberately omitted here a description of the migration process between different versions of the database, data binding, using models without Alloy, and working with SQLite. I hope to fill all these gaps in the next article, stay tuned.

This article is based on a) official documentation; b) own experience of using models in Alloy. If among the community there are people who understand this issue better than me and see errors or inaccuracies, please comment, discuss.

Source: https://habr.com/ru/post/272049/


All Articles