How to reference another collection on insert? - javascript

I'm trying to figure how to take an Image (a file using CollectionFS) and insert the Image's Id into my Items imageId field:
lib/collections/items.js
Items = new Mongo.Collection("items");
Items.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Name",
},
userId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
autoform: {
type: "hidden",
label: false
},
autoValue: function () { return Meteor.userId() },
},
image: {
type: String,
optional: true,
autoform: {
label: false,
afFieldInput: {
type: "fileUpload",
collection: "Images",
label: 'Select Photo',
}
}
},
imageId: {
type: String
}
}));
lib/collections/images.js
if (Meteor.isServer) {
var imageStore = new FS.Store.S3("images", {
accessKeyId: Meteor.settings.AWSAccessKeyId,
secretAccessKey: Meteor.settings.AWSSecretAccessKey,
bucket: Meteor.settings.AWSBucket,
});
Images = new FS.Collection("Images", {
stores: [imageStore],
filter: {
allow: {
contentTypes: ['image/*']
}
}
});
}
// On the client just create a generic FS Store as don't have
// access (or want access) to S3 settings on client
if (Meteor.isClient) {
var imageStore = new FS.Store.S3("images");
Images = new FS.Collection("Images", {
stores: [imageStore],
filter: {
allow: {
contentTypes: ['image/*']
},
}
});
}
Right now my allow rules are:
server/allows.js
Items.allow({
insert: function(userId, doc){return doc && doc.userId === userId;},
update: function(userId, doc){ return doc && doc.userId === userId;},
remove: function(userId, doc) { return doc && doc.userId === userId;},
})
Images.allow({
insert: function(userId, doc) { return true; },
update: function(userId,doc) { return true; },
remove: function(userId,doc) { return true; },
download: function(userId, doc) {return true;},
});
I'm using Autoform so my form looks like this:
client/item_form.html
<template name="insertItemForm">
{{#autoForm collection="Items" id="insertItemForm" type="insert"}}
{{> afQuickField name="name" autocomplete="off"}}
{{> afQuickField name="image" id="imageFile"}}
<button type="submit">Continue</button>
{{/autoForm}}
</template>
Right now when I select browse and select an image it will be in the database and I want to take that _id it has and place it in the Item that is created afterwards, but how do I fetch that particular image? I figured this is a good way to reference an image.
UPDATE 1
Find out the ID is actually located hidden after a file is selected:
<input type="hidden" class="js-value" data-schema-key="image" value="ma633fFpKHYewCRm8">
So I'm trying to get ma633fFpKHYewCRm8 to be placed as a String in the ImageId.
UPDATE 2
Maybe one way is to use FS.File Reference?

I have solved the same problem rather simpler, after file is inserted, I just call a method that does the related collection update:
client.html
<template name="hello">
<p>upload file for first texture: <input id="myFileInput1" type="file"> </p>
</template>
lib.js
var textureStore = new FS.Store.GridFS("textures");
TextureFiles = new FS.Collection("textures", {
stores: [textureStore]
});
Textures = new Mongo.Collection("textures");
client.js
Template.hello.events({
'change #myFileInput1': function(event, template) {
uploadTextureToDb('first',event);
}
});
function uploadTextureToDb(name, event) {
FS.Utility.eachFile(event, function(file) {
TextureFiles.insert(file, function (err, fileObj) {
// Inserted new doc with ID fileObj._id, and kicked off the data upload using HTTP
console.log('inserted');
console.log(fileObj);
//after file itself is inserted, we also update Texture object with reference to this file
Meteor.call('updateTexture',name,fileObj._id);
});
});
}
server.js
Meteor.methods({
updateTexture: function(textureName, fileId) {
Textures.upsert(
{
name:textureName
},
{
$set: {
file: fileId,
updatedAt: Date.now()
}
});
}
});
as you are using autoForm and simpleSchema, it might not be so easy, but I suggest to you to forget about autoForm and simpleSchema at first and try to make it work with simple html and default collections.
After everything works, you can go back to setting up those, but beware that there might be more issues when it comes to CollectionFS, especially when it comes to styling generated by autoForm.

Related

How to retrieve Image in Jade from Meteor FS Collection

I am working on a existing Meteor project. I have a Schema as shown below,
Activities.schema = new SimpleSchema({
title: {
type: String,
label: 'Title',
index: 1,
},
export const ActivitiesImages = new FS.Collection('activitiesImages', {
stores: [
new FS.Store.GridFS('homeActivitiesImages', { transformWrite: homeActivityImage }),
new FS.Store.GridFS('origActivitiesImages', {}),
],
filter: {
maxSize: 1024 * 1024 * 5,
allow: {
contentTypes: ['image/*'],
},
},
});
.........
)}
And I found Insert code as below
const insertObj = {
title,
description,
type: [type],
divisions: [],
clubType,
createdBy,
privateType,
};
if (image) {
insertObj.image = ActivitiesImages.insert(image);
}
Meteor.call('v1/insertActivity', insertObj, (error) => {
if (error) {
sAlert.error(error.message);
} else {
sAlert.success('Activity added');
$('.activity-modal').modal('hide');
}
});
Now My requirement is, I wanted to display the above stored image in the below Jade Template. I am finding difficulties in rendering the same.
MyProfile.tpl.jade (Jade Template)
each activity in fiveStarActivities
.slider__item
.ui.segment
.ui.vertical.segment
h3.ui.header.center.aligned {{activity.title}}
.ui.list
.item
img(
src="{{HERE I WANT TO DISPLAY IMAGE}}"
alt="image description"
).ui.fluid.rounded.image
.item
.ui.vertical.segment.big-card__btn-src
button.ui.mini.grey.basic.button {{activity.title}}
.item
p {{activity.description}}
I have tried the below but did not work.
MyProfile.js
activityImage(activity) {
return ActivitiesImages.findOne({ _id: activity.image._id });
},
Can someone assist me on how to display the image on Jade Template ?
I have resolved this by changing template as shown below.
MyProfile.tpl.jade
each activity in fiveStarActivities
.slider__item
.ui.segment
.ui.vertical.segment
h3.ui.header.center.aligned {{activity.title}}
.ui.list
if activityImage activity
with activityImage activity
.item
img(
src="{{this.url store='homeActivitiesImages'}}"
alt="image description"
).ui.fluid.rounded.image.medium
MyProfile.js
ActivitiesImages.findOne({ _id: activity.image._id });

Knockout and jQuery Validation Remote: not getting latest value

I have a knockout viewModel and am wiring up jQuery Validation for it. One of the values, code, I want a remote check to ensure it's not already in use. The problem is that in my method for the remote validation, the self.code() call is returning the old value instead of the new one.
My Validate code (note I also tried a "more direct" method of getting the value, to no avail - same result):
form.validate({
rules: {
'plandetail-code': {
required: true,
remote: {
url: '/Plans/ValidatePlanCode',
type: 'POST',
data: {
id: self.id(),
code: self.code() //form.find('[name="plandetail-code"]').val()
}
}
},
'plandetail-name': "required"
}
});
Relevant Html:
<div class="form-group">
<label for="plandetail-code">Code</label>
<input type="text" name="plandetail-code" data-bind="textInput: code" class="form-control" />
</div>
My controller action is simple, but note that code always comes through as the original value:
[HttpPost]
public string ValidatePlanCode(int? id, string code) {
return _service.ValidatePlanCode(id, code) ? "true" : "false";
}
And here's my viewmodel: I run the form.Validate({}) before applying bindings (tried putting that after as well), and in the saveChanges method I check form.valid():
function PlanDetailVM(model) {
var self = this;
self.originalModel = model;
self.form = $('#pgPlan-plan-detail-form');
self.id = ko.observable(model.ID);
self.active = ko.observable(model.Active);
self.code = ko.observable(model.Code);
self.name = ko.observable(model.Name);
self.notes = ko.observable(model.notes);
self.dirty = ko.computed(function () { return isDirty(); });
self.save = function () { saveChanges(); }
self.cancel = function () { cancelChanges(); }
ko.applyBindings(self, document.getElementById('pgPlan-detail-container'));
initValidation(self.form);
return self;
function initValidation(form) {
form.validate({
rules: {
'plandetail-code': {
required: true,
remote: {
url: '/Plans/ValidatePlanCode',
type: 'POST',
data: {
id: self.id(),
code: self.code() //form.find('[name="plandetail-code"]').text()
}
}
},
'plandetail-name': "required"
}
});
}
function isDirty() { ... }
function saveChanges() {
if (!self.form.valid()) {
return;
}
// ajax snipped
}
function cancelChanges() { ... }
}
Repro:
Load initial view, Code has value AAAA
Change Code to BBBB
Observe controller action called
Controller action code param = AAAA
I'm unsure why I can't get the latest value from the text input. Am I missing something? Thanks
rules is an object which is evaluated immediately, so the data object will get created with default values if you use self.id() (since it returns value not function)
so you need to use it as functions
form.validate({
rules: {
'plandetail-code': {
required: true,
remote: {
url: '/Plans/ValidatePlanCode',
type: 'POST',
data: {
id: self.id, // function evaluated at runtime
code: self.code
}
}
},
'plandetail-name': "required"
}
});

Meteor AutoForm stops proceeding submit

I would like to create a form, using the autoform package for Meteor, for my CAS_Entry collection. The code can be seen below. I also added the defined hooks, of which unfortunately only beginSubmit and before are executed and no entry is added to the collection. Using Meteor shell, the insert works like a charm.
I am grateful for any hint.
addCasEntry.html, Template for displaying the form:
{{#autoForm collection="CAS_Entry" type="insert" id="addCasEntryForm"}}
{{> afQuickField name="type" options="allowed"}}
{{> afQuickField name="description" rows="6" type="textarea"}}
{{> afQuickField name="file" type="cfs-file" collection="Images"}}
{{> afQuickField name="date" }}
<button type="submit" class="btn btn-primary">Add</button>
{{/autoForm}}
addCasEntry.js, adding debugging hooks:
AutoForm.hooks({
addCasEntryForm: {
before: {
insert: function(doc) {
console.log(doc);
}
},
after: {
insert: function(error, result) {
console.log('Occured error: ' + error);
}
},
beginSubmit: function() {
console.log('begin submit');
},
onSuccess: function(formType, result) {
console.log("Insert succeeded");
console.log('Result ' + result);
},
onError: function(formType, error) {
console.log('Error!!!');
console.log(error);
}
}
});
SimpleSchema.debug = true;
/lib/collection/cas_entry.js:
CAS_Entry = new Mongo.Collection("cas_entries");
CAS_Entry.attachSchema(new SimpleSchema({
type: {
type: String,
allowedValues: ['reflection', 'evidence']
},
description: {
type: String,
optional: true
},
file: {
type: String,
optional: true,
},
timeUploaded: {
type: Date,
optional: true,
autoValue: function() {
return new Date();
}
},
date: {
type: Date,
}
}));
CAS_Entry.allow({
'insert': function() {
return true;
},
'update': function() {
return true;
}
});
And here is the console output:
Your form won't be submitted because you are not returning or passing the document to this.result(); inside your before hook.
AutoForm.hooks({
addCasEntryForm: {
// ...
before: {
insert: function(doc) {
console.log(doc);
return doc;
}
}
// ...
}
});
According to the documentation, you should use one of the following statements depending on your defined preconditions:
Synchronous, submit: return doc;.
Synchronous, cancel: return false;.
Asynchronous, submit: this.result(doc);.
Asynchronous, cancel: this.result(false);.

Rails / typeahead.js on huge database

I am using typeahead.js to fill in a belongs_to field on a Rails form. When you create a Post, you pick the associated Book. There are thousands of records. The app is crashing on that page if I let typeahead.js look at Book.all, but if I limit to a couple hundred with Book.featured, it seems to work. How can I give the typeahead access to all the data, though?
/api/books.json returns all the records, not sure if that's helpful?
Here is how I have it set up:
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
cb(strs);
};
};
books = <%= Book.featured.to_json.html_safe %>
var bookFinder = function(books) {
$('#post_book_id').val(null)
// $('#post_book_name').val(null)
return function findMatches(q, cb) {
matches = [];
$.each(books, function(i, book){
if(book.title.indexOf(q) !== -1){
matches.push(book)
}
})
cb(matches)
}
}
$('#post_book_name').typeahead({
hint: true,
highlight: true,
minLength: 2
},
{
name: 'books',
displayKey: 'title',
source: bookFinder(books)
});
$('#post_book_name').on('typeahead:select', function(ev, suggestion) {
$('#post_book_id').val(suggestion.id)
});

Backbone collection fetch not firing

I'm new to backbone and I'm trying to send and receive data from the server in Json format. It just won't work. Here's my code (BTW, I'm using backbone aura):
Collection
define(['sandbox', '../models/message'], function(sandbox, Message) {
'use strict';
var Messages = sandbox.mvc.Collection({
model: Message,
url: '/messagelist.php',
localStorage: new sandbox.data.Store('messages-backbone-require'),
parse: function(response){
return response.rows;
}
});
return Messages;
});
Model
define(['sandbox'], function(sandbox) {
'use strict';
var Message = sandbox.mvc.Model({
defaults: {
opened: '',
messageid: '',
phonenumber: '',
numbername: '',
text: ''
},
parse: function(data){
return data;
}
});
return Message;
});
View
define(['sandbox', '../models/message', 'text!../templates/incoming_messages.html'], function(sandbox, Message, incomingMessagesTemplate) {
'use strict';
var AppView = sandbox.mvc.View({
widgetTemplate: sandbox.template.parse(incomingMessagesTemplate),
events: {
'click .refresh': 'refresh'
},
initialize: function() {
this.$el.html(this.widgetTemplate);
sandbox.events.bindAll(this);
this.collection.bind('createMessageList', this.createMessageList);
},
createMessageList: function() {
// Will work with the received data here
},
render: function() {
var handle = 'h4';
this.$el.draggable({handle: handle});
this.createMessageList();
},
refresh: function() {
this.createMessageList();
}
});
return AppView;
});
Main
define(['sandbox', './views/app', './collections/messages'], function(sandbox, AppView, Messages) {
'use strict';
return function(options) {
var messages = new Messages();
new AppView({
el: sandbox.dom.find(options.element),
collection: messages
}).render();
messages.fetch({
data: {
type: 'incoming',
offset: 0,
offsetcount: 25
},
type: 'GET',
success: function() {
console.log(messages.models); // Shows an empty array.
}
});
};
});
I've check logs and it seems that the ajax request (collection.fetch()) is not firing or is not able to communicate with the server. How can I fix this?
The problem is with the Backbone.LocalStorage plugin. When you assign Collection.localStorage, the plugin takes over the fetch command and reads the data from local storage instead of the server.
See my answer in this SO question on some options on how to solve this.

Categories