I am using Alloy Titanium and I wanted to do something like this :
I have a model, view and controller, this is the view index.xml -
<Alloy>
<Model src="post" instance="true" id="postIns"/>
<Window class="container" onSwipe="update" model="$.postIns">
<Label id="postTitle" top="15">{$.postIns.title}</Label>
<Label id="postContent">{$.postIns.body}</Label>
<Button id="updateButton" onClick="update" bottom="0">Zemi nov post</Button>
</Window>
this is the model - post.js -
exports.definition = {
config: {
"defaults": {
"userId": "",
"id": "",
"title": "Title",
"body": "",
},
adapter: {
type: "properties",
collection_name: "post"
}
},
extendModel: function(Model) {
_.extend(Model.prototype, {
// extended functions and properties go here
});
return Model;
},
extendCollection: function(Collection) {
_.extend(Collection.prototype, {
// extended functions and properties go here
});
return Collection;
}
};
and my controller index.js that connects to a fake api and fills the instance of the model -
var id = 1;
function update() {
id =_.random(0, 50);
var results = {};
var client = Ti.Network.createHTTPClient({
// called when the response data is available
onload : function(e) {
results = JSON.parse(client.responseText);
// display results on console
Ti.API.info(JSON.stringify(results,null,2));
// save the results to the instance
$.postIns.save(results);
},
// called when an error occurs, including a timeout
onerror : function(e) {
results = JSON.parse(client.responseText);
// display error results on the console
//Ti.API.err(JSON.stringify(results,null,2));
},
});
var url = "http://jsonplaceholder.typicode.com/posts/" + id;
client.open("GET", url);
client.send();
}
$.index.open();
Now let's say I wanted to make another view file .xml with a different window, how would I go about using the same instance of the post model in that window?
P.S. I am pretty sure that the model instance I made is local, but I am interested in a solution about binding a model to more windows.
You can check the titanium docs which clearly explains about the global singleton instance of the model and I think you will be able to use it through out.
Check out the Titanium doc words :
You can also create a global singleton instance of a model, either in markup or in the controller, which may be accessed in all controllers. Use the Alloy.Models.instance method with the name of the model file minus the extension as the only parameter to create or access the singleton.
// This will create a singleton if it has not been previously created,
// or retrieves the singleton if it already exists.
var book = Alloy.Models.instance('book');
Hope it gives some idea.
Related
I made a website to create some maps. So, all my maps have an id, and my map has some elements with ids.
So I create a collection Map with an id and its model is my map object :
app.collections.mapDetailsCollection = Backbone.Collection.extend({
model: app.models.mapDetailsModel,
initialize: function(options) {
this.id = options.id;
},
url: function () {
return this.absURL + '/api/maps/' + this.id;
},
parse: function(response){
return response.features;
},
toGeoJSON: function(){
var features = [];
this.models.forEach(function(model){
var feature = model.toGeoJSON();
if (! _.isEmpty(feature.geometry)) {
features.push(feature);
}
});
return {
'type': 'FeatureCollection',
'features': features
};
}
});
But my model have an id too. I don't know for a model how to return url with a collection id.
I need to return /api/maps/id_collection/id_model.
When a model is added to a collection, it receives a reference to the collection. So each model has a collection property this.collection set if it's in a collection.
From the Backbone model constructor documentation (emphasis mine):
If you pass a {collection: ...} as the options, the model gains a
collection property that will be used to indicate which collection the
model belongs to, and is used to help compute the model's url. The
model.collection property is normally created automatically when you
first add a model to a collection. Note that the reverse is not true,
as passing this option to the constructor will not automatically add
the model to the collection. Useful, sometimes.
Then, you could use that to build the full url of a model:
var app.models.mapDetailsModel = Backbone.Model.extend({
urlRoot: function() {
// Backbone adds the model id automatically
return this.collection.url() + '/my-model/';
}
// ...snip...
});
Note that the default url for a model is what you want.
Generates URLs of the form: [collection.url]/[id] by default, but
you may override by specifying an explicit urlRoot if the model's
collection shouldn't be taken into account.
I have problem with my Backbone mobile app (using Cordova, Require, Handlebars, etc..).
In particular the console log of chrome give me this result:
.
I've tried different solutions taken by debugging and searching on the web.
The first are defining the urlRoot in my Model.backbone and calling the function mymodel.fetch(). And the result given is: .
This is the code:
var MyModel = Backbone.Model.extend({
constructorName: "MyModel",
urlRoot: 'http://192.168.56.101/XXX/api/categories/26?io_format=JSON'
});
myView: function() {
var model = new MyModel();
model.fetch();
// create the view
var page = new MyView({
model: model
});
// show the view
this.changePage(page);
},
The second solution is embedding the API KEY in the urlRoot. Like that:
var MyModel = Backbone.Model.extend({
constructorName: "MyModel",
urlRoot: 'http://IYI6M35MLB8UVW38fhj9RY3YPQWRX5X8H#192.168.56.101/XXX/api/categories/26?io_format=JSON'
});
The last solution that we tried was passing the header in the fetch call. Like this code:
myView: function() {
var model = new MyModel();
model.fetch({
headers: {
'Authorization': 'Basic IYI6M35MLB8UVW38Y99RrgsrPQWRX5X8H:'
}
});
// create the view
var page = new MyView({
model: model
});
// show the view
this.changePage(page);
},
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
All of this solutions give me the same result, like "Chrome Console LOG", he sad me "Unauthorized etc.." like the image.
Can you give me some help? How can I bring JSON object in a model in Backbone using API with authentication by Username?
var obj = {
conn : null,
first : function(thisIdentity) {
"use strict";
var myObj = this;
$(document).on('click', thisIdentity, function(e) {
e.preventDefault();
$.ajax ({
url : some value,
// other parameters
success : function(data) {
myObj.conn = new Connection(data.user_id, "127.0.0.1:80");
sessionStorage.setItem('connection', JSON.stringify(myObj.conn));
}
});
},
second : function(thisIdentity) {
"use strict";
var myObj = this;
var conntn = sessionStorage.getItem('connection');
$(document).on('click', thisIdentity, function(e) {
e.preventDefault();
$.ajax ({
url : some value,
// other parameters
success : function(data) {
var parsedConnection = JSON.parse(conntn);
parsedConnection.sendMsg(data.id, data.nid);
}
});
}
};
var Connection = (function() {
function Connection(uid, url) {
this.uid = uid;
this.open = false;
this.socket = new WebSocket("ws://"+url);
this.setupConnectionEvents();
},
Connection.prototype = {
sendMsg : function(id, nid) {
alert("Working");
},
// other functions
}
})();
So connection is made in the AJAX callback function of first and I store the object in the sessionStorage via JSON but when I use it in the AJAX callback of second then error is coming that
TypeError: parsedConnection.sendMsg is not a function
Now I understand that may be it is because JSON can be used to store plain objects not prototype-based objects.
My question is : Can any one tell me how to store prototype-based objects via JSON or any other way to implement this?
I don't want to use eval. Any code, reference would be much appreciated. Thanks!
UPDATE
I did as #Dan Prince mentioned but then a new problem occurred that now when in sendMsg function I use
this.socket.send(JSON.stringify({
action: 'message',
rec: receiver,
msg: message
}));
Then it stays
InvalidStateError: An attempt was made to use an object that is not,
or is no longer, usable
Any inputs? Thanks!
You could probably hack your own solution into place by storing the prototype as a property of the object, then reinstantiating it with Object.create after you read it, but the real question is why do you want to do this in the first place?
I would suggest writing a serialize method on Connection's prototype, which exposes only the essential information (there's no sense serializing a web socket for example).
Connection.prototype.toJSON = function() {
return JSON.stringify({
uid: this.uid,
url: this.url,
open: this.open
});
};
Then use this method when you save the connection object into session storage.
myObj.conn = new Connection(data.user_id, "127.0.0.1:80");
sessionStorage.setItem('connection', myObj.conn.toJSON());
Each saved connection now has the minimum amount of data you need to call the constructor and recreate the instance.
When you load a connection from session storage, parse it and pass the values back into the constructor.
var json = sessionStorage.getItem('connection');
var data = JSON.parse(json);
var connection = new Connection(data.uid, data.url)
// ...
connection.sendMsg(data.id, data.nid);
This will recreate the correct prototype chain in a natural and predictable way.
It's hard to see exactly what you are trying to achieve in every respect, but let's assume :
for various DOM elements, a click handler (delegated to document) will cause asynchronously derived data to be sent via socket.send().
the socket is to be initialized with an asynchronously derived uri.
the socket is to be kept available for immediate reuse.
data by which the socket is initialized is to be cached in local storage for future sessions. (It makes no sense to store the socket itself).
In addition, we need to acknowledge that a socket consume resources should really be disposed of if its resuse is not immediate.
The whole strategy is abnormally complex. The overhead of performing an ajax operation once per session to obtain a uri would typically be accepted, as would the creation of a socket each time one is needed. However, it's an intersting exercise to write something with all the stated characteristics.
This may not be 100% correct but could possibly give you some ideas, including the use of promises to cater for several asynchronisms. Here goes ...
var obj = {
conn: null,
init: function(thisIdentity) {
// It makes sense to attach the click handler only *once*, so let's assume this is an init function.
"use strict";
var myObj = this;
$(document).on('click', thisIdentity, function(e) {
e.preventDefault();
$.ajax ({
url : some value,
// other parameters
}).then(function(data) {
myObj.send(JSON.stringify({
'id': data.id,
'nid': data.nid
}));
});
});
},
send: function(data) {
"use strict";
var myObj = this;
return myObj.getSocket().then(function(socket) {
socket.send(data);
}).then(function() {
// by disposing in later event turn, a rapid series of send()s has at least a chance of using the same socket instance before it is closed.
if(socket.bufferedAmount == 0) { // if the socket's send buffer is empty, then dispose of it.
socket.close();
myObj.conn = null;
}
});
},
getSocket: function() {
"use strict";
var myObj = this;
//1. Test whether or not myObj.conn already exists ....
if(!myObj.conn) {
//2 .... if not, try to recreate from data stored in local storage ...
var connectionData = sessionStorage.getItem('connectionData');
if(connectionData) {
myObj.conn = myObj.makeSocket(connectionData.user_id);
} else {
//3. ... if connectionData is null, perform ajax.
myObj.conn = $.ajax({
url: some value,
// other parameters
}).then(function(data) {
sessionStorage.setItem('connectionData', JSON.stringify(data));
return myObj.makeSocket(data.user_id);
});
}
}
return myObj.conn; // note: myObj.conn is a *promise* of a socket, not a socket.
},
makeSocket: function(uid) {
"use strict";
var myObj = this;
var uri = "127.0.0.1:80"; // if this is invariant, it can be hard-coded here.
// return a *promise* of a socket, that will be resolved when the socket's readystate becomes OPEN.
return $.Deferred(function(dfrd) {
var socket = new WebSocket("ws://" + uri);
socket.uid = uid;
socket.onopen = function() {
myObj.setupConnectionEvents();// not too sure about this as we don't know what it does.
dfrd.resolve(socket);
};
}).promise();
}
};
Under this scheme, the click handler or anything else can call obj.send() without needing to worry about the state of the socket. obj.send() will create a socket if necessary.
If you were to drop the requirement for storing data between sessions, then .send() and .getSocket() would simplify to the extent that you would probably choose to roll what remains of .getSocket() into .send().
I am running this code in backbone which saves some data to the server,
GroupModalHeaderView.prototype.save = function(e) {
var $collection, $this;
if (e) {
e.preventDefault();
}
$this = this;
if (this.$("#group-name").val() !== "") {
$collection = this.collection;
if (this.model.isNew()) {
console.log("MODEL IS NEW");
this.collection.add(this.model);
}
return this.model.save({ name: this.$("#group-name").val()}, {
async: false,
wait: true,
success: function() {
//console.log($this, "THIS");
console.log('Successfully saved!');
this.contentView = new app.GroupModalContentView({
model: $this.model,
collection: $this.collection,
parent: this
});
this.contentView.render();
return $this.cancel();
},
});
}
};
This works fine the first time I run it, however if I run it again straight after saving my first piece of data it does not save new data it merely updates the last saved data. So the first time I save it runs a POST request and the next time it runs a PUT request, why would this be?
I am not sure if you need this but here is my initialise function -
GroupModalHeaderView.prototype.initialize = function() {
_.bindAll(this)
}
Your view has a model object attached to it. As I understand you fill some forms, put their data to model and save the model. But all the time you have single model object, and you only update it's data. If you want to create a new object after saving model just add a line:
this.model = new YourModelClass();
right after line console.log('Successfully saved!');
From the backbone documentation:
If the model isNew, the save will be a "create" (HTTP POST), if the
model already exists on the server, the save will be an "update" (HTTP
PUT).
If you want to make a post request even if the model is new, just override the default save implementation and call Backbone.sync(method, model, [options]) with 'create' as the passed method.
I'm having trouble accessing the properties of a simple object.
This is what I get when I run alert(JSON.stringify(user)):
{"username": "david", "biography": "Hello world."}
But this is what I get when I run alert(user.username):
undefined
I've also tried user["username"] with the same result. This is in the context of a Backbone application using Handlebars. Here is the Backbone part:
var User = Backbone.Model.extend({
urlRoot: 'http://api.example.com/user',
});
var Router = Backbone.Router.extend({
routes: {
":username": "profile"
},
profile: function (username) {
var user = new User({id: username});
user.fetch({
beforeSend: authenticate,
success: function() {
var profile = new Profile({user: user});
profile.render();
}
});
}
});
var Profile = Backbone.View.extend({
render: function() {
var source = $("#profile").html();
var template = Handlebars.compile(source);
user = this.options.user;
var html = template(user);
$("#content section").html(html);
}
});
What might be the cause such an issue?
if you user object is a backbone model, then try this
console.log(user.get('username'));
or
cosole.log(user.attributes.username);
This is working for me
var user = {"username": "david", "biography": "Hello world."};
alert (user.username);
alert (user["username"]);
JSON.stringify will look for a toJSON function and use the return value of that as the JSON it uses.
Backbone probably does some fancy coding to make it easier to use its model objects. Since they don't think you want all the junk they put in, they provided a clean toJSON() result for you.
#Rayweb_on has the rest of the answer.