使用异步回调将数据从服务返回到控制器 [英] Returning Data from Service to Controller with Asynchronous Callback
问题描述
我正在尝试上传来自相机或图库的图片.我编写了服务中的所有代码.控制器必须从相机或图库接收完整的 url 才能将其显示在幻灯片框中,但现在什么也没有收到.
I'm trying to upload image from Camera or gallery. I wrote all the code in the service. The controller must receive the complete url from the camera or gallery to show it in a slide box, but now nothing receive.
一切正常,但我无法将图像的网址发送回控制器.我认为错误是因为我试图在函数obtenerImagen"执行之前返回值.
Everything works, but I couldn't send back to the controller the url of the image. I think the error is because I'm trying to return the value before that the function "obtenerImagen" execute.
我正在尝试实施回调,但我认为我没有正确实施.
I'm trying to implement Callbacks, but I think I did not implemented correctly.
我想和图片的url一起返回的变量是urlImagen
The variable that I want to return with the url of the image is 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.');
}
}
})();
推荐答案
问题是回调函数异步执行.
//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;
}
在赋值之前返回值.后续代码在定义值之前执行.
The value is returned before the value is assigned. Subsequent code executes before the value is defined.
obtenerImagen
函数需要重构以返回承诺,并且需要返回承诺.
The obtenerImagen
function needs to be refactored to return a promise and the promise needs to be returned.
//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
方法来延迟执行,直到定义了值.
By returning a promise, subsequent code can use the .then
method of the promise to delay execution until the value is defined.
因为调用 Promise 的 .then
方法会返回一个新的派生 Promise,所以很容易创建一个 Promise 链.可以创建任意长度的链,并且由于一个 promise 可以用另一个 promise 来解决(这将进一步推迟它的分辨率),因此 可以在任何时候暂停/推迟对 promise 的解决连锁,链条.这使得实现强大的 API 成为可能.
Because calling the .then
method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
-- AngularJS $q 服务 API 参考 - 链接承诺
另见SO:为什么来自Promise的回调.then
方法是反模式
Also see SO: Why are Callbacks from Promise .then
Methods an Anti-Pattern
我需要在obtenerImagenPromise"中做哪些更改才能正确返回承诺?因为现在我有以下错误TypeError:无法读取未定义的属性'then'"我想我需要在函数obtenerImagenPromise"中返回图像
what changes shall I need to do in "obtenerImagenPromise" to returns the promise correctly? Because now I have the following error "TypeError: Cannot read property 'then' of undefined" I think I need to return image in the function "obtenerImagenPromise"
首先从 $cordovaCamera.getPicture
返回派生的 promise:
Start with returning the derived promise from $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
.
The .then
method always returns a new derived promise. That promise needs to be returned to the parent function. Also make sure that the functions inside the .then
method return a value or promise. Failure to return something will result in the promise resolving as undefined
.
我设置了返回值,但控制器总是收到 undefined
I set the returns but always the controller receive
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
.
Also make sure that the functions inside the .then
method return a value or promise. Failure to return something will result in the promise resolving as undefined
.
函数式编程的经验法则是——总是返回一些东西.
The rule of thumb with functional programming is -- always return something.
这篇关于使用异步回调将数据从服务返回到控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!