Ember array serialization - arrays

I'm adding objects to an array property of a model, then saving it. When I look at the outgoing request, the property in question is always an empty array.
My custom serializer (extending Ember.RESTSerializer) has this:
DS.ArrayTransform = DS.Transform.extend(
{
deserialize: function(serialized)
{
return (Ember.typeOf(serialized) == "array") ? serialized : [];
},
serialize: function(deserialized)
{
var type = Ember.typeOf(deserialized);
if (type == 'array')
{
return [{foo:'bar'}];
// return deserialized;
}
else if (type == 'string')
{
return deserialized.split(',').map(function(item)
{
return item.trim();
});
}
else
{
return [];
}
}
});
App.register("transform:array", DS.ArrayTransform);
As you can see I've tried passing back an arbitrary array with an object in it, but even then the array always comes out as empty. In the app I create the record like this:
var post = this.store.createRecord('explorePost', {
title: content.get('title'),
text: content.get('text'),
postDate: content.get('postdate'),
publishDate: content.get('publishDate'),
published: content.get('published'),
postType: content.get('postType'),
link: content.get('link,'),
projectDownloads: [],
// projectDownloads: this.model.get('projectDownloads'),
projectLinks: content.get('projectLinks'),
});
then add the objects like this:
this.model.get('projectDownloads').forEach(function (_download) {
console.log('pushing download', _download);
post.get('projectDownloads').pushObject(_download);
});
I can confirm that at time of saving, the post object has a projectDownloads array with one object in it. No matter what I do I can't seem to get it to spit out the contents when it saves. It's definitely going into the custom serializer, and detects it as an array, but you can see something else seems to be overriding it.
Can anyone tell me what I'm doing wrong? My model setup is below:
App.ExplorePost = DS.Model.extend(
{
title: DS.attr('string'),
text: DS.attr('string'),
link: DS.attr('string'),
postDate: DS.attr('momentdate'),
publishDate: DS.attr('momentdate'),
user: DS.belongsTo('user',{async:true}),
postType: DS.attr('string'),
activity: DS.belongsTo('activity',{ inverse: 'explorePost', async:true}),
comments: DS.hasMany('comment',{ inverse: 'explorePost', async: true }),
// projectDownloads: DS.hasMany('projectDownload',{ inverse: 'explorePost'}),
projectDownloads: DS.attr('array'),
// projectLinks: DS.hasMany('projectLink',{ inverse: 'explorePost'}),
projectLinks: DS.attr('string'),
published: DS.attr('boolean', {defaultValue: true}),
// tags: DS.hasMany('tag')
sortdate: function()
{
var datestr = Ember.isEmpty(this.get('postDate')) ? '' : moment(this.get('postDate')).format('YYYYMMDDHHmm');
var fn = (datestr + '____________').slice(0, 12);
return fn;
}.property('postDate')
});

There's no built in DS.attr('array') and a naive implementation would probably not know how to serialize ember-data objects found inside. Did you intend to leave that in there? If you swap it back to the relationships you've commented out and change projectDownloads to work with the promise:
this.model.get('projectDownloads').then(function(downloads) {
downloads.forEach(function(_download){
post.get('projectDownloads').pushObject(_download);
});
});
This should work jsut fine. I put together something nearly identical the other day. http://emberjs.jsbin.com/zolani/3/edit?html,css,js,output

if you array not contain complex object, like array of string, you can use DS.attr(), it will work.

Related

excluding from an object angular ng-change

Hello guys i am trying to clear my ng-model when i fire my change function but my problem that i don't want to delete all i want to exclude one item in the object.
function change() {
if (vm.location.type) {
angular.forEach(vm.location, function (value, index) {
delete vm.location;
});
}
}
so i don't want to delete the
vm.location.type
my
vm.location
has
vm.location.boundaries;
vm.location.region;
vm.location.address;
vm.location.name;
vm.location.nonVisitingRadius;
vm.location.visitingRadius;
See the code below,
var obj = { a:123, b:123, c:123 }
delete obj.a;
Hence obj will be like this {b:123, c:123}
Note: Dont need any for loop to delete property from object
Updated Answer:
var obj= {
a: 'aaa',
b: 'bbb',
c: 'ccc',
d: 'ddd'
};
var removeObj = function(obj, props) {
for(var i = 0; i < props.length; i++) {
if(obj.hasOwnProperty(props[i])) {
delete obj[props[i]];
}
}
};
removeObj (obj, ["a", "d"]);
i don't know if i understand correctly what you're asking, but if you want to clear all the fields of your object preserving only type and preserving the reference of the object with plain javascirpt (no libraries), loop over the fields and check if the i field is equal to type.
for(var i in model.location){
if(i !== 'type')
delete model[i];
}
with underscore.js you could define a default model like:
var defaultModel = {
location: {
region: '',
address: '',
name: '',
nonVisitingRadius: '',
visitingRadius: '',
type: 'defaultvalue'
}
}
and when ng-change is triggered inside the function
_.extend(model, defaultModel);
that will keep the default value for type and clear all the others.
You can do it this with a temporary object :
let tempLocation = {};
tempLocation.type = $scope.location.type;
$scope.location = tempLocation;

How to write structured data with JSON writer?

How can I include hasOne associated model data in the JSON POST?
Structured data is required by my web API in the form of:
{
id: 1234,
name: 'Aaron Smith',
address: {
address1: '1925 Isaac Newton Sq',
address2: 'Suite 300',
city: 'Reston',
state: 'VA',
zip: 20190
}
}
#nonino
I think I know how to do it but I am also having a similar problem. I can't actually get my associations to give me the associated data. Anyway from what I have scrounged on the internet make a custom writer like this or just in the default writers getRecordData: function(record,operation)
Here is my custom writer
Ext.define('Wakanda.writer', {
extend: 'Ext.data.writer.Json',
// alternateClassName: 'SimplyFundraising.data.WakandaWriter',
alias: 'writer.wakanda',
writeAllFields: false,
getRecordData: function(record,operation) {
debugger;
Ext.apply(record.data,record.getAssociatedData());
debugger;
var isPhantom = record.phantom === true,
writeAll = this.writeAllFields || isPhantom,
nameProperty = this.nameProperty,
fields = record.fields,
data = {},
changes,
name,
field,
key;
if (writeAll) {
// console.log("getRecordData1", this, arguments);
fields.each(function(field){
if (field.persist) {
debugger;
name = field[nameProperty] || field.name;
data[name] = record.get(field.name);
} else {
}
});
} else {
changes = record.getChanges();
debugger;
// console.log("getRecordData2", this, arguments, changes);
for (key in changes) {
if (changes.hasOwnProperty(key)) {
field = fields.get(key);
name = field[nameProperty] || field.name;
data[name] = changes[key];
}
}
if (!isPhantom) {
debugger;
data[record.idProperty] = record.getId();
if(operation.action !== 'destroy'){
data[record.stampProperty] = record.get(record.stampProperty);
}
}
}
return {'__ENTITIES': [data]};
}
});
The key I think is in the getRecordData where I have a statement Ext.apply(record.data,record.getAssociatedData()); If record.getAssociatedData does indeed return your data then the Ext.apply statement will merge your current record.data with your record.getAssociatedData into 1 json file. At least this is what I hope happens. Can't test until I get my associations setup correctly.
Hope this helps,
Dan
getRecordData: function(record,operation) {
debugger;
Ext.apply(record.data,record.getAssociatedData());
debugger;

Check input value against an array

I am using this plugin for my autocomplete form:
http://www.planbox.com/blog/news/updates/jquery-autocomplete-plugin-for-backbone-js.html
Instead of checking only one item, as in the code below (if (inputVal == 'bakaxel')),
I would like to check the selected value against the entire collection
var collection = new Backbone.Collection([
{id:"AB", name:"Alberta"},
{id:"AD", name:"Album"},
{id:"BA", name:"barn"},
{id:"BC", name:"bak"},
{id:"BD", name:"baby"},
{id:"BE", name:"band"},
{id:"BF", name:"bakaxel"},
{id:"BG", name:"batteri"},
{id:"BH", name:"barbie"},
{id:"MB", name:"Manitoba"},
{id:"AP", name:"Armed Forces Pacific"}
]);
$('input.search').autocomplete({
collection: collection,
attr: 'name',
noCase: true,
ul_class: 'search_options tr_list',
ul_css: {'z-index':1234}
});
$('input.search').each(function(){
$(this).blur(function(){
var inputVal = $('input.search').val();
if (inputVal == 'bakaxel') {
$('#search_result_page').load('searchResult.html');
$('#searchPage').addClass('hidden');
}
});
});
I tried this, but I'd rather not create the ar array again, just use the backbone collection:
$('input.search').each(function(){
$(this).blur(function(){
var inputVal = $('input.search').val();
var ar = ["Alberta", "Album", "barn", "bak", "baby", "band", "bakaxel", "batteri", "barbie", "Manitoba", "Armed Forces Pacific"];
if (jQuery.inArray(inputVal, ar) != -1) {
$('#search_result_page').load('searchResult.html');
$('#searchPage').addClass('hidden');
}
});
});
Backbone proxies Underscore functions and most notably in your case http://underscorejs.org/#where
where _.where(list, properties)
Looks through each value in the list, returning an array of all the values that contain all of the
key-value pairs listed in properties.
Your test could be written as
var matches = collection.where({
name: inputVal
});
if (matches.length>0) {
...
}
Or as #mu suggested in the comments, you could just check the existence of the input with http://underscorejs.org/#find
var found = collection.find(function(model) {
return model.get('name') === inputVal
});
if (found) {
...
}

Store/push to an array in a Backbone Model

I have this model defaults:
test.Models.ItemModel = Backbone.Model.extend({
defaults: {
name: 'an item',
units: []
},
Which I then use the following code to set the Model:
addUnit: function(e){
if(e.keyCode == 13){
this.model.set({ 'units' : this.model.get('units').push($('#addUnit').val()) },
{success: function(){
this.render();
}}
);
}
},
However, it never seems to get added to the Model array, am I doing things right here??
The problem is that you're assuming the the push method is returning the whole array; instead, as stated here, the push method
Returns the new length property of the object upon which the method was called.
So, you need to push the element into the array before you set it to the model :
var _units = this.model.get('units');
_units.push($('#addUnit').val());
this.model.set({ 'units' : _units });
Careful though, this will modify anything else that points to this array, so if you do this, for example:
var myArray = [1,2,3]
this.model.set({units: myArray})
var _units = this.model.get('units')
_units.push(4)
this.model.set({ 'units' : _units })
myArray == this.model.get('units') // holy moly, they're the same :(
If you want to avoid this, or still want to use a single line of code for that, you could use array's concat method:
this.model.set({
'units' : this.model.get('units').concat($('#addUnit').val())
});

how can I update a model with custom idAttribute

in my simple backbone application, I am trying to update a model and every time it send a put request instead of post.
Well, this is my model named categoryModel
define(['Backbone'], function (Backbone) {
var CategoryModel = Backbone.Model.extend({
defaults: {
ID: '',
Name: 'Empty',
TagID: '0',
GID: '0'
},
idAttribute: "ID",
initialize: function () {
if (!this.get('Name')) {
this.set({ 'Name': this.defaults.Name });
}
}
});
return CategoryModel;
});
this is the collection
define(['Backbone','../../models/categories/categoryModel'], function (Backbone, categoryModel) {
var CategoryCollection = Backbone.Collection.extend({
url: '/parentcategory/Actions',
model: categoryModel
});
return new CategoryCollection;
});
here are my methods in the view
on a keychange event
createNewItem: function (e) {
var $this = $(e.currentTarget);
$('#selectedCategoryName').html($this.val());
//it creates a new model
globals.NewCategory = new CategoryModel({ Name: $this.val() });
}
on handleDrop event
handleDropEvent: function (event, ui) {
var draggable = ui.draggable;
//check if name has set
if (!globals.NewCategory) {
alert("Please write a category name");
$('#createNewCategory').focus();
return;
}
//get itemID
var itemID = draggable.attr("id").split('_')[1];
var itemDesc = draggable.attr("id").split('_')[0];
//check items category
if (itemDesc == "Tag") {
//check if tagID already exists
if (globals.NewCategory.TagID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("TagID", itemID);
} else if (itemDesc == "gTag") {
if (globals.NewCategory.GID) {
alert("you have already specify a tag from this category");
return;
}
globals.NewCategory.set("GID", itemID);
}
categoriesCollection.create(globals.NewCategory, {
silent: true,
wait: true,
success: function (model, response) {
model.set("ID", response);
alert(model.id);
}
});
}
The categoriesCollection.create is called twice. Firstly for setting the TagID (on a success request it gets an ID ) and secondly for setting the GID.
Since the ID has been set, shouldn't had sent a POST request instead of PUT on the second call?
What am I doing wrong?
Thanks
The standard behaviour is to send a POST if the model is new ( doesn't have an ID attributed ) and send a PUT if the model id is set.
In your case it's working as designed, if you want it to use POST to send UPDATES you have to override Backbone.sync to work as you need, but I think it's easier for you to make your backend RESTful and create a PUT listener controller method for updates.
Another thing, if I got it right you are using create() to update models in your collection, I would advise you not to do that and instead use the save() directly in the model you want to update, the code will be a lot more readable.
Cheers.

Resources