I cannot seem to find any documentation that will explain how I can get the filename and filepath of an uploaded collectionFS image into my meteor method.
I am able to get the image URL on the client side no problem using helpers, but I cannot seem to figure out how I can send the filename and filepath of the attached image to my method.
Method JS
Meteor.methods({
addQuote: function(data) {
check(data, Object);
var attachments = [];
var html = html;
// need to get the filename and filepath from collectionFS
// I would then have the data go here
attachments.push({filename: , filePath: });
this.unblock();
var email = {
from: data.contactEmail,
to: Meteor.settings.contactForm.emailTo,
subject: Meteor.settings.contactForm.quoteSubject,
html: html,
attachmentOptions: attachments
};
EmailAtt.send(email);
}
});
Controller JS
function ($scope, $reactive, $meteor) {
$reactive(this).attach($scope);
this.user = {};
this.helpers({
images: () => {
return Images.find({});
}
});
this.subscribe('images');
this.addNewSubscriber = function() {
// Uploads the Image to Collection
if(File.length > 0) {
Images.insert(this.user.contactAttachment);
console.log(this.user.contactAttachment);
}
// This is the variable I use to push to my method
// I image I need to push the filename and filepath also
// I am unsure how to access that information in the controller.
var data = ({
contactEmail: this.user.contactEmail,
contactName: this.user.contactName,
contactPhone: this.user.contactPhone,
contactMessage: this.user.contactMessage
});
// This will push the data to my meteor method "addQuote"
$meteor.call('addQuote', data).then(
function(data){
// Show Success
},
function(err) {
// Show Error
}
);
};
You can use the insert callback to get this informations:
Images.insert(fsFile, function (error, fileObj)
{
if (error) console.log(error);
else
{
console.log(fileObj);
//Use fileObj.url({brokenIsFine: true}); to get the url
}
});
Related
I have articles which i'm getting from my API. My API lists them correctly when i go to http://localhost:60367/api/article/ and gets the correct data correctly for a single item when i go to http://localhost:60367/api/article/1
Using angular, how get the data for one of these articles by it's id so that if i go to my angular app and click to http://localhost:60300/perspectives/1/ I get the data of that one item. ( fyi, When i go to the index http://localhost:60300/perspectives/ I get the data accordingly. )
Please assist, my app.js file is below:
var url = "http://localhost:60367/api/article";
var modules = ['ngRoute', 'ngSanitize'];
var App = angular.module("App", modules);
// Route providers
App.config(function ($routeProvider, $locationProvider) {
$routeProvider
// Get route for perspectives homepage
.when('/', {templateUrl: 'partials/articles-home.html',
controller: ArticleController})
// Get route for perspectives single page
.when("/:id/", {templateUrl: 'partials/articles-single.html',
controller: ArticleController})
.otherwise({ redirectTo : "/"})
// Use the HTML5 History API
$locationProvider.html5Mode({ enabled: true, requireBase: false});
});
// Controller
var ArticleController = function ($scope, $http, $log) {
// For onsuccess, also do console.log for $log property
var onSuccess = function (response) {$scope.articles = response.data;
$log.info(response);};
var onFailure = function (reason) {$scope.error =
reason;$log.info(reason);};
// Get all students and display them in index
var getAllArticles = function () {$http.get(url).then(onSuccess,
onFailure)};
getAllArticles();
// Get single student by Id
//
//
};
App.controller("ArticleController", ArticleController);
SOLUTION:
Ok this is how I solved it, I created a new controller for the single item and wrote it manually like this:
var SingleArticleController = function ($scope, $http, $routeParams) {
$http({
url: "http://localhost:60367/api/article/{id}",
params: { id: $routeParams.id },
method: "get"
})
.then(function (response) {
$scope.article = response.data;
});
};
You'll want to use $routeParams:
What I've outline here will allow you to use the same controller here as that's what you've show in your config. Often-times, you'd assign a separate controller in your route (something like ArticleController, ArticleListController.). If you do that, the same process applies, but you wouldn't need to check if you have an ID parameter.
In your Controller:
// Add $routeParams
.controller('ArticleController', function($scope, $routeParams) {
// Get the id
var id = $routeParams.id;
// Set url based on whether or not you have an ID
var fullUrl = id ? url + '/' + id : url;
var getAllArticles = function() {
$http.get(fullUrl).then(onSuccess,
onFailure)
};
})
I've got a server side route I'm using to download a file. This is called from a client side button click and everything is working fine. However, once the button has been clicked once it will not work again until another route is loaded and you go back. How can I code it so that the button can be clicked multiple times and the server side route be fired each time?
My button code looks like this...
'click #view_document_download': function (event, tmpl) {
Router.go('/download_document/' + this._id);
}
And my server side route looks like this...
Router.route('/download_document/:_id', function () {
//Get the file record to download
var file = files.findOne({_id: this.params._id});
//Function to take a cfs file and return a base64 string
var getBase64Data = function(file2, callback) {
var readStream = file2.createReadStream();
var buffer = [];
readStream.on('data', function(chunk) {
buffer.push(chunk);
});
readStream.on('error', function(err) {
callback(err, null);
});
readStream.on('end', function() {
callback(null, buffer.concat()[0].toString('base64'));
});
};
//Wrap it to make it sync
var getBase64DataSync = Meteor.wrapAsync(getBase64Data);
//Get the base64 string
var base64str = getBase64DataSync(file);
//Get the buffer from the string
var buffer = new Buffer(base64str, 'base64');
//Create the headers
var headers = {
'Content-type': file.original.type,
'Content-Disposition': 'attachment; filename=' + file.original.name
};
this.response.writeHead(200, headers);
this.response.end(buffer, 'binary');
}, { where: 'server' });
use a element instead of js 'click' event
page html
page js in server
Router.route("/download_document/:fileId", function(){
var file = files.findOne({_id: this.params.fileId});
var contentFile = //file text
let headers = {
'Content-Type': 'text/plain',
'Content-Disposition': "attachment; filename=file.txt"
};
this.response.writeHead(200, headers);
this.response.end(contentFile);
},
{where: "server", name: "download"}
);
Maybe you should just return an Object from your Server via a method and form it to a file on the client side? if possible..
To create a file on the client side is really simple, and you don't have to deal with Routers at this point.
function outputFile(filename, data) {
var blob = new Blob([data], {type: 'text/plain'}); // !note file type..
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
}
else{
var elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem)
elem.click();
document.body.removeChild(elem);
}
}
function getContentAndOutputFile() {
var content = document.getElementById('content').value;
outputFile('file.txt', content);
}
<input id="content" value="test content"/>
<button onClick="getContentAndOutputFile()">Create File</button>
I am using Autoform and Slingshot for my S3 interaction. When the user submits the form, i want to intercept the process, upload the file to S3 through Slingshot, extend the doc object with the returned downloadUrl and then at that point, return the new updated doc, and continue the autoform process
I have the following code:
{{#autoForm collection="Tabs" id="newTabForm" type="method" meteormethod="createTab"}}
...
<div class="modal-body">
<fieldset>
{{> afFormGroup name='downloadUrl' type='file' class='file-bag'}}
...
AutoForm.hooks({
newTabForm: {
before: {
insert: function(doc, template) {
console.log(doc);
var file = $('.file-bag')[0].files[0];
var self = this;
uploader.send(file, function(error, downloadUrl) {
if (error) { throw new Meteor.Error(error); }
doc = _.extend(doc, { downloadUrl: downloadUrl });
self.result(doc);
});
}
},
....
Meteor.methods({
createTab: function(doc) {
check(doc, TabSchema);
var priceInCents = doc.price * 100;
var extraTabAttributes = {
userId: Meteor.userId(),
price: priceInCents
};
_.extend(doc, extraTabAttributes);
Tabs.insert(doc, function(error, result) {
if (error) { return error; }
});
}
Which correctly stores url (however looks weird, C://fakepath/filename..) on the document, but fails to upload it to the S3 server
Also side question, why doesnt the console.log(doc); in the before hooks log anything to the client/server?
I'm not familiar with auto form but I think your before hook is incorrect.
From https://github.com/aldeed/meteor-autoform#callbackshooks , it said
before: {
// Replace `formType` with the form `type` attribute to which this hook applies
formType: function(doc) {}
}
So in your case,
insert: function(doc, template)
Should be replaced with
method: function(doc, template)
I'm building a meteor app and on one route I'm adding multiple data context like so -
this.route('orgPage', {
path: '/org/:title',
data: {
orgs: function () {Orgs.findOne(this.params._id)},
projects: function() {Meteor.subscribe('projects', this.params._id)}
}
The only problem is that when I try to access this data in my templates js file, I can't access the _id or any of the attributes of orgs.
I've tried several approaches, but it always returns undefined. If I use a single data context, it works perfectly. Here is the function that doesn't function properly -
Template.orgPage.events({
'click #newProject': function(e) {
$('#npModal').modal();
},
'submit #npModal form': function(e, template) {
e.preventDefault();
if(!$(e.target).find('[name=newTitle]').val()) {
var projectTitle = 'Untitled'
} else {
var projectTitle = $(e.target).find('[name=newTitle]').val()
}
var theid = this._id;
var newProject = {
title: projectTitle,
organization: theid
}
Meteor.call('project', newProject, function(error, id) {
if (error)
return alert(error.reason);
$('#npModal').modal('hide');
$('#npModal').on('hidden.bs.modal', function (e) {
Router.go('newFields', {});
})
});
});
Anyone have any ideas? Thanks!!
You have missed a return statement. function () {Orgs.findOne(this.params._id)} should be function () {return Orgs.findOne(this.params._id)}. Further more, this inside this function won't refer to what you want, so you can't use this.params. And why do you subscribe to a subscription as a data context property? Do it in the waitOn function instead.
The following code does not update the database everytime a tweet is found - it silently hangs, adding no tweets to the database.
If a tweet is manually added to the DB from the JS console in the browser, it shows up just fine, but no tweets are being added to the DB automatically.
Tweets = new Meteor.Collection("tweets");
if (Meteor.isClient) {
Template.kildeer.tweets = function () {
return Tweets.find({});
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
var require = __meteor_bootstrap__.require,
Twit = require('twit')
, T = new Twit({
consumer_key: 'blahblah',
consumer_secret: 'blahblah',
access_token: 'blahblah',
access_token_secret: 'blahblah'
});
var stream = T.stream('statuses/filter', { track: ['bing', 'google', 'microsoft'] })
stream.on('tweet', function (tweerp) {
var id;
console.log(tweerp.text);
id = Tweets.insert({text: tweerp.text, screen_name: tweerp.user.screen_name, profile_image: tweerp.user.profile_image_url});
console.log(id);
});
});
}
In Meteor, Collection.insert must always be called inside of a Fiber() closure.
Fiber(function() {
Tweets.insert({text: tweerp.text, screen_name: tweerp.user.screen_name, profile_image: tweerp.user.profile_image_url});
}).run();