了解 Angular Promises 和 API 调用 -- 从 API 创建用户仪表板

Understanding Angular Promises and API Calls -- Creating User Dashboard from API

我正在尝试创建一个简单的仪表板来模拟显示的功能 here。我想坚持 angular 结构,但我已经成功地把自己搞糊涂了 oblivion.I 总的来说,我对 Angular 很陌生。

我正在根据 Github API 中的数据创建一个简单的仪表板。我想引用以下数据源(其中 user 是搜索栏中的值,repo 是从回购列表中单击的值-- 请参阅第 1 段中的链接示例):

"https://api.github.com/users/" + user                          ----> returns general user info
"https://api.github.com/users/" + user + "/repos"               ----> used for repo list
"https://api.github.com/repos/" + user + "/" + repo + "/events" ----> list of events per repo

基本上,该应用程序应该按以下方式工作:

  1. 用户在搜索栏中输入 Github 用户名。

  2. 对 return 用户信息和回购列表进行 API 调用 (我列出的前两个url)

到目前为止,我可以正常工作。

  1. 然后,根据 returned 下拉列表中第一个选择的回购或选择的值,将调用第三个 url 以获取 return 更多数据。

据我所知,我需要合并 Angular promises,因为我的第三个 Get 请求未被识别。

谁能帮我重组我的 app.js 代码以确保: - 我在页面呈现上设置了 "repo"(即第一个列出的回购将是默认选择) - 事件 api 在用户与回购列表

交互后再次调用

我试图按照 的解释进行操作,但我对如何合并用户名和选定的存储库感到有些困惑。如果有人可以指导我如何在我的代码中添加这些参数(由用户指定),我将不胜感激!

这是我目前的代码,供参考:

app.js

angular.module('myApp', ['ui.router'])
    .controller('DashboardCtrl', function($scope, $state, $http){
        // Set search model to 'mbostock' and the fetch function to contact the
        // remote API and ensure the view is initialized. Load results when the search box changes.
        $scope.$watch('search', function() {
            initialFetch();
        });
        $scope.search = "mbostock";


    // Make calls to the API for Users and Repo List
    function initialFetch(){
        $http.get("https://api.github.com/users/" + $scope.search)
            .then(function(response){ $scope.userinfo = response.data; });

        $http.get("https://api.github.com/users/" + $scope.search + "/repos")
            .then(
                function(response){ $scope.repolist = response.data;

                // Create call for events listing based on repo choice
                var repo = "";

                // console.log(document.getElementById("repo1").value);

                $(function() {
                    //For showing default url
                    MakeUrl();
                    // On repository selection, call events
                    $('#repo-select').on('change', function () {
                        if ($(this).val() == 0) {
                            repo = document.getElementById("repo1").value;
                        } else {
                            repo = $(this).val();
                        }
                        MakeUrl();
                        return false;
                    });

                });

                function MakeUrl() {
                    var finalUrl = "https://api.github.com/repos/" + $scope.search + "/" + repo + "/events";
                    console.log(finalUrl);
                    $http.get(finalUrl)
                        .then(function (response) { $scope.eventinfo = response.data; });
                }


                });
    }


    // Function select which ensures that the entire
    // text is selected when the user clicks in the text input.
    $scope.select = function(){
        this.setSelectionRange(0, this.value.length);
    }
})

index.html

<body>
<div class="container-fluid outerdiv" ng-app="myApp" ng-controller="DashboardCtrl">

    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand"><b>Github User Information</b> <span class="span-style"></span></a>
            </div>

            <div class="input-group search-bar">
                <input type="text" ng-model="search" ng-model-options="{ debounce: 800 }" onclick="select()" class="form-control" placeholder="Enter Github user login" autofocus />
                <span class="input-group-addon bar-style"><i class="glyphicon glyphicon-search"></i></span>
            </div>
        </div>
    </nav>

    <noscript>
        <div class="nojs">Javascript is either disabled or not supported in your browser. Please enable it or use a Javascript enabled browser.</div>
    </noscript>

    <div class="animated zoomInRight">


        <div id="user-bio" class="col-sm-4 col-md-4">
            <div>
                <div class="avatar">
                    <img src="{{ userinfo.avatar_url }}" class="thumbnail animated flip movie-poster">
                </div>

                <span class="span-outer">
                    <a href="{{userinfo.html_url}}" target="_blank">{{ userinfo.login }}</a>
                </span><br>{{ userinfo.name }}

                <p><strong>Joined:</strong><br> {{ userinfo.created_at }}</p>
                <p><strong>Last Updated:</strong><br> {{ userinfo.updated_at }}</p>

                <p>{{ userinfo.bio }}</p>

                <p class="outer-p">
                <div class="inner-p">
                    <span class="label label-primary">Public Repos :</span> {{ userinfo.public_repos }}
                </div>
                <div class="inner-p">
                    <span class="label label-primary">Followers :</span> {{ userinfo.followers }}
                </div>
                <div class="inner-p">
                    <span class="label label-primary">Following :</span> {{ userinfo.following }}
                </div>
                </p>

            </div>

            <div ng-if="userinfo.message==='Not Found'">
                No results found.
            </div>
        </div>

        <div class="col-sm-8 col-md-8">
            <h5><strong>Repositories:</strong></h5>
            <select id="repo-select">
                <option ng-repeat="repo in repolist" id="repo{{ $index + 1 }}" value="{{ repo.name }}" onchange="MakeUrl();">{{ repo.name }}</option>
            </select>

            <h5><strong>Events:</strong></h5>
            <ul class="event-results" id="event-select" style="height:400px; overflow-y:auto;">
                <li ng-repeat="event in eventinfo">
                    <a id="{{ $index + 1 }}" value="{{ event.type }}">{{ event.type }}
                    </a>, {{ event.created_at }} <!--ng-click="update(movie)"-->
                </li>
            </ul>
        </div>

    </div>
</div>

</body>

编辑 这是我看到的错误——再一次,它们似乎表明我需要实现承诺。再一次,我不确定为什么我不能指定默认选择的回购。

可能未处理拒绝:{"data":{"message":"Not Found","documentation_url":"https://developer.github.com/v3"},"status":404,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"https://api.github.com/repos/mbostock//events","headers":{ "Accept":"application/json, text/plain, /"}},"statusText":"Not Found"}

更新和编辑 通过@mikwat 的建议,我尝试使用 ng-model 绑定 repo 变量。

我的新 app.js 文件如下所示:

angular.module('myApp', ['ui.router'])
    .controller('DashboardCtrl', function($scope, $state, $http, DataService){
        // Set search model to 'mbostock' and the fetch function to contact the
        // remote API and ensure the view is initialized. Load results when the search box changes.
        $scope.$watch('search', function() {
            initialFetch();
                // .then(MakeUrl);
        });

        var user = $scope.search;
        $scope.search = "mbostock";
        $scope.repo = "array-source";


        // Make calls to the API for Users and Repo List
        function initialFetch(){
            $http.get("https://api.github.com/users/" + $scope.search)
                .then(function(response){ $scope.userinfo = response.data; });

            $http.get("https://api.github.com/users/" + $scope.search + "/repos")
                .then(
                    function(response){ $scope.repolist = response.data; },
                    $http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
                        .then(function (response) { $scope.eventinfo = response.data; })
                );
        }


        // Function select which ensures that the entire
        // text is selected when the user clicks in the text input.
        $scope.select = function(){
            this.setSelectionRange(0, this.value.length);
        }
    });

虽然这是获取要呈现的数据,但我不知道如何将第一个回购列表值动态分配为我的默认值(我试过 document.getElementById("repo1").value 但我得到 'undefined')和函数不会在下拉列表更改时再次调用 API。

2017 年 5 月 5 日更新 -- 个人解决方案 非常感谢@mikwat 提供的所有帮助。我最终使用的解决方案与他在下面所做的略有不同,但两者都有效。

angular.module('myApp', [])
.controller('DashboardCtrl', function($scope, $http){
    // Set search model to 'mbostock' and the fetch function to contact the
    // remote API and ensure the view is initialized. Load results when the search box changes.
    $scope.$watch('search', function() {
        initialFetch();
        // .then(MakeUrl);
    });

    // NOTE: watch for changes to repo
    $scope.$watch('repo', function() {
        $http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
            .then(function (response) {
                $scope.eventinfo = response.data;
            });

    });

    var user = $scope.search;
    $scope.search = "mbostock";

    // Make calls to the API for Users and Repo List
    function initialFetch(){
        $http.get("https://api.github.com/events")
            .then(function(response){ $scope.publicevents = response.data; console.log(response.data);})
            .catch(function (err) {
                        console.log(err)
                    });

        $http.get("https://api.github.com/users/" + $scope.search)
            .then(function(response){ $scope.userinfo = response.data; })
            .catch(function (err) {
                        console.log(err)
                    });

        $http.get("https://api.github.com/users/" + $scope.search + "/repos")
            .then(
                function(response){
                    $scope.repolist = response.data;

                    // NOTE: select first repo
                    if ($scope.repolist && $scope.repolist.length > 0) {
                        var repo = $scope.repolist[0].name;
                    } else {
                        console.log("Something went wrong here!");
                        var repo = "undefined"
                    }
                    $scope.repo = repo;
                    return repo
                }).then(function (repo) { 
                        $http.get("https://api.github.com/repos/" + $scope.search + "/" + repo + "/events")
                        .then(function (response) { $scope.eventinfo = response.data; console.log(response.data);})
                        return repo; 
                    }).then(function (repo) {
                        $http.get("https://api.github.com/repos/" + $scope.search + "/" + repo + "/languages")
                        .then(function (response) { $scope.languages = response.data; console.log(response.data);})
                    }).catch(function (err) {
                        console.log("Here!" + err);
                    });
    };


    // Function select which ensures that the entire
    // text is selected when the user clicks in the text input.
    $scope.select = function(){
        this.setSelectionRange(0, this.value.length);
    }


});

这是一个可行的解决方案。我删除了一些依赖项只是为了让它在这个沙箱中工作。我使用 NOTE: 评论来帮助描述重要的变化。

angular.module('myApp', [])
    .controller('DashboardCtrl', function($scope, $http){
        // Set search model to 'mbostock' and the fetch function to contact the
        // remote API and ensure the view is initialized. Load results when the search box changes.
        $scope.$watch('search', function() {
            initialFetch();
                // .then(MakeUrl);
        });
        
        // NOTE: watch for changes to repo
        $scope.$watch('repo', function() {
            $http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
                 .then(function (response) {
                     $scope.eventinfo = response.data;
                  });
                  
            // NOTE: additional request to fetch languages
            $http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/languages")
                 .then(function (response) {
                     console.log(response.data);
                     // TODO: display results
                  });

        });

        var user = $scope.search;
        $scope.search = "mbostock";

        // Make calls to the API for Users and Repo List
        function initialFetch(){
            $http.get("https://api.github.com/users/" + $scope.search)
                .then(function(response){ $scope.userinfo = response.data; });

            $http.get("https://api.github.com/users/" + $scope.search + "/repos")
                .then(
                    function(response){
                        $scope.repolist = response.data;
                        
                        // NOTE: select first repo
                        if ($scope.repolist && $scope.repolist.length > 0) {
                            $scope.repo = $scope.repolist[0].name;
                        }
                    },
                    $http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
                        .then(function (response) { $scope.eventinfo = response.data; })
                );
        }


        // Function select which ensures that the entire
        // text is selected when the user clicks in the text input.
        $scope.select = function(){
            this.setSelectionRange(0, this.value.length);
        }
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div class="container-fluid outerdiv" ng-app="myApp" ng-controller="DashboardCtrl">

    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand"><b>Github User Information</b> <span class="span-style"></span></a>
            </div>

            <div class="input-group search-bar">
                <input type="text" ng-model="search" ng-model-options="{ debounce: 800 }" onclick="select()" class="form-control" placeholder="Enter Github user login" autofocus />
                <span class="input-group-addon bar-style"><i class="glyphicon glyphicon-search"></i></span>
            </div>
        </div>
    </nav>

    <noscript>
        <div class="nojs">Javascript is either disabled or not supported in your browser. Please enable it or use a Javascript enabled browser.</div>
    </noscript>

    <div class="animated zoomInRight">


        <div id="user-bio" class="col-sm-4 col-md-4">
            <div>
                <div class="avatar">
                    <img src="{{ userinfo.avatar_url }}" class="thumbnail animated flip movie-poster">
                </div>

                <span class="span-outer">
                    <a href="{{userinfo.html_url}}" target="_blank">{{ userinfo.login }}</a>
                </span><br>{{ userinfo.name }}

                <p><strong>Joined:</strong><br> {{ userinfo.created_at }}</p>
                <p><strong>Last Updated:</strong><br> {{ userinfo.updated_at }}</p>

                <p>{{ userinfo.bio }}</p>

                <p class="outer-p">
                <div class="inner-p">
                    <span class="label label-primary">Public Repos :</span> {{ userinfo.public_repos }}
                </div>
                <div class="inner-p">
                    <span class="label label-primary">Followers :</span> {{ userinfo.followers }}
                </div>
                <div class="inner-p">
                    <span class="label label-primary">Following :</span> {{ userinfo.following }}
                </div>
                </p>

            </div>

            <div ng-if="userinfo.message==='Not Found'">
                No results found.
            </div>
        </div>

        <div class="col-sm-8 col-md-8">
            <h5><strong>Repositories:</strong></h5>
            
            <!-- NOTE: use ng-model and ng-repeat and don't clobber repo variable on scope -->
            <select id="repo-select" ng-model="repo">
                <option ng-repeat="r in repolist" id="repo{{ $index + 1 }}" ng-value="r.name" onchange="MakeUrl();">{{ r.name }}</option>
            </select>

            <h5><strong>Events:</strong></h5>
            <ul class="event-results" id="event-select" style="height:400px; overflow-y:auto;">
                <li ng-repeat="event in eventinfo">
                    <a id="{{ $index + 1 }}" value="{{ event.type }}">{{ event.type }}
                    </a>, {{ event.created_at }} <!--ng-click="update(movie)"-->
                </li>
            </ul>
        </div>

    </div>
</div>