Search criteria form and collections - javascript

In my application, I would like to create a search form in order to retrieve some users. This form contains many criteria (lastname, firstname, birthdate, etc.). But I do not understand how I can post my form in order to filter my collection. I can not use fetch method because if I use GET method, my search criteria will be displayed in the URL. In fact I would like to submit a POST request (which contains the search criteria serialize in JSON) to the server and retrieve a user list.
I have read the backbone documentation but I do not found anything that allow me to do that. This functionality is not supported by backbone ? There is some workarround to implement this requirements ?
Thanks in advance

In short, I don't think backbone is a good fit for what you are describing. Read on for a longer version...
Based on your question, I think backbone would be well suited to display your collection of users, but not so good at the submission of your filter criteria.
You could create a criteria model and set the attributes of the model to Username, Birthday, etc and then you could post it to the server using backbone. However when you save it using backbone, it is assumed that you saved the criteria. Any result that comes back from the server from that POST is assumed to be updated properties of the criteria, and not a list of users.

Tim is correct that this isn't exactly what Backbone is meant for. That being said I think I would still at least use a Backbone model to track the state of the search criteria. On this model I would add a custom method for activating search that would serialize the model's state and POST it to the server itself. On the success of this method, I would use the results to reset the current user collection.
Here is my idea in code:
var SearchModel = Backbone.Model.extend({
defaults: {
firstName: "*",
lastName: "Smith"
},
search: function() {
$.ajax({
dataType: "json",
type: "POST",
data: this.toJSON(),
success: function(data) {
this.options.peopleCollection.reset(data);
}
})
}
});
var myPeopleCollection = new PeopleCollection();
var mySearch = new SearchModel({ peopleCollection: myPeopleCollection });
mySearch.set({ firstName: "Steve"});
mySearch.set({ lastName: "Smith" });
mySearch.search();

Related

Sails.js Waterline query by association

I'm developing and app with Sails.js and using Waterline orm for db. I'm developing functionality for users to do friend requests and other similar requests to each other. I have following URequest model for that:
module.exports = {
attributes: {
owner: {
model: 'Person'
},
people: {
collection: 'Person'
},
answers: {
collection: 'URequestAnswer'
},
action: {
type: 'json' //TODO: Consider alternative more schema consistent approach.
}
}
};
Basically owner is association to Person who made the request and people is one-to-many association to all Persons who the request is directed. So far fine.
Now I want to have a controller which returns all requests where certain user is involved in meaning all requests where user is either in owner field or in people. How I do query like "give me all rows where there is association to person P" ? In other words how I ca know which URequest models have association to a certain Person?
I tried something like this:
getRequests: function (req, res) {
var personId = req.param('personId');
URequest.find().where({
or: [
{people: [personId]}, //TODO: This is not correct
{owner: personId}
]
}).populateAll().then(function(results) {
res.json(results);
});
},
So I know how to do the "or" part but how do I check if the personId is in people? I know I should somehow be able to look into join-table but I have no idea how and couldn't find much from Waterline docs relating to my situation. Also, I'm trying to keep this db-agnostic, though atm I'm using MongoDB but might use Postgres later.
I have to be honest this is a tricky one, and, as far as I know what you are trying to do is not possible using Waterline so your options are to write a native query using query( ) if you are using a sql based adapter or native otherwise, or try doing some manual filtering. Manual filtering would depend on how large of a dataset you are dealing with.
My mind immediately goes to reworking your data model a bit, maybe instead of a collection you have a table that stores associations. Something like this:
module.exports = {
attributes: {
owner: {
model: 'URequest'
},
person: {
model: 'Person'
}
}
Using the sailsjs model methods (like beforeCreate) you could auto create these associations as needed.
Good Luck, I hope you get it working!

Adding information from another mongodb collection before providing the data via meteor's publish method

What I want to accomplish on http://crowducate.me:
Display the usernames of the course authors (i.e. "owner" of a document).
Current Code:
Meteor.publish 'popularCourses', ->
# find all courses
courses = Course.find({}, {sort: {createdAt: -1}}).fetch()
for course in courses
# find each User by course owner
owner = Meteor.users.findOne({_id: course.owner})
# overwrite the ownerId with the desired username
course.owner = owner.username
return courses
If I turn autopublish on, it works. The image shows the current status (autopublish off). As seen in the image, the author's name is only rendered if the current user is the same as the author.
--
A friend suggested the following:
https://gist.github.com/wiesson/1fd93d77ed9df353b7ab
"The basic idea was to attach the username to the course before providing the data with the publish method. However, as described in Meteor MongoDB find / fetch issues, the publish method should return a curser and not an array of objects.”
Any ideas how to solve that? Putting the owner usernames in an array? If so, how?
P.S.: Sourecode can be found here (currently, has more commits than the deployed version):
https://github.com/Crowducate/crowducate.me
Thanks a lot.
There are a couple of ways you can accomplish this join. A few notes before before we begin:
As I explained in the answer to this question, sorting in the publish function has no affect on the order of documents on the client.
Using the plural form in a collection name is the accepted standard. Course just looks odd when the collection contains courses.
This question is fundamentally about joins, so I'd recommend reading Reactive Joins In Meteor.
Server Transform
The literal answer to your question is to transform the documents on the server like so:
Meteor.publish 'popularCourses', ->
transform = (fields) ->
if fields.owner
username = Meteor.users.findOne(fields.owner)?.username
fields.owner = username
fields
handle = Course.find().observeChanges
added: (id, fields) =>
#added 'course', id, transform fields
changed: (id, fields) =>
#changed 'course', id, transform fields
removed: (id) =>
#removed 'course', id
#ready()
#onStop ->
handle.stop()
Advantages
All of the work is done on the server, so the client can just use owner as if it was a username.
Disadvantages
Using observeChanges is probably more computational work than a simple join deserves.
If you publish courses somewhere else, it's entirely likely that owner will be overwritten when the documents are merged on the client. This can be countered by appending a field like ownerUsername but that would also require a more expensive observe.
This isn't helpful if you actually need the owner id somewhere on the client.
It isn't reactive if the username changes (probably rare but figured I'd point that out).
Non-Reactive Publish + Client Join
You could implement the publish like this:
CoffeeScript
Meteor.publish 'popularCourses', ->
courseCursor = Course.find()
userIds = courseCursor.map (c) -> c.owner
userCursor = Meteor.users.find {_id: $in: userIds}, {fields: username: 1}
[courseCursor, userCursor]
JavaScript
Meteor.publish('popularCourses', function() {
var courseCursor = Course.find();
var userIds = courseCursor.map(function(c) {return c.owner;});
var userCursor = Meteor.users.find(
{_id: {$in: userIds}},
{fields: {username: 1}
});
return [courseCursor, userCursor];
});
Note that I'm being careful to only publish the username and _id from userCursor (you don't want to publish the hashed password and session data by accident). Then you can join the two collections on the client like this:
Template.myTemplate.helpers
courses: ->
Course.find().map (c) ->
c.owner = Meteor.users.findOne(c.owner)?.username
c
Advantages
Computationally light-weight and simple publish function.
Reactive if the username changes.
Disadvantages
Not reactive if the owner changes.
You'll need to do the join on the client. An interesting alternative is to use something like Collection Helpers.
Finally, I'll point out that you can use a package to do a fully reactive join. However, unless the owner (or owner's username) is changing a lot then this is probably overkill.
A simple solution would be to just publish both popularCourses and owners and add the owner to each course on the client (with the exact same code you have written on the publication).

Multiple Backbone.js collection options?

Intro:
Building in node.js and express on the backend, I am sending a res.json(details) to the localhost:3000/me route containing the users session information.
So that on the client side I can work with that specific user, for example on the client side initialization I write some code like this.
var me = new MeModel();
me.fetch({
success: function(response) {
App.data.me = me;
var messages = new MessagesCollection([], { id: response.get('user_id') });
messages.fetch({
success: function() {
App.data.messages = messages;
App.core.vent.trigger('app:start');
}
});
}
});
You see I fetch the me model and use that to filter the messages in the MessagesCollection.
Question:
In my MessagesCollection I pass options like this.
module.exports = MessagesCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
},
url: function() {
return '/api/messages/' + this.id;
},
model: MessageModel,
//url: '/api/messages'
});
This is great for getting the desired models when using var messages = new MessagesCollection([], { id: response.get('user_id') });
My question is when somewhere else I run window.App.data.messages.create(Message); this wants to POST to /api/messages/:id when I want to POST to the regular collection?
Theory:
So obviously I would say the best thing is to rethink how I filter the models in the fetch method.
So basically to simplify this question I need to filter the collection upon .fetch() GET... and set no filters upon .create() POST
BTW I am using marionette, maybe that can help with something?
model.url() Returns the relative URL where the model's resource
would be located on the server. If your models are located somewhere
else, override this method with the correct logic. Generates URLs of
the form: "[collection.url]/[id]" by default, but you may override by
specifying an explicit urlRoot if the model's collection shouldn't be
taken into account.
Delegates to Collection#url to generate the URL, so make sure that you
have it defined, or a urlRoot property, if all models of this class
share a common root URL. A model with an id of 101, stored in a
Backbone.Collection with a url of "/documents/7/notes", would have
this URL: "/documents/7/notes/101"
http://backbonejs.org/#Model-url
So you can define method url at MeModel and generate url there ( if there are no other users - you can just return string "/me" or generate in based on model properties ( for example switch if model has id )

Facebook api, get user liked page using FB.api

Using this method, facebook will return an object of the user's liked pages
FB.api('/me', {fields: 'gender, first_name, last_name, email, likes'}, function(response) {
console.log(response);
}
result
email: "ariel#dropbox.com"
first_name: "Ariel"
gender: "female"
id: "178006027123671"
last_name: "Yeung"
likes: Object data: Array[2]
However, I only want to get the object whether this user is liking my facebook page
As https://developers.facebook.com/docs/graph-api/reference/user/likes describes, you can ask whether the user likes a certain page using
/me/likes/{page_id}
Since you are asking for specific user fields already, you probably don’t want to make a separate API call for that – but you can fit it in there as well, using field expansion:
/me?fields=gender,first_name,last_name,email,likes.target_id({page_id})
If the user likes that particular page, you get a likes property in the response data.
(Not sure if that works using the syntax you are using currently as well, passing the fields via object, but you can simply try: {fields: 'gender, …, likes.target_id({page_id})'} … let us know if that works, thanks.)
Edit: Although this will still work, as long as you get user_likes permission from the user, Facebook will not approve the use of that permission for this singular purpose. Like Gating in any way, shape or form is not allowed any more.
You canot get the list of users liking you page via graph api. So, you can not check wether or not a user has like your page directly.
You already have the likes of a user, just loop through them and check that your page is liked or not.

How to pass two parameters to model.fetch() in backbone?

I'm making a very simple user authentication system. I'm trying to pass a combination of username and password as params to the back-end nodejs server. So this combination will be used in my db query to fetch user details.
This is what I tried on the front-end:
var user = new UserModel({id:['username', 'password']});
user.fetch();
I have defined a urlRoot property in my model that goes like this: /api/users
The back-end will handle the following url: /api/users/:id
Here since I have passed id as an array, I tried to access the 'username' by doing this req.params.id[0]. Instead it returns the first letter of the 'username'. But I want to take the entire string of username. Of course I could use the split() function to separate them but I believe there is a better way to do this.
Please tell me if my approach is wrong somewhere.
That's because Backbone serializes your array to string and then encodes it as URI component.
So effectively you're sending a String 'username%2Cpassword' instead of an array.
I had the same problem and decided that sign in process doesn't really represent any "physical" resource, and most likely shouldn't be handled by user model. One doesn't CRUD users when signing in.
What i did was to create a separate model for SignIn:
SignInModel = Backbone.Model.extend({
urlRoot: 'api/sign_in',
defaults: {
'username' : '',
'password': ''
}
});
which statically maps to api/sign_in (no id's here), and then query the database by username and password passed in the request body to the api/sign_in handler.
UserModel can then be left to handle CRUD of users.

Categories