使用异步回调将数据从服务返回到控制器

时间:2017-01-04 03:01:41

标签: angularjs cordova ionic-framework cordova-plugins angular-promise

我试图从相机或图库上传图像。我写了服务中的所有代码。控制器必须从相机或图库中接收完整的URL,以便在幻灯片框中显示,但现在没有任何内容。

一切正常,但我无法将图像的网址发送回控制器。我认为错误是因为我试图在函数" obtenerImagen"之前返回值。执行。

我正在尝试实施回调,但我认为我没有正确实施。

我想用图片的网址返回的变量是 urlImagen

这是我调用服务的控制器

 //generar el popup para seleccionar el origen de la imagen: cámara o galería
    function seleccionarImagen() {
        cambiarImagenesService.seleccionarImagen()
            .then(reemplazarImagen);
    }

服务:

(function() {
'use strict';

angular
    .module('example.cambiarimagenes')
    .factory('cambiarImagenesService', cambiarImagenesService);

cambiarImagenesService.$inject = ['remoteDataService','$q', '$ionicPopup','$cordovaCamera', '$cordovaFile', '$cordovaFileTransfer', '$cordovaDevice', '$rootScope'];

/* @ngInject */
function cambiarImagenesService(remoteDataService,$q, $ionicPopup, $cordovaCamera, $cordovaFile,  $cordovaFileTransfer, $cordovaDevice, $rootScope){ 
    var dias = [];
    var mensaje = '';
    var image = null;
    var urlImagen = '';
    var service = {
        obtenerHorariosComplejo: obtenerHorariosComplejo,
        seleccionarImagen: seleccionarImagen
    };
    return service;

    //cargar una nueva imagen
    function seleccionarImagen() {
        var popup = seleccionarImagenPopup();
        return $ionicPopup.show(popup).then(function(result) {
            if (result == -1) {
                return false;
            }
            return urlImagen;
        });
    }

    function obtenerImagen(sourceType, callback){
        var options = {
            callback: callback,
            quality: 100,
            destinationType: Camera.DestinationType.FILE_URI,
            sourceType: sourceType,
            saveToPhotoAlbum: false
        };

        $cordovaCamera.getPicture(options).then(function(imagePath) {
            // Grab the file name of the photo in the temporary directory
            var currentName = imagePath.replace(/^.*[\\\/]/, '');

            //Create a new name for the photo
            var d = new Date(),
            n = d.getTime(),
            newFileName =  n + ".jpg";

            // If you are trying to load image from the gallery on Android we need special treatment!
            if ($cordovaDevice.getPlatform() == 'Android' && sourceType === Camera.PictureSourceType.PHOTOLIBRARY) {
                window.FilePath.resolveNativePath(imagePath, function(entry) {
                    window.resolveLocalFileSystemURL(entry, success, fail);
                    function fail(e) {
                        console.error('Error: ', e);
                    }
                    function success(fileEntry) {
                        var namePath = fileEntry.nativeURL.substr(0, fileEntry.nativeURL.lastIndexOf('/') + 1);
                        // Only copy because of access rights
                        $cordovaFile.copyFile(namePath, fileEntry.name, cordova.file.dataDirectory, newFileName).then(function(success){
                            image = cordova.file.dataDirectory + newFileName;
                            urlImagen = image;
                        }, function(error){
                            $scope.showAlert('Error', error.exception);
                        });
                    };
                }
                );
            } else {
                var namePath = imagePath.substr(0, imagePath.lastIndexOf('/') + 1);
                // Move the file to permanent storage
                $cordovaFile.moveFile(namePath, currentName, cordova.file.dataDirectory, newFileName).then(function(success){
                    image = cordova.file.dataDirectory + newFileName;
                    urlImagen = image;
                }, function(error){
                    $scope.showAlert('Error', error.exception);
                });
            }
        },
        function(err){
            console.log("error en el serivicio o cancelacion:"+err);
            // Not always an error, maybe cancel was pressed...
        })
    }

    //poopup para cargar nuevo imagen
    function seleccionarImagenPopup() {
        var scope = $rootScope.$new();
        scope.data = {
            tipo: null
        };
        return {
            templateUrl: 'scripts/complejo/agenda/nuevo-turno.html',
            title: "¿De dónde desea obtener la imagen?",
            scope: scope,
            buttons: [{
                text: 'Cancelar',
                onTap: function(e) {
                    scope.tipo = -1
                    return scope.tipo;
                }
            }, {
                text: '<b>Cámara</b>',
                type: 'button-positive',
                onTap: function(e) {
                    scope.tipo = Camera.PictureSourceType.CAMERA;
                    obtenerImagen(scope.tipo, function(val){
                        urlImagen = val;
                    });
                    console.log("el valor de la imagen al tocar la camara es:"+image);
                    return urlImagen;
                }
            }, {
                text: '<b>Galería</b>',
                type: 'button-positive',
                onTap: function(e) {
                    scope.tipo = Camera.PictureSourceType.PHOTOLIBRARY;
                    obtenerImagen(scope.tipo, function(val){
                        urlImagen = val;
                    });
                    console.log("el valor de la imagen al tocar la galeria es:"+image);
                    return urlImagen;
                }
            }]
        };
    }

    //generar error si hubo un problema
    function generarError(e){
        console.log("error!!!!!!:"+e);
        if (e.message) {
            return $q.reject(e.message);
        }
        return $q.reject('Ups! Hubo un problema al conectarse al servidor.');
    }

}
})();

感谢您的帮助!

// 修改 //

这是我的服务:

(function() {
'use strict';

angular
.module('example.cambiarimagenes')
.factory('cambiarImagenesService', cambiarImagenesService);

cambiarImagenesService.$inject = ['remoteDataService','$q', '$ionicPopup','$cordovaCamera', '$cordovaFile', '$cordovaFileTransfer', '$cordovaDevice', '$rootScope'];

/* @ngInject */
function cambiarImagenesService(remoteDataService,$q, $ionicPopup,$cordovaCamera, $cordovaFile,  $cordovaFileTransfer, $cordovaDevice, $rootScope){ 
var dias = [];
var mensaje = '';
var image = null;
var urlImagen = '';
var service = {
    obtenerHorariosComplejo: obtenerHorariosComplejo,
    seleccionarImagen: seleccionarImagen
};
return service;

//cargar una nueva imagen
function seleccionarImagen() {
    var popup = seleccionarImagenPopup();
    return $ionicPopup.show(popup).then(function(result) {
        if (result == -1) {
            return false;
        }
        return urlImagen;
    });
}

function obtenerImagen(sourceType){
    var options = {
        quality: 100,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: sourceType,
        saveToPhotoAlbum: false
    };

    return $cordovaCamera.getPicture(options).then(function(imagePath) {
        // Grab the file name of the photo in the temporary directory
        var currentName = imagePath.replace(/^.*[\\\/]/, '');

        //Create a new name for the photo
        var d = new Date(),
        n = d.getTime(),
        newFileName =  n + ".jpg";

        // If you are trying to load image from the gallery on Android we need special treatment!
        if ($cordovaDevice.getPlatform() == 'Android' && sourceType === Camera.PictureSourceType.PHOTOLIBRARY) {
            window.FilePath.resolveNativePath(imagePath, function(entry) {
                window.resolveLocalFileSystemURL(entry, success, fail);
                function fail(e) {
                    console.error('Error: ', e);
                }
                function success(fileEntry) {
                    var namePath = fileEntry.nativeURL.substr(0, fileEntry.nativeURL.lastIndexOf('/') + 1);
                    // Only copy because of access rights
                    $cordovaFile.copyFile(namePath, fileEntry.name, cordova.file.dataDirectory, newFileName).then(function(success){
                        image = cordova.file.dataDirectory + newFileName;
                        return image;

                    }, function(error){
                        $scope.showAlert('Error', error.exception);
                    });
                };
            }
            );
        } else {
            var namePath = imagePath.substr(0, imagePath.lastIndexOf('/') + 1);
            // Move the file to permanent storage
            $cordovaFile.moveFile(namePath, currentName, cordova.file.dataDirectory, newFileName).then(function(success){
                image = cordova.file.dataDirectory + newFileName;
                return image;
            }, function(error){
                $scope.showAlert('Error', error.exception);
            });
        }
    },
    function(err){
        console.log("error en el serivicio o cancelacion:"+err);
        // Not always an error, maybe cancel was pressed...
    })
}

//poopup para cargar nuevo imagen
function seleccionarImagenPopup() {
    var scope = $rootScope.$new();
    scope.data = {
        tipo: null
    };
    return {
        templateUrl: 'scripts/complejo/agenda/nuevo-turno.html',
        title: "¿De dónde desea obtener la imagen?",
        scope: scope,
        buttons: [{
            text: 'Cancelar',
            onTap: function(e) {
                scope.tipo = -1
                return scope.tipo;
            }
        }, {
            text: '<b>Cámara</b>',
            type: 'button-positive',
            onTap: function(e) {
                scope.tipo = Camera.PictureSourceType.CAMERA;
                var promise = obtenerImagen(scope.tipo)
                .then(function(val){
                    // asignamos el valor asincrónico
                    urlImagen = val;
                    // retornamos el valor a la cadena
                    return val;
                });
                // retornamos la promesa de manera síncrona
                return promise;
            }
        }, {
            text: '<b>Galería</b>',
            type: 'button-positive',
            onTap: function(e) {
                scope.tipo = Camera.PictureSourceType.PHOTOLIBRARY;
                var promise = obtenerImagen(scope.tipo)
                .then(function(val){
                    // asignamos el valor asincrónico
                    urlImagen = val;
                    // retornamos el valor a la cadena
                    return val;
                });
                // retornamos la promesa de manera síncrona
                return promise;
            }
        }]
    };
}

//generar error si hubo un problema
function generarError(e){
    console.log("error!!!!!!:"+e);
    if (e.message) {
        return $q.reject(e.message);
    }
    return $q.reject('Ups! Hubo un problema al conectarse al servidor.');
}

}
})();

3 个答案:

答案 0 :(得分:0)

你非常接近你想要的东西。您只需调用传递给callback函数的obtenerImagen

在你的成功功能中:

function success(fileEntry) {
   urlImagen = image;
   callback(urlImagen);
}

您的回调函数已将url作为参数。

答案 1 :(得分:0)

问题是回调函数是以异步方式执行的

//ERRONEOUS
onTap: function(e) {
    scope.tipo = Camera.PictureSourceType.CAMERA;
    obtenerImagen(scope.tipo, function(val){
        //ASYNCHRONOUSLY ASSIGNED
        urlImagen = val;
    });
    console.log("el valor de la imagen al tocar la camara es:"+image);
    //SYNCHRONOUSLY RETURNED
    return urlImagen;
}

在分配值之前返回值。后续代码在定义值之前执行。

obtenerImagen函数需要重构才能返回一个promise,并且需要返回promise。

//GOOD
onTap: function(e) {
    scope.tipo = Camera.PictureSourceType.CAMERA;
    var promise = obtenerImagenPromise(scope.tip)
      .then(function(val){
        //ASYNCHRONOUSLY ASSIGNED
        urlImagen = val;
        //return value to chain
        return val;
    });
    //SYNCHRONOUSLY RETURN PENDING PROMISE
    return promise;
}

通过返回一个promise,后续代码可以使用promise的.then方法来延迟执行,直到定义了值。

因为调用promise的.then方法会返回一个新的派生promise,所以很容易创建一个promise链。可以创建任意长度的链,并且由于承诺可以通过另一个承诺来解决(这将进一步推迟其解决方案),因此可以在任何时候暂停/推迟对承诺的解决连锁,链条。这使得实现强大的API成为可能。

- AngularJS $q Service API Reference - Chaining Promises

另见SO: Why are Callbacks from Promise .then Methods an Anti-Pattern

  

我需要做什么改变&#34; obtenerImagenPromise&#34;正确地返回承诺?因为现在我有以下错误&#34; TypeError:无法读取属性&#39;然后&#39;未定义&#34;我想我需要在函数中返回图像&#34; obtenerImagenPromise&#34;

首先从$cordovaCamera.getPicture返回派生的承诺:

//function obtenerImagen(sourceType, callback){
function obtenerImagenPromise(sourceType){
    var options = {
        //callback: callback,
        quality: 100,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: sourceType,
        saveToPhotoAlbum: false
    };

    //$cordovaCamera.getPicture(options).then(function(imagePath) {
    return $cordovaCamera.getPicture(options).then(function(imagePath) {
  //^^^^^^ ---- return derived promise
        // Grab the file name of the photo in the temporary directory

.then方法始终返回新的派生承诺。该承诺需要返回到父函数。还要确保.then方法中的函数返回值或承诺。未能返回某些内容将导致承诺解析为undefined

  

我设置了返回但总是控制器收到undefined

调试提示:放置console.log语句以查看中间值:

    //$cordovaCamera.getPicture(options).then(function(imagePath) {
    return $cordovaCamera.getPicture(options)
  //^^^^^^ ---- return derived promise

    .then(
        function(imagePath) {
            //ADD console.log to see intermediate data 
            console.log("getPicture success handler called");
            console.log("imagePath= "+imagePath);

            // Grab the file name of the photo in the temporary directory
            var currentName = imagePath.replace(/^.*[\\\/]/, '');

            //...

            //Always return something
            return "something";

        },
        function(err){
            console.log("error en el serivicio o cancelacion:"+err);
            // Not always an error, maybe cancel was pressed...

            //throw to chain error
            throw "error en el serivicio o cancelacion:"+err
        }
    );
};

还要确保.then方法中的函数返回值或承诺。未能返回某些内容将导致承诺解析为undefined

功能编程的经验法则是 - 总是返回一些东西。

答案 2 :(得分:-1)

用此更换服务中的seleccionarImagen功能;

&#13;
&#13;
function seleccionarImagen() {
  var deferred = $q.defer();
  var popup = seleccionarImagenPopup();
  $ionicPopup.show(popup).then(function(result){
    if(result == -1)
      deferred.reject(result);
    deferred.resolve(result);
  }, function(err){
    deferred.reject(err);
  });
  return deferred.promise;
}
&#13;
&#13;
&#13;