PouchDB code won't work without adding attachment - javascript

I'm building an app to store information items. Currently there are 3 input fields (Title, description and fileinput).
When I fill in all three fields and call the addItem() function, it works. But when I leave the input field empty, the function doesn't add it to the database.
Is there a way to tell that the file input field is not required?
My javascript code:
function addItem() {
//get file
var inputFile = document.querySelector('#inputFile');
var getFile = inputFile.files[0];
//get info
var title = document.getElementById('itemTitle').value;
var desc = document.getElementById('itemDesc').value;
//add
locallp.put({
_id: new Date().toISOString(),
title: title,
description: desc,
_attachments: {
"file": {
content_type: getFile.type,
data: getFile
}
}
}).then(function(){
console.log("Added to the database");
location.href = "menu.html";
}).catch(function(err){
console.log(err);
});
}
some extra info, I'm using Cordova to build the app. My database is PouchDB and connected via couchperuser to a CouchDB server.

I'd be surprised that your second line (inputFile.files[0]) even works if you don't provide a file. In any case, I'm sure the getFile.type method call will fail if there's no file.
You need some logic in this method so that it does two different things based on whether there's a file supplied or not. If there is one then it does what you have, if there isn't then it doesn't try adding the _attachments and probably skips the whole setting of getFile too.

Related

Meteor remote collection - hooks don’t work

I have to connect to the external database and get access to its collections. It works fine, when I use it, but the problem is when I need collection hooks, e.g. Collection.after.insert(function(userId, doc)). The hook is not being fired. I have following code:
// TestCollection.js
let database = new MongoInternals.RemoteCollectionDriver("mongodb://127.0.0.1:3001/meteor",
{
oplogUrl: 'mongodb://127.0.0.1:3001/local'
});
let TestCollection = new Mongo.Collection("testCollection", { _driver: database });
module.exports.TestCollection = TestCollection;
console.log(TestCollection.findOne({name: 'testItem'})); // writes out the item correctly
// FileUsingCollection.js
import { TestCollection } from '../collections/TestCollection.js';
console.log(TestCollection.findOne({name: 'testItem'})); // writes out the item correctly second time
TestCollection.after.update(function (userId, doc) {
console.log('after update');
}); // this is NOT being fired when I change the content of remote collection (in external app, which database I am connected)
How to make this work?
EDIT:
I have read many hours about it and I think it might be connected with things like:
- oplog
- replicaSet
But I am newbie to Meteor and can’t find out what are those things about. I have set MONGO_OPLOG_URL and I added oplog parameter to database driver as I read here: https://medium.com/#lionkeng/2-ways-to-share-data-between-2-different-meteor-apps-7b27f18b5de9
but nothing changed. And I don’t know how to use this replicaSet, how to add it to the url. Anybody can help?
You can also try something like below code,
var observer = YourCollections.find({}).observeChanges({
added: function (id, fields) {
}
});
You can also have 'addedBefore(id, fields, before)', 'changed(id, fields)', 'movedBefore(id, before)', 'removed(id)'
For more features goto link.

Meteor this.unblock in ValidatedMethod

I have a validated method:
export const updateSuitClass = new ValidatedMethod({
name: 'presets.suitClass.update',
validate: new SimpleSchema({
_id: { type: String },
rankClass: { type: String },
suitClass: { type: String },
}).validator(),
run({ _id, rankClass, suitClass }) {
const userId = Meteor.userId();
if (userId) {
const lookUpPreset = Presets.findOne({ _id });
if (userId === lookUpPreset.owner) {
Presets.update(_id, { $set: { [`${rankClass}.${suitClass}`]: !lookUpPreset[rankClass][suitClass] } });
} else {
throw new Meteor.Error('not-authorized', 'Please don\'t update another user\'s preset.');
}
} else {
throw new Meteor.Error('not-authorized', 'Please login to update your preset.');
}
},
});
that gets called on a click event (on an item in a list and toggles a check mark next to it to indicate checked) to save state of user's configuration settings. Problem is, it gets called as user clicks clicks and clicks so it will get called quite frequently.
First question, is it bad to make so many method calls to server to update a portion at a time? Should I just put a save button (ew!) and do a single mass update?
Second question, if I were to keep the same method code as is but add a this.unblock or Meteor.defer, how do I do that to a validated method? I tried putting it after run, before run, before the whole block...
Can you guys help?
First question, is it bad to make so many method calls to server to update a portion at a time? Should I just put a save button (ew!) and do a single mass update?
If you want to avoid massive clicks from a single user, use ddp-rate-limiter package and create a rule for your method. With this package you can limit the calls on the server by a time period.
Second question, if I were to keep the same method code as is but add a this.unblock or Meteor.defer, how do I do that to a validated method? I tried putting it after run, before run, before the whole block...
ValidatedMethod run function works the same way as a method function. So your just need to add this.unblock inside the run function.
Hope it helps!

adding data based on users login credentials (Lightswitch HTML)

I've been doing some research into how I can add data based on the login credentials. as an example scenario lets say I want a user to login to the application and then based on there login, add there hours they have done for that day, so like a timesheet application.
I don't want the user that is logged in to see any other names other
than there own.
the browse screen would show only there times they have submitted
rather than everyones aswell.
when using the insert call method in (_customnameDataService.cs) you can add in a username by associating a field within a table like below:
entity.Username = Application.User.Name
so if this is possible there must be a way of calling this in JavaScript when logging in so any help or pointers would be great help. Adding a DataItem and displaying the username would be most preferable. (using edit render code) then from this I can pass it through the hierarchy and display only the information associated with the logged in user.
follow these steps to achieve the above question:
Google GetUserName.ashx to get the code for this file to add to your
Lightswitch HTML Project.
copy the below function into the javascript file (in my case a Browse screen for my users)
function CallGetUserName(operation) {
$.ajax({
type: 'post',
data: {},
url: '../web/GetUserName.ashx',
success: operation.code(function AjaxSuccess(AjaxResult) {
operation.complete(AjaxResult);
})
});
}
For the users that can login in and access the Lightswitch Application, the user information must be stored somewhere, in my case "tbl_Users". This table has a row called username. Using the below code this enables an administrator or someone high up in the hierarchy to access all the users, and also the specific user referenced in the table to access themselves.
myapp.BrowseUsers.username_postRender = function (element,
contentItem) {
msls.promiseOperation(CallGetUserName).then(function PromiseSuccess(PromiseResult) {
if (PromiseResult == 'TestUser' || PromiseResult == 'administrator') {
} else {
contentItem.value = PromiseResult;
}
});
};
What is actually happening?
The function is calling the GetUserName.ashx file, which in turn retrieves the current user logged in. (from the aspnet_Users table which is automatically created) I have used a foreign key to associated my table (tbl_Users) and aspnet_Users together.
in the debug or release environment if you were to add a data item (string) and display this information, it would return ("TestUser")
myapp.BrowseUsers.displayUsername_postRender = function (element,
contentItem) {
msls.promiseOperation(CallGetUserName).then(function PromiseSuccess(PromiseResult)
{
element.innerText = PromiseResult;
}); };

Renamed a file uploaded via Angularjs

In a nutshell, I need to rename files uploaded via Angular and Node and make that new name available to both the back and front ends (I think). What is the best practice for doing so?
I am using a file upload plugin (https://github.com/danialfarid/angular-file-upload) in an Angularjs app. The upload hits a route handled by Nodejs, uploads the file then, on success, angular saves the file name into a users account. However, if two people upload a file of the same name there will be a conflict so I need to rename each file (perhaps by adding a date and time).
My code works as follows:
Angular handles the upload, hitting a route that node can use.
Node move the file into place.
on success, angular saves the file name into the user's account.
It appears I cannot rename the file prior to uploading. If I rename the file via node after it has been moved into place I run into the issue of needing to get that new name back to angular for saving into the database. Not sure of the best way to approach this process.
Here is my current file upload code (works fine, just doesn't rename the file):
In Angular...
$scope.onFileSelect = function ($file) {
var photo = $file[0];
$scope.upload = $upload.upload({
url: '/upload/userphoto',
headers: {'Content-Type': photo.type},
file: photo,
method: 'POST'
}).progress(function (evt) {
//removed for brevity
}).success(function (data, status, headers, config) {
User.currentUser.user_profile_image = photo.name;
User.updateUser();
}).error(function (err) {
//removed for brevity
});
};
The post route, /upload/userphoto is matched in node with:
exports.uploadProfilePhoto = function(req, res) {
var callbacks = {};
callbacks.uploadSuccess = function(){
res.json('success');
};
callbacks.uploadFailure = function(err){
//removed for brevity
};
user.handleUserImageUpload(req.files, callbacks);
};
Which sets some callbacks and then hits handleUserImageUpload() which is:
this.handleUserImageUpload = function(params, callbacks) {
fs.readFile(params.file.path, function(err, data){
if(err){
callbacks.uploadFailure(err);
}
var newPath = path.resolve("./public/fileupload/userphotos/" + params.file.filename);
fs.writeFile(newPath, data, function(err) {
if(err){
callbacks.uploadFailure(err);
}
callbacks.uploadSuccess();
});
});
};
Which, assuming success, takes us back to the success handler of the first bit of Angular code above. I know I can rename the file in the handleUserImageUpload() method with fs.rename() like (just a hard coded example... I would actually take into account the file type and existing name plus add a random string or account identifier):
var renamedPath = path.resolve("./public/fileupload/userphotos/" + "abc123.jpg");
fs.rename(newPath, renamedPath);
But then I don't have that new name available when I go to save the file info into the user's account. I guess I could pass it back in the success callback? Is that the best way or am I missing something?
The solution turned out to be what I suspected... pass the value in the callback. So I added the following to handleUserImageUpload()...
// grab the extension
var fileExtension = '.' + params.file.filename.split('.').pop();
// rename the file with a sufficiently random value and add the file extension back
var renamedFile = Math.random().toString(36).substring(7) + new Date().getTime() + fileExtension;
//set up the new path for use in fs.rename()
var renamedPath = path.resolve("./public/fileupload/userphotos/" + renamedFile);
// Make it so
fs.rename(newPath, renamedPath);
// pass the new name to the callback
callbacks.uploadSuccess(renamedFile);
In the function that created the callbacks, uploadProfilePhoto(), I needed to make one important change, swap json with send (otherwise I would get quotation marks added to the value)
//res.json('success');
res.send(renamedFile);
Then I could access the this name as the data value in my Angular success handler.

Add a new field to a document mongodb

I am very new to mongodb and have a basic question that I am having trouble with. How do I get the ID field of a document that has already been created? I need the ID so i can update/add a new field to the document.
//newProfile is an object, one string it holds is called school
if(Schools.find({name: newProfile.school}).fetch().length != 1){
var school = {
name: newProfile.school
}
Meteor.call('newSchool', school);
//Method 1 (doesn't work)
var schoolDoc = Schools.findOne({name: newProfile.school});
Schools.update({_id: schoolDoc._id}, {$set: {enrolledStudents: Meteor.user()}});
//Method 2?
//Schools.update(_id: <what goes here?>, {$push: {enrolledStudents: Meteor.user()}});
}
else {
//Schools.update... <add users to an existing school>
}
I create a new school document if the listed school does not already exist. Schools need to hold an array/list of students (this is where i am having trouble). How do I add students to a NEW field (called enrolledStudents)?
Thanks!
I'm having some trouble understanding exactly what you're trying to do. Here's my analysis and understanding so far with a couple pointers thrown in:
if(Schools.find({name: newProfile.school}).fetch().length != 1){
this would be more efficient
if(Schools.find({name: new Profile.school}).count() != 1) {
Meteor.call('newSchool', school);
Not sure what you're doing here, unless you this will run asynchronously, meaning by the time the rest of this block of code has executed, chances are this Meteor.call() function has not completed on the server side.
//Method 1 (doesn't work)
var schoolDoc = Schools.findOne({name: newProfile.school});
Schools.update({_id: schoolDoc._id}, {$set: {enrolledStudents: Meteor.user()}});
Judging by the if statement at the top of your code, there is more than one school with this name in the database. So I'm unsure if the schoolDoc variable is the record you're after.
I believe you are having trouble because of the asynchronous nature of Meteor.call on the client.
Try doing something like this:
// include on both server and client
Meteor.methods({
newSchool: function (school) {
var newSchoolId,
currentUser = Meteor.user();
if (!currentUser) throw new Meteor.Error(403, 'Access denied');
// add some check here using the Meteor check/match function to ensure 'school'
// contains proper data
try {
school.enrolledStudents = [currentUser._id];
newSchoolId = Schools.insert(school);
return newSchoolId;
} catch (ex) {
// handle appropriately
}
}
});
// on client
var schoolExists = false;
if (Schools.findOne({name: newProfile.school})) {
schoolExists = true;
}
if (schoolExists) {
var school = {
name: newProfile.school
};
Meteor.call('newSchool', school, function (err, result) {
if (err) {
alert('An error occurred...');
} else {
// result is now the _id of the newly inserted record
}
})
} else {
}
Including the method on both the client and the server allows Meteor to do latency compensation and 'simulate' the insert immediately on the client without waiting for the server round-trip. But you could also just keep the method on the server-side.
You should do the enrolledStudents part on the server to prevent malicious users from messing with your data. Also, you probably don't want to actually be storing the entire user object in the enrolledStudents array, just the user _id.
For what you're trying to do, there is no need to get the _id. When you use update, just switch out the {_id: schoolDoc._id} with your query. Looks like using {name: newProfile.school} will work, assuming that the rest of your code does what you want it to do.
While that would work with the normal Mongo driver, I see that Meteor does not allow your update query to be anything but _id: Meteor throws throwIfSelectorIsNotId exception
First, make sure that you're pulling the right document, and you can try something like this:
var school_id = Schools.findOne({name: newProfile.school})._id;
Schools.update({_id: school_id}, { $push: { enrolledStudents: Meteor.user()}});
If that doesn't work, you'll have to do a little debugging to see what in particular about it isn't working.

Categories

Resources