Generating Javascript from coffeescript equivalent to manual javascript in _.each underscore - coffeescript

I have a code in Javascript as :
_.each(this.collection.models, function (student) {
$(this.el).append(new StudCloneItemView({ model: student }).el);
}, this);
While I am writing This in coffescript as
_.each this.collection.models , (student) =>
$(#el).append new Item ({ model:student }).el
which generates
_.each(this.collection.models, function(student) {
return $(_this.el).append(new Item({
model: student
}.el));
});
Which isn't desirable as per my requirement . The last segment of "this" element has been missing into the generated javascript . Its very important.
How would I generate The javascript as mentioned on top using the coffeescript I mentioned for _.each ????
Is there anyway to do that ?? Or am I missing any syntax ?

Your JavaScript:
_.each(this.collection.models, function (student) {
$(this.el).append(new StudCloneItemView({ model: student }).el);
}, this);
and what your => CoffeeScript produces:
var _this = this; // This is somewhere above your _.each in the generated JS
_.each(this.collection.models, function(student) {
return $(_this.el).append(new Item({
model: student
}.el));
});
are functionally equivalent. You don't need the context argument to _.each since the => generates an alias for this (called _this) that is used inside the callback.
As an aside, Backbone collections have various Underscore methods mixed in so you don't have to say _.each(#collection.models, ...), you can use each directly on the collection:
#collection.each (student) =>
#$el.append(new StudCloneItemView(model: student).el)
I've also switched to the pre-built $el that your view already has, there's no need to build a new jQuery object on each iteration.

like this:
_.each #collection.models, ((student) ->
$(#el).append new StudCloneItemView(model: student).el
), this

Related

Using one autoForm, I need to insert data into two collections

I am currently working on an inventory system that takes a Part Collection, and a Purchase Collection as the backbone of the application. Each part much have a corresponding purchase. I.E a Part must have a partId, serial number, and cost number associated with it. I am using Meteor.js with coffeescrip, jade, and Graphr. I can insert into each collection individually, but they do not seem connected. I have set up the linkers between the two connection but I am a little lost as to where to go next
here is a snippet of the collections
Purchase Collection
PurchaseInventory.schema = new SimpleSchema
partId:
type:String
optional:true
serialNum:
type:Number
optional:true
costNum:
type:Number
optional:true
Parts Collection/schema
Inventory.schema = new SimpleSchema
name:
type:String
optional:true
manufacturer:
type:String
optional:true
description:
type:String
optional:true
parts query
export getInventory = Inventory.createQuery('getInventory',
$filter: ({ filters, options, params }) ->
if params.filters then Object.assign(filters, params.filters)
if params.options then Object.assign(options, params.options)
return { filters, options , params }
name:1
manufacturer:1
description:1
pic:1
purchase:
partId:1
)
purchase query
export getPurchase = PurchaseInventory.createQuery('getPurchase',
$filter: ({ filters, options, params }) ->
if params.filters then Object.assign(filters, params.filters)
if params.options then Object.assign(options, params.options)
return { filters, options , params }
serial:1
cost:1
date:1
warrentyDate:1
userId:1
)
Linkers
//Parts
Inventory.addLinks
purchase:
collection:PurchaseInventory
inversedBy:"part"
//purchases
PurchaseInventory.addLinks
part:
type:'one'
collection:Inventory
field:'partId'
index: true
And finally the Jade/Pug auto form
+autoForm(class="inventoryForm" schema=schema id="inventoryInsertForm" validation="blur" type="method" meteormethod="inventory.insert")
.formGroup
+afQuickField(name="name" label="Name")
+afQuickField(name="manufacturer" label="Manufacturer")
+afQuickField(name="description" label="Description")
button#invenSub(type="submit") Submit
To reiterate my goal is to have each item in parts to have a link to its corresponding purchase data.
The most straight forward way is to use autoform form type normal and create a custom event handler for the submit event (alternatively you can use the AutoForm hooks onSubmit). From there you can use the AutoForm.getFormValues API function to get the current document.
Since I am not into Coffeescript I would provide the following as Blaze/JS code but I think it should give you the idea:
{{# autoForm type="normal" class="class="inventoryForm" schema=schema id="inventoryInsertForm" validation="blur"" schema=schema id="insertForm" validation="blur" }}
<!-- your fields -->
{{/autoForm}}
/**
* validates a form against a given schema and returns the
* related document including all form data.
* See: https://github.com/aldeed/meteor-autoform#sticky-validation-errors
**/
export const formIsValid = function formIsValid (formId, schema) {
const { insertDoc } = AutoForm.getFormValues(formId)
// create validation context
const context = schema.newContext()
context.validate(insertDoc, options)
// get possible validation errors
// and attach them directly to the form
const errors = context.validationErrors()
if (errors && errors.length > 0) {
errors.forEach(err => AutoForm.addStickyValidationError(formId, err.key, err.type, err.value))
return null
} else {
return insertDoc
}
}
Template.yourFormTempalte.events({
'submit #insertForm' (event) {
event.preventDefault() // important to prevent from reloading the page!
// validate aginst both schemas to raise validation
// errors for both instead of only one of them
const insertDoc = formIsValid('insertForm', PurchaseInventory.schema) && formIsValid('insertForm', Inventory.schema)
// call insert method if both validations passed
Meteor.call('inventory.insert', insertDoc, (err, res) => { ... })
Meteor.call('purchaseInventory.insert', insertDoc, (err, res) => { ... })
}
})
Note, that if you need both inserts to be successful on the server-side you should write a third Meteor method that explicitly inserts a single doc in both collection in one method call. If you have Mongo version >= 4 you can combine this with transactions.

Pass Dynamic Variable to highlightResults in Autocomplete.js

I'm building a typeahead component for my Vue application that searches Algolia, which has several different indexes to search in different places, so I've created props to be passed in to set the input placeholder, search index, and displayKey.
All works well except my highlighting function for suggestions.
I'm sure this is something simple but I can't get the highlight return to pick up the dynamic prop passed in.
$('.typeahead').autocomplete({ hint: false }, [{
source: $.fn.autocomplete.sources.hits(this.client, { hitsPerPage: 5 }),
displayKey: this.display,
templates: {
suggestion: (suggestion) => {
return suggestion._highlightResult.{this.display goes here}.value;
}
}
}]).on('autocomplete:selected', (event, suggestion, dataset) => {
console.log(suggestion, dataset);
})
If I omit the highlighting all works perfectly.
I knew it was simple, call it via array key instead of dot notation.
return suggestion._highlightResult[this.display].value;

Query sailsjs blueprint endpoints by id array using request

I'm using the request library to make calls from one sails app to another one which exposes the default blueprint endpoints. It works fine when I query by non-id fields, but I need to run some queries by passing id arrays. The problem is that the moment you provide an id, only the first id is considered, effectively not allowing this kind of query.
Is there a way to get around this? I could switch over to another attribute if all else fails but I need to know if there is a proper way around this.
Here's how I'm querying:
var idArr = [];//array of ids
var queryParams = { id: idArr };
var options: {
//headers, method and url here
json: queryParams
};
request(options, function(err, response, body){
if (err) return next(err);
return next(null, body);
});
Thanks in advance.
Sails blueprint APIs allow you to use the same waterline query langauge that you would otherwise use in code.
You can directly pass the array of id's in the get call to receive the objects as follows
GET /city?where={"id":[1, 2]}
Refer here for more.
Have fun!
Alright, I switched to a hacky solution to get moving.
For all models that needed querying by id arrays, I added a secondary attribute to the model. Let's call it code. Then, in afterCreate(), I updated code and set it equal to the id. This incurs an additional database call, but it's fine since it's called just once - when the object is created.
Here's the code.
module.exports = {
attributes: {
code: {
type: 'string'//the secondary attribute
},
// other attributes
},
afterCreate: function (newObj, next) {
Model.update({ id: newObj.id }, { code: newObj.id }, next);
}
}
Note that newObj isn't a Model object as even I was led to believe. So we cannot simply update its code and call newObj.save().
After this, in the queries having id arrays, substituting id with code makes them work as expected!

How to call cascading find() in Meteor + MongoDB?

I use Meteor & Iron-router. I have the following data context defined in the router:
data: function() { return {
eventId: this.params._id,
registrants: Registrants.find({eventIds: {$elemMatch: { $in: [this.params._id]}}}, {sort: {name:1, phone:1, email:1}}),
}}
I want to enable Registrants to be filtered further by user input. In my case, I already have ReactiveVar called filterName which listen to input text from user. Whenever the input text changed, the filterName is updated. ( I followed this answer ng-repeat + filter like feature in Meteor Blaze/Spacebars)
Now, I want to add $and to the Registrants.find() method to derive new registrants data context. How should I do it so that the query is reactive to the filterName?
Another approach is by defining Template helper method filteredRegistrants. Initially, its value is the same as return this.registrants. Whenever filterName changed, I would do return this.registrants.find({name: filterName}), but somehow I can't invoke find from registrants cursor, can I? I got undefined is not function error when doing that.
this.registrants is already a cursor (result of Registrants.find()), and not a collection, thus it doesn't have the find() method you look for. However, there is nothing wrong with making another query in the helper if the functionality provided by your controller is not enough:
Template.registrantsTemplate.helpers({
filteredRegistrants: function() {
return Registrants.find(...query...);
},
});

Is data returned from Mongoose immutable?

I want to add to the return data from a mongoose query:
User.findById(userId, function(err, data) {
if (!err) {
data.newvar = 'Hello, world';
}
});
However, when I console log the output, the newvar does not exist. I've also tried this using Underscore's extend:
_.extend(data, {'newvar': 'Hello, world'});
With no luck either. Since I have nested documents, using a shallow copy won't work. Is there any way to append data here?
One way to handle this is to convert your mongoose model instance into a plain object that you have full control over by calling toObject() on it:
User.findById(userId, function(err, data) {
if (!err) {
data = data.toObject();
data.newvar = 'Hello, world';
}
});
If you want a more structured solution, you can add virtual attributes to your schema as described here.
As it turns out, Mongoose documents are their own special class and not standard Javascript objects. In order to get a javascript option that can be extended, you must use the toObject() method.
Now you can use lean() method to return a plain js object:
User.findById(userId)
.lean()
.exec( function(err, data) {
if (!err) {
data.newvar = 'Hello, world';
}
});
Why can't you modify the data returned by a Mongoose Query (ex: findById)