The logic is click user, checks for existing rooms. If there is, go to existing room. If not, creates new room and goes into it. There will only be a unique room between the user and myself. When I click user for first time, it sets a unique Id for the room. But if i click the name a 2nd time, it routes me to a room with url params as user's Id.
Was wondering whats wrong and if theres a cleaner way. I also cant display the name of the receiver. I used helpers Meteor.users.findOne({ _id: this.receiver }).
roomDetail
//subscription
self.subscribe('room', Router.current().params._id);
//.. room helper
return Rooms.findOne({ _id: Router.current().params._id });
User page to click button
Template.usersShow.events({
'click .user': function(event) {
var receiver = this._id;
if (receiver) {
var res = Rooms.findOne({ 'receiver' : receiver });
if(res){
//..route to the existing room
} else {
//...create new room
In order to ensure that there is only one room between any pair of users you should treat both the "initiator" and the "receiver" equally. Below I describe one way of doing just that.
A room has an id (the built in _id) and the ids of both users:
{
userA: "aaaa",
userB: "bbbb",
// ...
}
When finding a room between "aaaa" and "bbbb", we don't know which is which. We can avoid this problem by imposing the rule that userA has the id that sorts before userB. To make it easy to use this rule, we write a helper function:
function userAAndB(user1, user2) {
if (user1 < user2) return {userA: user1, userB: user2};
else return {userA: user2, userB: user1};
}
Now if we want to find the room for users x and y, we can find it like this:
var theRoom = Rooms.findOne(userAAndB(x, y));
The helper can also be used as a starting point for creating a new room.
In order to find all rooms that a user is in, we can use an $or operator:
var allRoomsWithX = Rooms.find({$or: [{userA: x}, {userB: x}]});
Using this design, there are a couple of ways that you can implement your routes and buttons:
Put the other user's id in the route, like allRooms/:otherUser. The router, template and publication can use userAAndB(userId, otherUser) to find the room they need.
In this case, you need some code that creates the room if it doesn't exist. On the server is best, so you could put it in the publish function. This means that the client doesn't need to know the room id, and can simply link to the right route.
Put the room id in the route, like allRooms/:_id. The router, template and publication can use the room's _id.
In this case, you need to find out what the correct room id is for a given pair of users in order to make a link. This is pretty easy using userAAndB(userId, otherUser). You also need to create a new room if one doesn't exist. You could do this when the user clicks on the link.
It might be best to make a Meteor Method to get the room id for a pair of users so that you don't need to send all of the rooms to the client. The method would be something like getRoomId(user1, user2) and it would create a room if needed, then return the id.
Slightly related, because you are asking about nicer ways to structure code, I want to mention Iron Router's waitOn and data features.
You can have Iron Router make subscriptions and fetch data for you, so that you don't need to do it in your templates. This is great if a template can't be sensibly rendered until the main data has loaded, and would probably be good in your roomDetail route. Look for waitOn and data in this section of the Iron Router guide for an example.
Related
I am trying to access the child value of a unique key value (that had been "pushed") within Firebase. Currently, my database looks like this: I want to access the value of "emailOfUser"
I am very new to Firebase so I am not familiar with the functions. Currently, this is my method of obtaining other values for a different section of the database:
Thank you so much for any feedback!
I've tried different methods to accessing this data within the Firebase, but I cannot get it to work/the methods I were using were outdated. I also tried to "update" the Firebase instead of "pushing" the values to prevent a unique key from generating, but it simply overwrote my current data rather than appending something new.
If you want to load all the users who voted and print their emails, you can do that with:
get(child(dbref, 'usersWhoVoted')).then((snapshot) => {
snapshot.forEach((childSnapshot) => {
console.log(childSnapshot.key, childSnapshot.val().emailOfUser);
});
})
Note that your current structure allows a user to vote multiple times. If you want to only allow them to vote once, use some identifier of the user as the key in your database structure:
userVotes: {
"uniqueIdOfUser1": "valueTheyVotedOn",
"uniqueIdOfUser1": "valueTheyVotedOn",
...
}
Now each user can by definition only vote once, If they vote again (assuming your security rules allow that), their new vote will simply replace the existing vote.
I have a collection of users. Each user has a cards array, which is composed of the ids of the cards that the user chose.
user.cards = [10, 12, 24, 31]
I want to have the 10 cards that are the most chosen by users.
I have user.service.js and card.service.js, which I guess are DAOs. card.service.js looks something like this (used plain Promises for simplicity).
(function(app) {
app.CardService = ng.core.Injectable({}).Class({
constructor: function() {},
getCards: function() {
return Promise.resolve(app.cards)
},
getCard: function(cardId) {
return Promise.resolve(app.cards.get(cardId));
},
getTop10Cards: function() {
// How would I go about doing this method?
}
})
})(window.app || (window.app = {}));
Logically, I would need the list of all the users, collect their cardIds and aggregate them to find the most used cards. But should CardService use UserService (as an injection)? Or is using a DAO in another DAO the wrong way to go about it? Should I have a separate collection that contains the links between each models (like a joint table)?
To help myself deal with this client-side problem, I investigated on what the back-end resources would look like. I came up with something similar than "How to handle many-to-many relationships in a RESTful API?".
These are what my API endpoints will potentially look like: /users, /cards, /cards/{cardId}/votes. /cards/{cardId}/votes contains resources that define the relationship between the users and the cards they have chosen.
In the code, I am indeed injecting vote.service.js inside card.service.js. When I GET the cards, I also compose them with their subset of votes. See "angular js model relationships" for more information.
I hope this will help anyone that will come across this question.
Cheers!
I am extending this chat client built using AppWarp Client API and App42 Backend.
After appropriate initializations and setting listeners I am successfully able to fetch all available rooms using _warpclient.getAllRooms(); and its listener:
function onGetAllRoomsDone(rooms) {
console.log(rooms);
for(var i=0; i<rooms.getRoomIds().length; ++i) {
_warpclient.getLiveRoomInfo(rooms.getRoomIds()[i]);
}
}
Problem:
However, rooms.getRoomIds() returns dynamic rooms that are stale (dead / destroyed see: dynamic rooms here). Is there a way to identify these stale rooms?
Attempts:
I have extensively searched their API reference but haven't
found any information on how to achive this.
I also explored the room objects received in the callbacks onGetLiveRoomInfoDone and onGetAllRoomsDone but it doesn't contain anything relevant.
Their App42 Management Console doesn't provide a list of these rooms or their properties.
If a room has been destroyed/dead, it won't appear in getRoomIds(). It might be the room is not dead but empty. Try joining any such room, you will be able to join it. If It was dead, your join room request will fail.
A rare case can be, when you called getAllRooms(), there was someone in room and hence you got it in the result, but the before you can send the join request, it got empty and destroyed.
I want to publish and subscribe subset of same collection based on different route. Here is what I have
In /server/publish.js
Meteor.publish("questions", function() {
return Questions.find({});
});
Meteor.publish("questionSummaryByUser", function(userId) {
var q = Questions.find({userId : userId});
return q;
});
In /client/main.js
Deps.autorun(function() {
Meteor.subscribe("questions");
});
Deps.autorun(function () {
Meteor.subscribe("questionSummaryByUser", Session.get("selectedUserId"));
});
I am using the router package (https://github.com/tmeasday/meteor-router). They way i want the app to work is when i go to "/questions" i want to list all the questions by all the users and when i visit "/users/:user_id/questions", I want to list questions only by specific user. For this I have setup the "/users/:user_id/questions" route to set the userid in "selectedUserId" session (which i am also using in "questionSummaryByUser" publish method.
However when i see the list of questions in "/users/:user_id/questions" I get all the questions irrespective of the user_id.
I read here that the collections are merged at client side, but still could not figure a solution for the above mentioned scenario.
Note that I just started with Meteor, so do not know in and outs of it.
Thanks in advance.
The good practice is to filter the collection data in the place where you use it, not rely of the subset you get by subscribe. That way you can be sure that the data you get is the same you want to display, even when you add further subscriptions to the same collection. Imagine if later you'd like to display, for example, a sidebar with top 10 questions from all users. Then you'd have to fetch those as well, and if you have a place when you display all subscribed data, you'll get a mess of every function.
So, in the template where you want to display user's questions, do
Template.mine.questions = function() {
return Questions.find({userId: Meteor.userId()});
};
Then you won't even need the separate questionSummaryByUser channel.
To filter data in the subscription, you have several options. Whichever you choose, keep in mind that subscription is not the place in which you choose the data to be displayed. This should always be filtered as above.
Option 1
Keep everything in a single parametrized channel.
Meteor.publish('questions', function(options) {
if(options.filterByUser) {
return Questions.find({userId: options.userId});
} else {
return Questions.find({});
}
});
Option 2
Make all channel return data only when it's needed.
Meteor.publish('allQuestions', function(necessary) {
if(!necessary) return [];
return Questions.find({});
});
Meteor.publish('questionSummaryByUser', function(userId) {
return Questions.find({userId : userId});
});
Option 3
Manually turn off subcriptions in the client. This is probably an overkill in this case, it requires some unnecessary work.
var allQuestionsHandle = Meteor.subscribe('allQuestions');
...
allQuestionsHandle.stop();
I'm not sure where to put some methods.
Let's say I want to send an email.
Which of the following options should I choose:
email = new Email("title", "adress", "body");
email.send();
or
email = new Email("title", "adress", "body");
Postman.send(email);
Because how can an email send itself? And isn't it better to have a central object that handles all emails because then he can regulate things like sending all emails at a specific time, sort mails, remove mails etc.
Also if I want to delete an user, how should I do:
user.delete();
or
administrator.delete(user);
Please share your thoughts about how to know where to put the methods.
I disagree with Arseny. An email can send itself, and that's exactly where the code should live. That's what methods are: actions that can be performed on the object.
However, note that your approaches are not mutually incompatible. An email's send action could easily just contain the code to add itself to the Postman's send queue, and if you do want to regulate the actions, that might be a good idea. But that's no reason not to have a send method for the email class.
All sensible methods that act on emails should be in the email class, for the convenience of users of your class. But email objects should not contain any fields except those related to the content of the email itself (single responsibility principle).
Therefore, I'd suggest this:
class Email
def email(postman)
postman.send(self)
end
end
In statically typed languages, the type of the postman argument should definitely be an interface.
Use the second method to have a class manager handle the objects (emails or users). This follows the single-responsibility-principle.
In Ruby I'd do this:
email = Email.deliver(recipient, subject, message)
The correspoding class would look something like this:
class Email
def self.deliver(recipient, subject, message)
# Do stuff to send mail
end
end
This is clean and easy to use.
On the delete issue: Delete the object you want to delete. So #user.delete would be best. If you want to register the administrator who deleted the user: #user.delete_by(#admin)
I agree with Daniel.
Following your first example, a lot of common widgets would also have a "collections" manager like you mentioned but they don't necessarily. A Tabs widget can show/hide one of its own tabs, without necessarily specifying a new Tab class for each individual one.
I believe functionality should be encapsulated. The example of deleting a user however, is a slightly different case. Having a delete method on the User class could do stuff like clear its own internal variables, settings, etc, but it won't delete the reference to itself. I find that delete methods are better suited for collection-based classes. I wouldn't per se put the delete method on a admin class but rather on a Users "collection" class.
function Users(){
var users = [];
this.add = function(user){
// add user code
users.push(new User(user));
}
this.remove = function(user){
// remove user code and remove it from array
}
}
I don't quite see how an object can fully add/remove itself so it makes sense to me to have that functionality at the collections level. Besides that though, I would say it should be encapsulated within the class it's meant for.