试图剖析教程,但行为尴尬

时间:2014-07-28 17:12:16

标签: javascript angularjs

我对Angular.js非常陌生,我正在努力学习它,我需要根据我们已经提出的一些新要求调整我的一些项目以适应它。

我的一个主要问题是我需要在许多页面上重现某些行为,这是一个典型的base model -> inheritance问题。这是先前在Typescript中通过使用自然类并扩展它们来完成的。

在研究Angular时,我了解到它并没有真正开箱即用。所以我已经对许多不同的教程进行了抽样,并且遇到了 this one in particular ,这非常有见地,非常有趣。

我试图自己重现它,使用一些稍微不同的布局和语法来试图理解它。但我似乎无法让行为得到匹配...我所做的最大改变是我没有把所有东西“链接”在一起,而是在我第一次拾取角度时学习的路线,然后声明一个变量来添加像这样的所有控制器和工厂;

var app = angular.module("MyApp", []);

app.controller('...', function($scope, ... );
app.factory('...', function($http, ...);

我已经多次查看了我的代码,并检查了它,仔细检查了它,三重检查了它,但是当我运行它时,它不起作用。如果我在最终的 jsFiddle 中复制/粘贴确切的代码,它会正常工作,所以我100%肯定我在某个地方犯了错误。

我在这里发布自己的代码,希望有人可以告诉我我做错了什么 - 因为我现在真的很失落。我还包括一个分叉的 jsFiddle 我自己的代码,以便于运行和比较。

我希望我能列出我得到的确切错误,但我不确定。实际上有很多奇怪的输出回到angular.js文件,而不是我自己的文件,所以我不知道我自己的代码中的错误在哪里。

app.js

(function() {
    var app = angular.module('MyApp', []);

    app.factory('SimpleGithubUser', function($http) {
        var apiUrl = 'https://api.github.com/';

        // instantiate our initial object
        var SimpleGithubUser = function( username ) {
            this.username = username;
            this.profile = null;
        };

        // define the getProfile method which will fetch data
        // from GH API and *returns* a promise
        SimpleGithubUser.prototype.getProfile = function(){
            // generally, javascript callbacks, like here the $http.get callback,
            // change the value of the 'this' variable inside it
            // so we need to keep a reference to the current instance of 'this'
            var self = this;

            return $http.get(apiUrl + 'users/' + this.username).then(function(response){
                // when we get the results, we store the data in user.profile
                self.profile = response.data;

                // promises success should always return something in order to allow chaining
                return response;
            });
        };

        return SimpleGithubUser;

    });

    app.factory('AdvancedGithubUser', function($http, SimpleGithubUser){
        var apiUrl = 'https://api.github.com/';

        // create our new custom object that reuse the original object constructor
        var AdvancedGithubUser = function(){
            SimpleGithubUser.apply(this, arguments);
        };

        // reuse the original object prototype
        AdvancedGithubUser.prototype = new SimpleGithubUser();

        // define a new internal private method for this object
        function getUserEvents() {
            var self = this;

            return $http.get(apiUrl + 'users/' + this.username + '/events').then(function(response){
                // attach the events API result to our user profile
                self.profile.events = response.data;
                // promises should always return a result
                return response;
            });
        }

        // now let's override our original getProfile method
        AdvancedGithubUser.prototype.getProfile = function(){
            var self = this;

            // we first call the original getProfile method (aka super method)
            var originalGetProfile = SimpleGithubUser.prototype.getProfile.apply(this, arguments);

            // we use promises chaining to add additional data
            return originalGetProfile.then(function(){
                // beforer returning the results,
                // call our new private method and bind 'this' to 'self'
                // we need to do this because the method is not part of the prototype
                return getUserEvents(self);
            });
        };

        return AdvancedGithubUser;
    });

    app.service('MyUserProfile', function(AdvancedGithubUser){
        var user = new AdvancedGithubUser('revolunet');
        user.getProfile();
        return user;
    });

    app.controller('MyCtrl', function(MyUserProfile){
        $scope.user = MyUserProfile;
        alert(MyUserProfile.location);
    });

    app.controller('DemoCtrl', function($scope, SimpleGithubUser, AdvancedGithubUser){
        $scope.users = [];

        $scope.fetchUsers = function(){
            $scope.users = [];
            var users = ['mhevery', 'igorminar', 'btford', 'substack', 'sindresorhus', 'n1k0', 'revolunet'];

            users.forEach(function(userName){
                var user = new AdvancedGithubUser(userName);
                user.getProfile().then(function(){
                    $scope.users.push(user);
                });
            })
        }
    })
})();

HTML

<div ng-app="MyApp" ng-controller="DemoCtrl">
    <h1>{{ welcome }}</h1>

    <button ng-click="fetchUsers()">fetch some badass GitHub users data</button>
    <ul>
        <li ng-class="{odd: $odd}" ng-repeat="user in users">
            <img class="gravatar" ng-src="{{ user.profile.avatar_url }}" /> <b>{{ user.profile.login }}</b> ({{ user.profile.location }})
            <div class="event">{{ user.profile.events[0].type }} : {{ user.profile.events[0].repo.name }}</div>
        </li>
    </ul>
</div>

1 个答案:

答案 0 :(得分:1)

来自revolunet博客的Julien:)

好吧,在您的代码中,您只是在我的示例中使用return getUserEvents(self);时调用return getUserEvents.call(self);。区别在于this函数中的getUserEvents

在您的情况下,它将是window,它没有任何profile.events,这会导致您的错误,而在我的情况下,我强制它成为当前的AdvancedGithubUser实例,这要归功于call函数,用于更改函数调用中的this值。

javascript中的this行为可能非常令人困惑;它不取决于它的位置,而是取决于你使用它的上下文。

这是一篇关于这个主题的好文章:http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/