get data from Meteor collection in client-side javascript - javascript

I need to access collection on client-side javascript files to sort data and perform various operations. Currently I'm just using find() or findOne(), but it's very inconsistent and sometimes doesn't return value quickly enough so it doesn't work.
Am I doing it the right way? Are there any other options provided to retrieve and manipulate collection data?
For example, I want to find this data:
data = Stats.findOne({
name: Session.get('name')
}, {sort: {date: -1}});
variable = data.variable;
And then I want to use variable to filter collection:
ranking = Stats.find({
variable: variable
}, {sort: {score: -1}}).fetch();
I can't find a reason why it doesn't work.

Meteor is reactive, which means that as that Stats collection populates with data from the server, code that uses it will be automatically re-run. If you're trying to display the data on screen, you want to use a helper, a reactive variable, and the autorun function.
JS:
Template.yourTemplate.onCreated( function() {
var template = this;
template.variable = new ReactiveVar( null );
template.autorun( function() {
var result = Stats.findOne({ name: Session.get('name') }, { sort: { date: -1 } });
if( result && result.variable ) {
template.variable.set( result.variable );
}
});
});
Template.yourTemplate.helpers({
ranking() {
return Stats.find({ variable: Template.instance().variable.get() }, { sort: { score: -1 } }).fetch();
}
});
HTML:
<template name="yourTemplate">
{{#each rank in ranking}}
Do stuff with {{rank}}
{{/each}}
</template>
This will ensure that your reactive variable changes as Stats.findOne() of your session variable changes, and that your template can appropriately get the rankings you want.

Related

Is there a standard way to add presentation information to Vue?

Let's say I have a Vue component that has the following data, which has been retrieved from an API:
data: () => ({
books: [
{name: 'The Voyage of the Beagle', author: 'Charles Darwin'},
{name: 'Metamorphoses', author: 'Ovid'},
{name: 'The Interpretation of Dreams', author: 'Sigmund Freud'},
],
}),
I would like to store presentation variables for each of these books, e.g. an open boolean to determine whether the book is open or not. I don't want the API to return these variables though, as I don't want the API to be cluttered with presentation data.
Is there a standard way of doing this in Vue?
you can add the presentation data after receive the information from the API:
...
data: () => ({ books: [] });
...
methods: {
// API call to get the books
async requestBooks() {
// TODO: add try catch block
const books = await getBooks(); // Your API call
this.books = addPresentationInformation(books);
},
addPresentationInformation(books) {
return books.map(book => {
return {
...book, // default format from API (name, author)
open: false, // add the open variable to the object
reading: false,
currentPage: 0
}
});
}
},
created() {
this.requestBooks(); // Call the api on created hook to initialize the books data prop
}
You can add many presentation variables as you want, I recommend use vuex to store the books and their presentation variables, that way you can save information in the local storage for each book, so after restart the app, you can know if some book is currently being reading or is open.
I would personally maintain another array that contains some state relational to each book rather than trying to mutate the API response data. That's just me though.
Probably another way is to copy object and modify it and keep original response data
data(){
let data = Object.assign({}, this);
// add necessary presentation data
return data;
}
I now use normalizr to process and flatten responses from the backend API, and this library provides a means to add extra data. For example, the following schema adds the hidden data attribute.
const taskSchema = new schema.Entity(
'tasks',
{},
{
// add presentation data
processStrategy: (value) => ({
...value,
hidden: false
}),
}
);

2 publications inside a Meteor collection good practice

is it good or bad practice to publish 2 find querys inside the one Meteor.isServer function inside my collection?
I have this code: deals.js / collection
Meteor.publish('deals', function () {
return Deals.find({ userId: this.userId });
});
And I'd like to add another publication like so:
if (Meteor.isServer) {
Meteor.publish('deals', function () {
return Deals.find({ userId: this.userId });
});
Meteor.publish('deals', function () {
return Deals.find({ category: 'technology });
});
}
The reason for the 2nd publication is too enable a category component where only that category of results are displayed.
So now I can subscribe to this inside my component createContainer. Thanks!
There is nothing wrong in itself having more than 1 publication from the same Collection.
However, in your case, I am not sure using the same 'deals' identifier for the Meteor publication is a good idea.
If your publications serve different purposes (typically they are used at different times / in different components), then simply use different names / identifiers.
But if I understand correctly, you actually want to use them in the same component, so that it receives Deals documents that are either from the current user or from a given category? In that case, simply use a MongoDB query selector with $or:
Meteor.publish('deals', function () {
return Deals.find({
$or: [{
userId: this.userId
}, {
category: 'technology'
}]
});
}
Or even return an array of cursors:
Meteor.publish('deals', function () {
return [
Deals.find({ userId: this.userId }),
Deals.find({ category: 'technology' })
];
}
(note that this also enables you to publish documents from different Collections in a single publication!)

How to find one collection item by an arbitrary property, but not _id, in Meteor?

I have an application that uses Flow Router and its pub/sub mechanics. I also have a collection and template helpers. The code is, on client
Template.theCase.helpers({
theCase: function () {
var id = FlowRouter.getParam('id');
var theCase = Cases.findOne({
id: id
});
return theCase;
}
});
and
{{#with theCase}}
{{ id }}
{{/with}}
then, on server
Meteor.publish('theCase', function (id) {
return Cases.findOne({
id: id
});
});
and finally, on both (lib)
FlowRouter.route('/case/:id', {
subscriptions: function (params) {
this.register('theCase', Meteor.subscribe('theCase', params.id));
},
action: function (params, queryParams) {
return BlazeLayout.render('container');
}
});
The problem, as I see it, is that helper returns undefined, since it's not allowed to find items in a collection by any other property than _id. How can I overcome it? I've read truckload of the official docs on pub/sub, helpers and routing, and I just can't find the solution. Any suggestions?
You can query by any field. The helper returns undefined because it didn't find anything that matched.
This code is problematic:
Meteor.publish('theCase', function (id) {
return Cases.findOne({
id: id
});
});
It should be: return Cases.find({id: id});
Publications must return a cursor or call this.ready()

How to get data out of ember objects

I'm fairly new to ember and I'd like to know whats the fastest way to extract the data out of ember objects. I've loaded my model with a very large amount of records using this.store.find('modelName);` in my route.
I created a component on my view using {{kendo-ui.kendo-table descriptor=tableDescriptor data=model}}. My controller defined other arguments to be passed to my component (descriptor).
In my components.js I'm' getting the data passed over by using
export default Ember.Component.extend({
didInsertElement: function() {
var columns = this.get('descriptor.columns'); // this is right
var model = this.get('data')['content']; // this returns the objects of the model
var height = this.get('descriptor.height'); // this is ok too
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: model,
pageSize: 100
},
height: height,
scrollable: {
virtual: true
},
groupable: true,
sortable: true,
columns: columns
});
}
});
On the line var model = this.get('data')['content'];, this gives me an Array of Ember Classes. Inside each class, there is a _data object that holds the value of the actual class.
The easiest solutions is to just loop through and extract the _data but that is no good for larger model array. Is there a quick way to extract all the _data from my array of ember objects?
You could use getProperties method. http://emberjs.com/api/classes/Ember.Object.html#method_getProperties
To get the values of multiple properties at once, call getProperties with a list of strings or an array:
record.getProperties('firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
You could define computed property dataArray:
dataArray: function() {
return this.get('data').map( function(item) {
return item.getProperties('id', ... ); // your list of properties
});
}.property('data.[]'),
didInsertElement: function() {
//...
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: this.get('dataArray'),
//...
},
// ...
});
}
UPDATE:
for records (DS.Model) you could use toJSON method. Use DS.JSONSerializer to get the JSON representation of a record.
toJSON takes an optional hash as a parameter, currently supported options are:
includeId: true if the record's ID should be included in the JSON representation.
http://emberjs.com/api/data/classes/DS.Model.html#method_toJSON

Using AngularJS $filter with ng-disabled

I've got an object in my $scope that contains a bunch of details about, say, an election. This object includes a voters array of objects, each with an _id:
$scope.election = {
voters: [
{ _id: '123' },
{ _id: '456' },
{ _id: '789' }
]
}
Also in my scope I have details about the currently logged in user:
$scope.user = { _id: '456' }
How can I bind ng-disabled to the presence of $scope.user._id in the array of objects $scope.voters?
What I've Tried
I have success simply displaying the presence of $scope.user._id in $scope.election.voters like this (Jade syntax):
pre(ng-bind="election.voters | filter:{user._id} | json")
When the current user is among the voters, they get displayed. When they're not among the voters, I get an empty array. That seems quite close to what I want.
But using the same filter (sans | json) with ng-disabled, I get the Angular Infinite $digest loop error.
Is this situation too complicated? Should I move it to a $filter? If so, how would I go about making it generic enough to be useful in a number of situations (if that's even feasible)?
Can run a simple filter right in controller, or using app.filter('filterName', func...) create a custom filter you can use in markup
$scope.userIsVoter = function() {
return $scope.election.voters.filter(function(el) {
return el._id == $scope.user._id;
}).length
}
<button ng-disabled="userIsVoter()">Do Something</button>

Categories

Resources