Underscore function in Backbone acting strangely - javascript

I have been working on a Rails app using Backbone on the frontend. It has two models, publication and article. I wrote the following function in the publications backbone router and it supposed to delete the specified publication and all it's related articles. It deletes the publication just fine but I am running into an issue when it comes to deleting the articles. Here is the code:
publications_router.js
delete_publication: function(id){
var publication = new SimpleGoogleReader.Models.Publication({id: id});
publication.fetch();
publication.destroy();
var articles = new SimpleGoogleReader.Collections.Articles();
articles.fetch({
success: function(data){
_.each(data.models, function(item){
if (item.toJSON().publication_id == id) {
console.log(item);
}
});
}
});
}
This works great for printing each item in the console. However, if I change the console.log(item) line to say:
item.destroy();
It will only destroy every other item. Obviously I would like to correct this so the function destroys all the items and not every other one.
Can anybody tell me why this is happening and what we can do to fix it?

First filter out the items you want to destroy.
var tobeDeleted = _.filter(data.models, function(item){
return (item.toJSON().publication_id == id);
});
... and then destroy
_.invoke(tobeDeleted, "destroy");

Related

Issue with multiple subscriptions on one publication

I have a page that generates a series of post templates, each of which subscribes to a single publication. I'm running into an issue where passing the post ID to my publication does not return any results. Can anyone help me figure out what's wrong?
I have tried using console.log() inside onCreated (both inside and outside of the autorun) to make sure that my IDs are correctly generated and have also tried other query values (such as {$exists:true}) to ensure that my query is working.
// client
CommentSubs = new SubsManager();
Template.post.onCreated(function() {
var self = this;
self.ready = new ReactiveVar();
self.autorun(function() {
var commentParent = this._id;
var handle = CommentSubs.subscribe('comments', commentParent);
self.ready.set(handle.ready());
});
});
// server
Meteor.publish("comments", function (commentParent) {
return Posts.find({commentParent: commentParent}, {sort: {createdAt: 1}});
});
I am new to meteor so perhaps I am missing something that should be obvious.

Two-way data binding for a Meteor app

I've built an app that is form-based. I want to enable users to partially fill out a form, and then come back to it at a later date if they can't finish it at the present. I've used iron router to create a unique URL for each form instance, so they can come back to the link. My problem is that Meteor doesn't automatically save the values in the inputs, and the form comes up blank when it is revisited/refreshes. I tried the below solution to store the data in a temporary document in a separate Mongo collection called "NewScreen", and then reference that document every time the template is (re)rendered to auto fill the form. However, I keep getting an error that the element I'm trying to reference is "undefined". The weird thing is that sometimes it works, sometimes it doesn't. I've tried setting a recursive setTimeout function, but on the times it fails, that doesn't work either. Any insight would be greatly appreciated. Or, if I'm going about this all wrong, feel free to suggest a different approach:
Screens = new Meteor.Collection('screens') //where data will ultimately be stored
Forms = new Meteor.Collection('forms') //Meteor pulls form questions from here
NewScreen = new Meteor.Collection('newscreen') //temporary storage collection
Roles = new Meteor.Collection('roles'); //displays list of metadata about screens in a dashboard
//dynamic routing for unique instance of blank form
Router.route('/forms/:_id', {
name: 'BlankForm',
data: function(){
return NewScreen.findOne({_id: this.params._id});
}
});
//onRendered function to pull data from NewScreen collection (this is where I get the error)
Template.BlankForm.onRendered(function(){
var new_screen = NewScreen.findOne({_id: window.location.href.split('/')[window.location.href.split('/').length-1]})
function do_work(){
if(typeof new_screen === 'undefined'){
console.log('waiting...');
Meteor.setTimeout(do_work, 100);
}else{
$('input')[0].value = new_screen.first;
for(i=0;i<new_screen.answers.length;i++){
$('textarea')[i].value = new_screen.answers[i];
}
}
}
do_work();
});
//onChange event that updates the NewScreen document when user updates value of input in the form
'change [id="on-change"]': function(e, tmpl){
var screen_data = [];
var name = $('input')[0].value;
for(i=0; i<$('textarea').length;i++){
screen_data.push($('textarea')[i].value);
}
Session.set("updateNewScreen", this._id);
NewScreen.update(
Session.get("updateNewScreen"),
{$set:
{
answers: screen_data,
first: name
}
});
console.log(screen_data);
}
If you get undefined that could mean findOne() did not find the newscreen with the Id that was passed in from the url. To investigate this, add an extra line like console.log(window.location.href.split('/')[window.location.href.split('/').length-1], JSON.stringify(new_screen));
This will give you both the Id from the url and the new_screen that was found.
I would recommend using Router.current().location.get().path instead of window.location.href since you use IR.
And if you're looking for two way binding in the client, have a look at Viewmodel for Meteor.

Angular.js (possible bug) - objects attribute return undefined on a single item view

i have a poll application with 2 views, list view and single item view. these are the init functions in the angular client controller
// Poll List
$scope.pollList = function(){
$scope.votes = Votes.query({
userId:Authentication.user._id
});
$scope.polls = Polls.query();
};
// View Poll
$scope.findOne = function(){
$scope.votes = Votes.query({
userId:Authentication.user._id
});
$scope.poll = Polls.get({
pollId: $stateParams.pollId
});
$scope.categories = Categories.query();
$scope.languages = Languages.query();
$scope.pollItem($scope.poll);
};
// Poll Item
$scope.pollItem = function(poll){
$scope.pollVoted(poll);
$scope.countPollVotes(poll);
for (var i = 0; i < poll.poll_options.length; i++){
$scope.optionItem(poll,poll.poll_options[i]);
};
};
both the list view and the single view initiate a pollItem function in each poll object. in the list view everything works good - each poll object passes the pollItem function without an error. but in the single poll view im recieveing errors, namely, that the diffrent attributes of the poll (i.e user, comments, poll_options) are returning undefined! allthough when i console.log the poll object i can see them all there. please help! im aware to the fact that it sounds a bit vague but im not sure which other details to provide.. im stuck a couple of days on this problem and no answer to be found.

Appending new results to an open jQuery autocomplete menu

I have an app in which I have multiple search sources. Previously, the users had to choose in what source to search in before searching. If they did not choose, the app would default to one of the options.
However, now they want to search in all the sources at the same time. This is fine enough, but the problem is that when one of the searches returns, it overwrites the previous search result. Pretty much expected behavior. What I basically want is to append the new results to the already open autocomplete menu, instead of overwriting the old results. Naturally, the autocomplete menu would have to empty when it closes.
I guess that this is possible to do, but what approach is the best? I could just have an array I guess, which I append results to and then overwrite _renderMenu to use this array instead of the items one that is passed to the function. Then empty said array at the close event.
Is this the best way to go though? Or is there a more elegant solution?
Some code:
Ok, so searchAction is called by jquery autocomplete eventually. In collection.search I do the ajax call, here the URL is created based in the this parameter, then respondWhithData is called and maps the search result to a proper format (ie value and label for the autocomplete menu). After reponse is called from respondWithData, jquery automagically renders the resultsmenu. Thus, I probably have to overwrite the reponse event function as well as the _renderMenu and possibly _renderItem, yes?
searchAction: function(searchTerm, collection, response){
var self = this;
$.when(collection.search(searchTerm, this)).then(function(data) {
self.respondWithData(data, response);
});
},
respondWithData : function(data, response) {
if (data.length > 0) {
var responseVal = _.map(data, this.mapData);
this.checkResponseCount(responseVal);
response(responseVal);
}
else {
response(this.emptyResult());
}
},
To be clear, the problem is not the multiple search itself, but rendering the asynchronos results. I want to render the first results that come back, and then appends the rest as soon as they are returned from the server.
Edit 2:
Just tried to edit ui.content in the autocompleteresponse event, but any edit does not take once it renders for some reason...
Edit 3: Ah, ui.content can only be modified directly, not changed. If I push every single change instead of concating two arrays ui.content shows what I want.
It works I guess, but its not perfect.
I can figure how looks your scenario but I'm guessing:
You should have like:
function search1() {
$.ajax({ ...
success: function(data) {
$('#myResultsDiv").html(data)
}
});
}
etc
Instead of overwritting the #myResultsDiv you need to Append the results like:
function search1() {
$.ajax({ ...
success: function(data) {
$('#myResultsDiv").append(data)
}
});
}
Edit: You can also do something like this:
var resultsArray = [];
var searchDone = 0;
var totalSearchs = 5; //assuming 5 searches
function search1() {
function search1() {
$.ajax({ ...
success: function(data) {
//APPEND data to resultsArray
searchDone++;
if(searchDone==totalSearch) //syncronize the 5 searchs before render
renderSearchs(resultsArray);
}
});
}

Backbone infinite loop when creating models

I'm doing something pretty standard, I think.
Model:
app.model.Todo = Backbone.Model.extend({
defaults: {
task: ''
, completed: 0
, attachments: []
, note: ''
}
});
Collection:
var Todos = Backbone.Collection.extend({
model: app.model.Todo
, localStorage: new Store('Todos')
, incomplete: function () {
return this.filter(function (todo) {
return !todo.get('completed')
});
}
, complete: function () {
return this.filter(function (todo) {
return todo.get('completed')
});
}
, comparator: function(todo) {
return todo.get('order');
}
});
app.collection.Todos = new Todos();
Then, if I just do:
app.collection.Todos.create({task: 'hi'});
app.collection.Todos.create({task: 'hi'});
The 2nd one never works. I get an infinite loop (too much recursion on Firefox and stack_overflow on Chrome).
I'm really at a loss. I commented out all events as well.
Appears it spins out of control here in backbone:
// Return a copy of the model's `attributes` object.
toJSON: function(options) {
return _.clone(this.attributes);
},
UPDATE: If I add id: 0 or whatever id to the model the error stops, but if I give it a custom ID (i.e. new Date().getTime() the error happens again. It's like whenever I create a unique item it blows up.
UPDATE 2:
var todo = new gator.model.Todo({task: actionbarVal});
gator.collection.Todos.add(todo);
gator.collection.Todos.sync('create', todo);
Doing the above kinda works, and for what I need it for it works, but it's really bad. It's bad because every single time we do a new add and sync it calls toJSON 1 time for every time add and sync has been called on this page load. So, if you add 3 items, you get 6 toJSON calls (1 for the first, 2 for the second, 3 for the third). Also, it's not as clean. I also noticed in the toJSON call in backbone this.attributes with create was correct the first time. The 2nd time it was like this.attributes == backbone or something. Very, very strange. It had all the methods of Backbone. It was as if clone did a deep clone or something.
You have a mismatch between your version of Backbone (v0.9.9) and the version of the localstorage add-on. Be sure to get the latest version of the localstorage add-on from the Backbone repo and it will fix this problem.
I eventually fixed it by reverting back to 0.9.2 of Backbone, thanks to Derick Bailey. My attempts of using the latest localStorage add-on didn't seem to fix it. Maybe I was using a different source? I was using develop of this:
https://github.com/jeromegn/Backbone.localStorage

Categories