繁体   English   中英

Http服务工厂向Angular中的控制器返回未定义

[英]Http Service Factory Returns Undefined to Controller in Angular

我有这项服务,用于初始化品牌和ActualBrand变量。

.factory('brandsFactory', ['$http', 'ENV', function brandsFactory($http, ENV){

    var actualBrand = {};
    var brands = getBrands(); //Also set the new actualBrand by default

    function getBrands(){
        var URL;
        console.log('MOCKS enable? ' +  ENV.mocksEnable);
        if(ENV.mocksEnable){
           URL = ENV.apiMock + ENV.getBrandsMock;
        }else{
            URL = ENV.apiURL + ENV.getBrands;
        }

       $http.get(URL).
        success(function(data){
            console.log('data is :' +  data);
            actualBrand = data[0];
            console.log('Actual brand is ' + actualBrand);
            return data;
        }).
        error(function(){
        console.log('Error in BrandsController');
     });

    }

    var setActualBrand = function(activeBrand) {
        actualBrand = activeBrand;
    };

    var isSetAsActualBrand = function(checkBrand) {
        return actualBrand === checkBrand;
    };

    return {
        brands : brands,
        actualBrand : actualBrand
    }; 

我的控制器如下所示:

/* BrandController to manage the html content of a single brand */
    .controller('BrandCController', function(brandsFactory, $scope){

        $scope.brands = brandsFactory.brands;
        console.log('brands in controller is ' + $scope.brands + ' -- ' + brandsFactory.brands);

    });

控制器中的问题是,在brandFactory.brands中无法对其进行定义,因为它是在服务之前加载的。

我为什么可以解决这个问题? 我在angular和javascript中真的很新,也许我做错了很多事情。 我将不胜感激,谢谢。

之所以undefined ,实际上是由于$http请求的异步性质。 您无法采取任何措施(嗯,基本上什么也没有)来改变这种行为。 您将不得不使用最常见的方式来解决这个问题(如果继续使用Angular和大多数Web开发语言,这将成为一种长期的做法)。

什么是诺言? 在线上有很多关于它的指导原则。 向您解释它的最基本方法是,在异步操作返回之前,承诺将不会执行。

https://docs.angularjs.org/api/ng/service/$q
http://andyshora.com/promises-angularjs-explained-as-cartoon.html

现在,当您使用console.log属性时,它可能/可能尚未加载(实际上,它可能尚未加载),因此您undefined

此外,我对此不太确定,但是您使用的是工厂提供程序,但是您没有从提供程序函数中返回任何内容,因此您将其当作服务来使用。 我不确定Angular是否允许这样做,但是最好的实践方法是从工厂返回要创建的实例。

我在angular和javascript中真的很新,也许我做错了很多事情。 我将不胜感激,谢谢。

您是正确的-您做错了很多事情。 让我们从getBrands函数开始:

//Erroneously constructed function returns undefined
//
function getBrands(){
    var URL;
    console.log('MOCKS enable? ' +  ENV.mocksEnable);
    if(ENV.mocksEnable){
       URL = ENV.apiMock + ENV.getBrandsMock;
    }else{
        URL = ENV.apiURL + ENV.getBrands;
    }

   $http.get(URL).
    success(function(data){
        console.log('data is :' +  data);
        actualBrand = data[0];
        console.log('Actual brand is ' + actualBrand);
        //SUCCESS METHOD IGNORES RETURN STATEMENTS
        return data;
    }).
    error(function(){
    console.log('Error in BrandsController');
 });

return data语句嵌套在一个函数内,该函数是$http .success方法的参数。 不会将数据返回到getBrands功能。 由于getBrands主体中没有return语句,因此getBrands函数返回undefined

此外, $http服务的.success方法将忽略返回值。 因此, $http服务将返回解析为response对象而不是data对象的promise。 要返回解析为data对象的.then ,请使用.then方法。

//Correctly constructed function that returns a promise
//that resolves to a data object
//
function getBrands(){
    //return the promise
    return (
        $http.get(URL)
            //Use .then method
            .then (function onFulfilled(response){
                //data is property of response object
                var data = response.data;
                console.log('data is :' +  data);
                var actualBrand = data[0];
                console.log('Actual brand is ' + actualBrand);
                //return data to create data promise
                return data;
            })
     )
}

有了return里面声明onFulfilled功能和return的身体声明getBrands功能, getBrands函数将返回解决与履行承诺data (与拒绝或做出决议response的对象。)

在控制器中,解决与承诺.then.catch方法。

brandsFactory.getBrands().then(function onFulfilled(data){
    $scope.brand = data;
    $scope.actualBrand = data[0];
}).catch( function onRejected(errorResponse){
    console.log('Error in brands factory');
    console.log('status: ', errorResponse.status);
});

弃用通知1

$http旧版.success方法.success.error已过时。 请使用标准的.then方法。


更新

我用$q promise解决。 我在控制器中使用了.then ,但无法在工厂中使用。 如果要检查。 并感谢您的反馈。

//Classic `$q.defer` Anti-Pattern
//
var getBrands = function(){

    var defered = $q.defer();
    var promise = defered.promise;

    $http.get(URL)
    .success(function(data){
        defered.resolve(data);
    })
    .error(function(err){
        console.log('Error in BrandsController');
        defered.reject(err);
    });

   return promise;    
};//End getBrands

这是经典的$q.defer Anti-Pattern 不建议使用.sucesss.error方法的原因之一是,由于这些方法会忽略返回值 ,因此它们鼓励使用这种反模式。

这种反模式的主要问题是,它破坏了$http承诺链,并且当错误条件未得到正确处理时,承诺将挂起。 另一个问题是程序员经常无法在随后的函数调用中创建新的$q.defer

//Same function avoiding $q.defer   
//
var getBrands = function(){
    //return derived promise
    return (
        $http.get(URL)
          .then(function onFulfilled(response){
            var data = response.data;
            //return data for chaining
            return data;
        }).catch(function onRejected(errorResponse){
            console.log('Error in BrandsController');
            console.log('Status: ', errorResponse.status;
            //throw value to chain rejection
            throw errorResponse;
        })
    )
};//End getBrands

此示例显示了如何记录拒绝,并将errorResponse对象抛出链,以供其他拒绝处理程序使用。

有关更多信息,请参见$q Angular执行顺序

另外, 为什么不推荐使用角度$http成功/错误?

而且, 这是“延迟反模式”吗?

我认为您遇到的问题是您的控制器正在尝试访问由$ http.get调用设置的属性。 $ http.get返回一个promise,它是对您提供的获取品牌的URL的异步调用。 这意味着发出呼叫的控制器将不会等待品牌加载。

有一些解决此问题的选项,但是您可以简单地从服务中返回承诺并在控制器中解决它。

function getBrands(){
            var URL;
            console.log('MOCKS enable? ' +  ENV.mocksEnable);
            if(ENV.mocksEnable){
               URL = ENV.apiMock + ENV.getBrandsMock;
            }else{
                URL = ENV.apiURL + ENV.getBrands;
            }

           return $http.get(URL);
         });

然后在您的控制器中,您可以将成功和错误功能设置为...

brandsFactory.brands().then(function(response){
  $scope.brand = response.data;
  $scope.actualBrand = response.data[0];
}, function(err){
   console.log('Error in brands factory');
});

也有其他选项(例如,延迟加载或将工厂直接放置在您的范围内),但是您可以根据需要进行研究。

我用$ q promise解决。

.factory('brandsFactory', ['$http', '$q', 'ENV', function brandsFactory($http, $q, ENV){

        var actualBrand = {};

        var getBrands = function(){
            var URL;
            var defered = $q.defer();
            var promise = defered.promise;

            if(ENV.mocksEnable){
                URL = ENV.apiMock + ENV.getBrandsMock;
            }else{
                URL = ENV.apiURL + ENV.getBrands;
            }

            $http.get(URL)
            .success(function(data){
                actualBrand = data[0];
                defered.resolve(data);
            })
            .error(function(err){
                console.log('Error in BrandsController');
                defered.reject(err);
            });

            return promise;    
        };//End getBrands

        var setActualBrand = function(activeBrand) {
            actualBrand = activeBrand;
        };

        var isSetAsActualBrand = function(checkBrand) {
            return actualBrand === checkBrand;
        };

        return {
            setActualBrand : setActualBrand,
            getBrands : getBrands
        }; 

    }])//End Factory

在我的控制器中:

 /* BrandController to manage the html content of a single brand */
    .controller('BrandCController', function(brandsFactory, $scope){
        $scope.brands = [];
        $scope.actualBrand = {};
        $scope.setActualBrand = function(brand){
            brandsFactory.setActualBrand(brand);
            $scope.actualBrand = brand;
        };

        brandsFactory.getBrands()
        .then(function(data){
            $scope.brands = data;
            $scope.actualBrand = data[0];
        })
        .catch(function(errorResponse){
            console.log('Error in brands factory, status : ', errorResponse.status);
        });
    });//End controller

如果我可以改善答案,请告诉我。 我使用了从先前答案中获得的所有信息。 谢谢任何帮助。

更新

在我的工厂中删除$ q.defer 反模式

.factory('brandsFactory', ['$http', '$q', 'ENV', function brandsFactory($http, $q, ENV){

    var actualBrand = {};
    var getBrands = function(){
        var URL;

        if(ENV.mocksEnable){
            URL = ENV.apiMock + ENV.getBrandsMock;
        }else{
            URL = ENV.apiURL + ENV.getBrands;
        }

       return (
        $http.get(URL)
          .then(function onFulfilled(response){
            var data = response.data;
            //return data for chaining
            return data;
        }).catch(function onRejected(errorResponse){
            console.log('Error in BrandsController');
            console.log('Status: ', errorResponse.status);
            return errorResponse;
        })
        );

    };//End getBrands

    var setActualBrand = function(activeBrand) {
        actualBrand = activeBrand;
    };

    return {
        setActualBrand : setActualBrand,
        getBrands : getBrands
    }; 
}]);//End Factory

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM