Angular: cannot read property 'then' of undefined

480 Views Asked by At

I have a service upload imageto amazon s3 after i sign it with my own backend using cordova file-transfer plugin. I call this service after taking a picture using cordova camera plugin to upload the taken picture to the s3 bucket. The app sign correctly with my own backend but when it trigger the function upload i get the error i defined in the title. This is the service that it call an end point in my backend to sign the file and then upload the image to amazon s3:

//Image upload Service
.factory('S3Uploader', function($q, $window, $http, $ionicPopup, API_URL) {

    var signingURI = API_URL + "s3signing";

    function upload(imageURI, fileName) {
        document.addEventListener('deviceready', function() {
            console.log('Uploading ' + fileName + ' to S3');

            var deferred = $q.defer(),
                ft = new FileTransfer(),
                options = new FileUploadOptions();

            options.fileKey = "file";
            options.fileName = fileName;
            options.mimeType = "image/jpeg";
            options.chunkedMode = false;

            console.log('Requesting signed doc ' + signingURI);
            $http.post(signingURI, {
                "fileName": fileName
            })
                .success(function(data) {
                    console.log('Got signed doc: ' + JSON.stringify(data));
                    options.params = {
                        "auth": true,
                        "key": fileName,
                        "AWSAccessKeyId": data.awsKey,
                        "acl": "public-read",
                        "policy": data.policy,
                        "signature": data.signature,
                        "Content-Type": "image/jpeg"
                    };

                    ft.upload(imageURI, "https://" + data.bucket + ".s3.amazonaws.com/",
                        function(e) {
                            console.log("Upload succeeded");
                            console.log(JSON.stringify(e));
                            deferred.resolve(e);
                            $ionicPopup.alert({
                                title: 'great',
                                content: 'The image upload to amazon success'
                            });
                        },
                        function(e) {
                            deferred.reject(e);
                            $ionicPopup.alert({
                                title: 'Oops',
                                content: 'The image upload failed to amazon'
                            });
                        }, options);

                })
                .error(function(data, status, headers, config) {
                    console.log(JSON.stringify(data));
                    console.log(status);
                    $ionicPopup.alert({
                        title: 'Oops',
                        content: 'The image upload failed to sign with node'
                    });
                });

            return deferred.promise;

        }, false); //device ready

    }

    return {
        upload: upload
    }

})

and here is the controller code where am calling the camera plugin and in the success of taking the picture am calling the upload function from the S3Uploader service:

.controller('newItemCtrl', function($scope, $http, $ionicPopup, $timeout, $cordovaCamera, API_URL, me, S3Uploader) {
$scope.selectPicture = function() {
        document.addEventListener('deviceready', function() {

            var options = {
                destinationType: Camera.DestinationType.FILE_URI,
                sourceType: Camera.PictureSourceType.CAMERA,
                allowEdit: true,
                encodingType: Camera.EncodingType.JPEG,
                targetWidth: 300,
                targetHeight: 300,
            };
            $cordovaCamera.getPicture(options).then(function(imageURI) {
                $scope.imageSrc = imageURI;
                // upload to Amazon s3 bucket
                var fileName = new Date().getTime() + ".jpg";
                S3Uploader.upload(imageURI, fileName).then(function() {
                    alert("upload to S3 successed");
                });
            }, function(err) {
                alert(err);
            });

        }, false); // device ready
    }; // Select picture
}) 

i get the erorr in this line of the controller:

S3Uploader.upload(imageURI, fileName).then(function() {

it's also important to mention am using crosswalk with my ionic app.

2

There are 2 best solutions below

3
Beyers On BEST ANSWER

Your current implementation of S3Uploader.upload does not return a promise, it returns nothing. Move your declaration and return of the promise to directly inside the S3Uploader.upload function and not nested inside the document.addEventListener code.

Change your code to something like:

.factory('S3Uploader', function($q, $window, $http, $ionicPopup, API_URL) {

    var signingURI = API_URL + "s3signing";

    function upload(imageURI, fileName) {
        var deferred = $q.defer()
        document.addEventListener('deviceready', function() {
            // Removed for brevity
        }, false);
        return deferred.promise;
    }

    return {
        upload: upload
    }

})
0
David Barker On

You are creating and returning your deferred object and it's promise from an event listener. Not the upload factory method.

Something along these lines is what you need:

.factory('S3Uploader', function($q) {

    function upload() {
        var deferred = $q.defer();

        // listener logic

        return deferred.promise;
    }

    return {
        upload : upload
    }
});

You will have problems with this, as you will want a new deferred object for each time the listener is fired. Adding a listener to a factory method to perform something seems like a bad pattern to me. The event should wrap the invocation of the factory method.