I am trying to submit a form with ng-file-upload that contains additional form data. I had it working fine when the file was required, but now the client wants the file upload to be optional. I modified the PHP service to accept the new data, and it works fine.
The issue is displaying a success message when there is no file attached to the form. The data is received and processed fine by the service, but now I can't get it to indicate that the submission was successful.
I really don't want to have to shift gears at this point, and I like ng-file-upload's preview feature. I tried changing the checked variable in the ng-show directive for the success message and setting it in the .then function, but that doesn't work.
The only thing I can think of is to branch the code based on whether the file input is present and send it via regular $http if it isn't, but is there a better way?
Here's my controller code:
app.controller('adController', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) {
//$scope.text = "Is this working?";
$scope.ad = {};
$scope.isSaving = false; // disables fieldset to prevent accidental double-click submissions
$scope.successful_post = false;
$scope.errorClass = function(el) {
var s = $scope.adApplicationForm[el];
return (s.$invalid && s.$dirty) ? "has-error" : "";
};
$scope.submitForm = function(valid) {
console.log("Form Submitted" + valid)
return valid;
};
$scope.uploadPic = function(file) {
$scope.isSaving = true;
file.upload = Upload.upload({
url: 'php/upload_ad.php',
data: {ad: $scope.ad, file: file},
});
file.upload.then(function (response) { // success
$timeout(function () {
file.result = response.data;
});
$scope.isSaving = false;
$scope.successful_post = true;
}, function (response) {
if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
}, function (evt) {
// Math.min is to fix IE which reports 200% sometimes
file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
});
};
}]);
Thanks for your help.
Related
I spent several hours on this problem and I can not figure this out :( I know that I tested that functionality about a week or so ago and I saw 2 toastr error messages displayed (I didn't want two of them, though). I am not sure what exactly changed but now I can not get even a single one displayed :( I see no toastr message at all.
My code is:
app.directive('serverError', ['resourceFactory', 'spinnerService', 'toastr', '$log',
function (resourceFactory, spinnerService, toastr, $log) {
return {
restrict: 'A',
controller: ['$scope', '$timeout', function ($scope, $timeout) {
log = $log.getInstance("serverError");
var errorToastConfig = {
closeButton: true,
timeOut: 0,
extendedTimeOut: 0,
tapToDismiss: false,
preventDuplicates: true
};
var title = resourceFactory.getResource("Messages", "applicationError");
$scope.$on('sm:badRequest', function (event, data) {
if (!$scope.errorHandled && !$scope.showForm) {
log.debug("Handling bad request");
let errorMsg = "";
angular.forEach(data, function (value, key) {
if (value.message == '') value.message = 'The ' + value.property + ' value is invalid.'
errorMsg = errorMsg + value.message + " ";
});
errorMsg = errorMsg.trim();
$scope.errors = data;
toastr.clear();
if (errorMsg=="")
errorMsg = resourceFactory.getResource('Messages', 'errorOnForm');
title = resourceFactory.getResource("Messages", "badRequest");
toastr.error(errorMsg, title, errorToastConfig);
}
$scope.disableAction = false;
});
And then also in the controller:
function (error) {
spinnerService.stopSpinner('loaditemtree');
$scope.stopExecution = true;
if (error.hasOwnProperty("data")) {
toastr.clear();
let errorMsg = error.data[0].message;
toastr.error(errorMsg);
}
});
I see in the console [ItImagesSearchController] Init of the Images Search Controller is firing...
/SiriuswareControl/api/itDraftHeaders/1019 Failed to load resource: the server responded with a status of 400 (Bad Request)
2angular.js:14525 [serverError] Handling bad request
I also trace my code in the controller and I can see the line with toastr being executed. However, I see no toastr :( What can possibly be wrong?
I believe I figured this out, but I am sort of back to square 1. I wanted to prevent duplicate toastr messages from being displayed, thus I have preventDuplicates: true in the server error toastr config. However, it somehow made none of the messages to be displayed. When I slightly changed message in my controller, I saw both toastr messages now. How can I make only one of them to show?
I am working on a card processing API with ASP.NET , HTML , AngularJS and Stripe.NET. I am pretty new to all of them.
I followed the documentation on the Stripe website for sending the Stripe token to the server (here): https://stripe.com/docs/stripe.js#card-validateCardNumber
It worked! However, instead of JQuery I want to use AngularJS. I want to convert from JQuery to AngularJS this part of the JQuery code:
Stripe.card.createToken({
number: $('.card-number').val(),
cvc: $('.card-cvc').val(),
exp_month: $('.card-expiry-month').val(),
exp_year: $('.card-expiry-year').val(),
address_zip: $('.address_zip').val()
}, stripeResponseHandler);
function stripeResponseHandler(status, response) {
// Grab the form:
var $form = $('#payment-form');
if (response.error) { // Problem!
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false); // Re-enable submission
} else { // Token was created!
// Get the token ID:
var token = response.id;
// Insert the token into the form so it gets submitted to the server:
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// Submit the form:
$form.get(0).submit();
}
}
If someone can help, I will appreciate it a lot. Thanks. :)
I was able to answer my question (a while ago, just finding some time to answer it in here).
Firstly, here are the tips:
Use "angular-payments.js". You can find it here: https://github.com/laurihy/angular-payments
You have to use the html syntax for the card details as in the documentation of the repository.
It is not the same as in the Stripe documentation. I have used AngularJS service so that I can pass my token to my ASP.NET application.
Thirdly, I had problems with the verification token - here is a nice post for how to handle it: http://blog.novanet.no/anti-forgery-tokens-using-mvc-web-api-and-angularjs/
Here is (part of) my (AngularJS) code:
(function () {
var app = angular.module("myApp", ["angularPayments"]);
app.service('Service', function ($http) {
this.AddCard = function (stripeToken, stripeEmail) {
var tokenData = {
"stripeToken": stripeToken,
"stripeEmail": stripeEmail
};
$http.post("http://localhost:48484/payment/card", tokenData).success(function (response) {
window.location = '/cardprocess/confirmation';
})
};
});
app.directive('ncgRequestVerificationToken', ['$http', function ($http) {
return function (scope, element, attrs) {
$http.defaults.headers.common['RequestVerificationToken'] = attrs.ncgRequestVerificationToken || "no request verification token";
};
}]);
app.controller("myCtrl", ['$scope', myCtrl]);
app.controller("buyCtrl", function ($scope, CardService) {
$scope.submit = function () {
$scope.processing = true;
}
$scope.stripeFormSubmit = function (code, result) {
$scope.processing = false;
$scope.hideAlerts();
if (result.error) {
$scope.stripeError = result.error.message;
} else {
//$scope.stripeToken = result.id;
$scope.stripeToken = result.id;
CardService.AddCard($scope.stripeToken, $scope.stripeEmail);
}
};
$scope.hideAlerts = function () {
$scope.stripeError = null;
$scope.stripeToken = null;
};
});
}());
(The html page is quite big so I decided not to put in here. It should be straight forward - I have a form, which calls AngularJS model "stripeFormSubmit".)
Finally, you can see the "CardService", which is talking to my api - the service is initialised at the begining of the paste code.
That is the main idea. I decided not to go in a lot of detail. But I will try to answer questions (if any).
I am looking into allowing users to upload PDF files (and later preview/download them back). I was looking around and it seems that ng-File-Upload is a popular directive to upload files since it supports many browsers.
I originally thought that I would have to convert my PDF's to blobs and store them in my SQL database and then convert back to PDF when given them the option to preview/download.
Playing around with ng-file-upload I see that when one uploads a file in the manner (taken from their sample page):
$scope.uploadFiles = function (file, errFiles) {
console.log(file);
$scope.f = file;
$scope.errFile = errFiles && errFiles[0];
if (file) {
file.upload = Upload.upload({
url: 'https://angular-file-upload-cors-srv.appspot.com/upload',
data: { file: file }
});
file.upload.then(function (response) {
$timeout(function () {
file.result = response.data;
});
}, function (response) {
if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
}, function (evt) {
file.progress = Math.min(100, parseInt(100.0 *
evt.loaded / evt.total));
});
}
}
}]);
and me checking out the file properties in the console I see a property called $ngfBlobUrl:
$ngfBlobUrl:
"blob:http%3A//localhost%3A54170/0cc4b67a-9f6a-4833-acd0-7fd51e00996e
I am curious if anyone can give me any insight on how they generate this I assume in the file upload and if this is exactly what I need for my solution and if so can anyone point me to documentation on how I would go about converting it back to its original PDF form. I am trying to understand more how ng-file-upload works and whether it is a viable directive for my project or not.
Thanks
I have two views right now.
login
main
Right now I login and change my path to /main which works fine. When I am not logged in, and try to visit /main my web service returns "Access denied for user anonymous" which I then forward them to / which is my login view. How can I pass something so my LoginController knows they were forwarded from /main to alert them to login first?
LoginController.js
VforumJS.controller('LoginController', function($scope, $location, $routeParams, LoginModel)
{
$scope.email = "";
$scope.password = "";
$scope.fetching = false;
$scope.error = null;
$scope.login = function()
{
$scope.error = null;
$scope.fetching = true;
LoginModel.login($scope.email, $scope.password);
}
$scope.$on('LoginComplete', function(event, args)
{
log('login complete: ' + args.result);
$scope.fetching = false;
if (args.result == "success")
{
$location.path('/main');
}
else
{
$scope.error = args.result;
}
});
});
MainController.js
VforumJS.controller('MainController', function($scope, $location, $routeParams, MainModel)
{
$scope.currentTitle = '-1';
$scope.presentationData = MainModel.getPresentations();
$scope.$on('PresentationsLoaded', function(event, args)
{
log(args.result);
if (args.result != "Access denied for user anonymous")
{
//-- Parse preso data
$scope.presentationData = args.result;
}
else
{
//-- Need to login first, route them back to login screen
$location.path("/");
}
});
});
You can use $location.search() in your MainController to pass query string to the LoginController.
Inside you MainController:
if (args.result != "Access denied for user anonymous")
{
//-- Parse preso data
$scope.presentationData = args.result;
}
else
{
//-- Need to login first, route them back to login screen
$location.search({ redirectFrom: $location.path() });
$location.path("/");
}
And then in your LoginController, shortened for brevity:
VforumJS.controller('LoginController', function($scope, $location, $routeParams, LoginModel)
{
var queryString = $location.search();
$scope.$on('LoginComplete', function(event, args)
{
log('login complete: ' + args.result);
$scope.fetching = false;
if (args.result == "success")
{
if (queryString && queryString.redirectFrom) {
$location.path(queryString.redirectFrom);
} else {
$location.path('/somedefaultlocation');
}
}
else
{
$scope.error = args.result;
}
});
});
Alternatively you can use a shared service, maybe even your LoginModel to set a parameter from MainController to indicate the redirect came from it.
Update
Even better still, use $httpProvider.interceptors to register a response interceptor, and then use the same $location.search() technique described above to redirect to the login screen on authentication failure. This method is ideal as your controllers are then clean of authentication logic.
$location broadcasts $locationChangeStart and $locationChangeSuccess events, and the third param of each is oldUrl.
One solution would be to have a service that subscribes to $locationChangeStart in order to save the current and old urls.
When you hit /, your LoginController can check your service to see if the oldUrl is /main, and then act accordingly.
I have a registration form, and when the user clicks the submit button the value in every textbox will be sent to server to insert that data, and return true/false.
Client:
Template.cust_register.events({
'click button': function(){
var email = $('#tbxCustEmail').val();
var msg = $('#tbxCustMsg').val();
var isSuccess = insertMsg(email,msg);
if(isSuccess){
alert("Success");
}else alert("Try again");
}
});
Server:
function insertMsg(email,msg){
Messages.insert({Email:email,Message:msg});
return true;
}
This turned out to not work.
How to solve this?
Many people said "use publish/subscribe", but I don't understand how to use that.
First, watch the introductory screencast and read the Data and security section of the docs.
Your code in a publish/subscribe model would look like this:
Common:
Messages = new Meteor.Collection('messages');
Client:
Meteor.subscribe("messages");
Template.cust_register.events({
'click button': function(){
var email = $('#tbxCustEmail').val();
var msg = $('#tbxCustMsg').val();
Messages.insert({Email:email,Message:msg});
}
});
Server:
Meteor.publish("messages", function() {
return Messages.find();
});
An alternative solution is to use Meteor.call('yourMethodName') (on the client).
Then, on the server, you can have
Meteor.methods({
yourMethodName: function() { /* validate input + return some data */ }
});
You can consider setting a session variable to the return value.
Meteor.call('yourMethodName', function (err, data) {
if (!err) {
Session.set('myData', data);
}
});
And then in some some template...
Template.whatever.helpers({
messages: function() {
return Session.get('myData');
}
});
Why do all this?
1) You can explicitly deny all direct `insert/update/find` queries from the client, and force usage of pre-defined Meteor methods.
2) You can manually determine when certain data is "refreshed".
Obviously, this methodology undermines the value of the subscription/publication model, and it should only be used in cases where real-time data isn't required.