I've just started to look into using Meteor for an upcoming project, and have a question about data persistence. It sounds like you have two options: First, you can declare a "name" when instantiating a new Collection which will create a database collection which will be saved upon alteration.
Chatrooms = new Meteor.Collection("chatrooms");
The other option is to create an anonymous collection, which won't be saved.
Chatrooms = new Meteor.Collection();
But what do I do if I want to populate a Collection from the database, but not save it upon alteration on the client-side? For instance, I might want to create a collection of user Movies that will be displayed in a grid -- each having their own absolute positioning based upon the sorting and filtering applied to the collection. Upon changes to the collection, the associated views (or templates) will be re-rendered to reflect those changes. But I don't necessarily want these absolute positions to be stored in a database...
Any ideas?
I'm not very clear about your question. But perhaps, you can bind the absolute position into the collection data? They are just normal javascript objects. And the collection data will only be changed through insert/update/remove function call.
I ended up doing something like this:
movies: function() {
var movies = Movies.find().fetch();
_.each(movies, function(movie, index){
movie.left = index * 2;
movie.top = index * 2;
});
return movies;
},
Basically, 'fetch()' allows me to deal with pure JSON objects, making it easier to iterate through them and alter them without performing 'update' commands.
Related
So I'm working with Ember Data and have a component where I want to render so many records from the store, let's say 10. I also have a live feed of these records that I want to make sure show up in this component, but I still want no more than 10. Now if I had a static array of objects I'd just say 'hey, remove the last as the new data comes in', however I need this component reflecting a live recordset so that my live feed can pour into it.
For now I query the store to get the base set of records loaded from the backend
this.store.query('recordName', {
numberOfRecords: 10
});
Then the list of records that I render is assigned recordList = this.store.peekAll('recordName');
Now this will update whenever new records are created (they are created client-side). My downsides are that new records are automatically appended to the bottom of the list rather than the top (which is a separate bug) and I can just keep loading infinite records into this list.
Is there an elegant way with Ember Store to limit the live record set returned or is it going to have to result in some operating on the list with custom javascript?
its pretty simple to filter for this in a component:
#tracked recordList;
constructor() {
super(...arguments);
this.recordList = this.store.peekAll('recordName');
}
get limitedArray() {
return this.recordList.reverse().slice(0, 10);
}
this works because the live array is autotracked.
you can then do {{#each this.limitedArray}} in your template.
My Meteor client receives data from the server and stores it in minimongo. This data is guaranteed not to change during their session, so I don't need Meteor's reactivity. The static data just happens to arrive by that route; let's just take that as a given.
The data looks like this:
{_id: 'abc...', val: {...}}
On the client, is it more efficient for me to look up values using:
val_I_need = Collection.findOne({id})
or to create a JavaScript object:
data = {}
Collection.find().fetch().map((x) => {data[x._id] = x.val})
and use it for look ups:
val_I_need = data[id]
Is there a tipping point, either in terms of the size of the data or the number of look ups, where the more efficient method changes, or outweighs the initial cost of building the object?
FindOne may be more efficient on larger datasets because it looks up using cursors where _id is an indexed key while your find().fetch() approach requires to get all docs and then iterate manually by mapping.
Note, that findOne could also be replaced by .find({_id:desiredId}).fetch()[0](assuming it returns the desired doc).
More on this in the mongo documentation on query performance.
However, if it concerns only one object that is afterwards not reactively tracked, I would rather load it via a "findOne"-returning method from the server:
export const getOne = new ValidatedMethod({
name: "getOne",
validate(query) {
// validate query schema
// ...
},
run(query) {
// CHECK PERMISSIONS
// ...
return MyCollection.findOne(query);
});
This avoids using publications / subscriptions and thus minimongo for this collection on the current client template. Think about that pub/sub has already some reactivity initialized to observe the collection and thus eats up some computation somewhere.
My gut feeling is that you'll never hit a point where the performance gain of putting it in an object makes a noticeable difference.
It's more likely that your bottleneck will be in the pub/sub mechanism, as it can take a while to send all documents to the client.
You'll see a much more noticeable difference for a large dataset by retrieving the data using a Meteor method.
At which point you've got it in a plain old javascript object anyway and so end up with the small performance gain of native object lookups as well.
After reading the docs, this is my understanding of sync.
I instantiate some Backbone.Model and call Collection.create(). create() eventually calls sync() and the Model is POSTed to the server. Then there is a sync in the opposite direction such that the Model on the client is given an id.
Does this update then trigger componentDidUpdate()?
Note: componentDidUpdate is a ReactJS thing, so if that doesn't make sense, the question reduces to "Is the client-side model updated and the view re-rendered?"
Since inside of my componentDidUpdate() I am making a call to save() to keep everything up to date, this subsequently makes a call to sync() which then fires a PUT request (because the Model already has an id).
I'm asking, because in my current application, creating a TodoItem seems to result in a POST and then a PUT which I find redundant. Perhaps it is for an unrelated reason.
It actually fires two POSTS and then two PUTS when adding one item, but that is another question.
The first time you save a model (one which doesn't have an id) it will make a POST, thereafter it will make a PUT (update). I think you are confusing when to use create/add/save:
Use save at any point to save the current client collection/model state to the server
Use add to add Model(s) to a collection (a single Model, an array of Models, or an array of objects which contain attributes and let the collection create them)
Use create as a shorthand for creating a model, adding it to the collection, and syncing the collection to the server.
My guess is that you are calling create and save in one operation - you should be using add and save instead, or just create.
The view will not automatically update for you, you will need to listen to changes or events on the collection/model and update the view yourself - there is no equivalent of componentDidUpdate. For example:
initialize: function() {
this.listenTo(this.collection, 'sync', this.onCollectionSync);
},
onCollectionSync: function() {
this.render();
}
In this stackoverflow post i read about filtering backbone collections and using subsets.
One answer (by sled) recommends using backbone.subset.js (usage example).
I could not find any further resources on backbone.subset.js and I failed implementing it into my project.
It seems like backbone.subset.js is the perfect solution for what i'm trying to achieve.
(Having one "parent" collection that holds all models at all times, and depending on user input filtering the relevant models from the parent collection into a backbone.subset collection.)
My "parent" collection, holding all tasks:
var TasksAll = Backbone.Collection.extend({
url: '/tasks', // the REST url to retrieve collection data
model: Task // the models of which the collection consists of
});
var allTasks = new TasksAll();
Now i want to create a subset collection for e.g. tasks where task.status = 0:
var TasksTrash = new Backbone.Subset({
superset: allTasks,
filter: function(Task) {
return Task.isTrash();
}
});
var trashTasks = new TasksTrash();
Whereas inside the Task model, the method "isTrash" returns true if:
this.get('status') == 0
a) Are there any more resources on backbone.subset.js?
b) How do I implement above scenario?
c) Can I pass 'superset' and 'filter' options as params to the Backbone.Subset init function?
d) I looked into the backbone.subset.js code, when I 'reset' my parent Collection my subset Collections should be updated straight away, right?
PS: I'm fairly new to Backbone. Thanks for your help.
Looking at the source for backbone-subset, it looks as though there is a pre-initialization hook which you could utilize in order to make the 'sieve' or filter available as an option or argument:
https://github.com/masylum/Backbone.Subset/blob/master/backbone.subset.js#L50
As for providing parent as an argument, there is an outstanding patch to add that exact functionality:
https://github.com/masylum/Backbone.Subset/pull/5
With it, you can pass in parent as an option, if it is not an option the library will fall back to looking for it on the object Prototype
I am using Backbone.js to render a list of items (email recipients) that have different status, eg. confirmed, pending and so on. After the list is rendered there are options for user to filter them so the user can list all recipients, or only confirmed recipients and so on. The items (recipients) are naturally stored in a collection..
My approach is to on a filter event:
Clear all item's view's
From the app view call a filterOnStatus function in the collection that return all models and adds them to the view.
Step 2 works fine. But what's the best way to clear all items on a collection's view's.
In the Todo example application (http://documentcloud.github.com/backbone/examples/todos/index.html) they do something similar. In the app view the following code is used to clear all completed items from the list.
clearCompleted: function() {
_.each(Todos.done(), function(todo){ todo.destroy(); });
return false;
},
The difference here is that they do this by removing the actual model. And that model's view listens for the destroy event which them removes the view.
I want to keep the model.
What's the best way to solve this. Do I in the models need to stored a reference to its views and then iterate over the models and remove the views?
Is there a better approach if I want to filter on attributes in the models?
If your first step is simply clearing all items then why don't you add a simple method to your AppView that will do just that, like: clearList: function() { this.$('.list').html('') }.
Or even better, you can filter all models and render them to the temporarily element and than replace current list with it. Thus all the filtering will be done in only one DOM call (DOM is slow). Example with jQuery:
AppView.filterOnStatus = function() {
var $fragment = $('<div/>')
// filter your collection and append rendered views to $fragment
this.$('.list').html( $fragment.html() )
}
Of course there are more complicated ways, but whether you need them depends on what you're trying to achieve. From what I understood this simple approach would be fine enough.