如何在使用 AngularJS/Web API/Angular-ui-router 的指令中调用 $http.put 服务后更新页面

Posted

技术标签:

【中文标题】如何在使用 AngularJS/Web API/Angular-ui-router 的指令中调用 $http.put 服务后更新页面【英文标题】:How To Update Page After Calling $http.put service in Directive Using AngularJS/Web API/Angular-ui-router 【发布时间】:2017-09-14 20:02:39 【问题描述】:

我们是 AngularJS 的新手,但正在开发一个 AngularJS/Web API 应用程序,该应用程序从 AngularJS Bootstrap popover/ 指令更新数据模型。

我们已经成功地从指令/弹出框更新了数据库,但是在不重新加载页面的情况下,我们无法弄清楚如何使用更新后的数据刷新页面上的数据。

主页 CShtml

<div ng-app="FFPA" ng-controller="myCtrl">
   <div svg-floorplan="dataset"></div>
</div>

弹出式 HTML:

<div>
   <div>
      ID:  person.Id <br />
      Name:  person.fullName <br />
      Current Cube/Office:  person.seatId 
      <br />
      Dept:  person.deptId 
      <br />
      Job Desc:  person.jobDesc
      <br />
      Phone: person.phone
      <br />
      <!--<input type="button" value="Click Me" ng-click="changeName()">-->
   </div>
   <div class="hiddenDiv" ng-hide="toggle">
      <div class="form-group">
         <label for="floor">Floor</label>
         <input id="floor" ng-model="person.floor" type="text" ng-trim="true"  class="form-control" />
      </div>
      <div class="form-group">
         <label for="section">Section</label>
         <input id="section" ng-model="person.section" ng-trim="true" type="text" class="form-control" />
      </div>
      <div class="form-group">
         <label for="offCubeNum">offCubeNum</label>
         <input id="offCubeNum" ng-model="person.offCubeNum" ng-trim="true" type="text" class="form-control" />
      </div>
      <div class="form-group">
         <label for="cbCube">Cubicle?</label>
         <input id="cbCube" ng-model="person.cbCube" type="checkbox" size="1" class="checkbox" />
      </div>
   </div>
   <div ng-hide="buttonToggle">
      <input type="button" value="Move" class="btn btn-success" ng-click="moveEmp()">
      <input type="button" value="Term" class="btn btn-danger" ng-click="changeName()">
   </div>
   <div ng-hide="submitToggle">
      <input type="button" value="Submit" class="btn btn-success" ng-click="submitMove()">
      <input type="button" value="Cancel" class="btn btn-warning" ng-click="cancel()">
   </div>
</div>

主页最初从角度控制器中的服务获取数据:

var app = angular.module('FFPA', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.router']);
    app.controller('myCtrl', function ($scope, dataService) 
    $scope.test = 'test';
        dataService.getData().then(function (data) 
            //The reduce() method reduces the array to a single value.
            $scope.dataset = data.reduce(function (obj, item) 
                obj[item.seatId.trim()] = item;
                item.fullName = item.fName + ' ' + item.lName;
                item.deptId = item.deptId;
                item.jobDesc = item.jobDesc;
                item.phone = item.phone;

                return obj;

            , );
        );
    );

获取数据服务:

    angular.module('FFPA').service('dataService', function ($http) 
    this.getData = function () 
        //web api call
        return $http.get("api/Controller/GetData).then(
          function (response) 
              return response.data;
          , function () 
              return  err: "could not get data" ;
          
        );
    
);

从 Popover 指令调用更新服务。

更新服务:

    angular.module('FFPA').service('updateService', function ($http) 
    this.putData = function (oc) 

        //web api call
        return $http.put("api/Controller/PutUpdateData", oc).then(
          function (response) 

              return response.data;
          , function () 
              return  err: "could not update data" ;
          
        );
    
);

这里是我们的 Popover 指令中的一个 sn-p,更新发生的位置以及我们认为可以刷新范围的位置,以及页面的数据:

updateService.putData(data).then(function (response) 
   if (response == false)
    alert("Move Failed!");
   else 
    alert("Move Succeeded.");
    //$window.location.reload() causes a page reload..not desirable
    //$window.location.reload();
     $state.reload();

);

我们尝试了 $state.reload();在 updateService.putData(data) 之后的 popover 指令中,但是这导致 -> 错误:无法转换到抽象状态 '[object Object]' 错误。

这是完整的 Popover 指令:

angular.module('FFPA').directive('svgFloorplanPopover', ['$compile', 'updateService', 'vacancyService', 'addService', 'terminateService', '$window', '$state', function ($compile, updateService, vacancyService, addService, terminateService, $window, $state) 
return 
    restrict: 'A',
    scope: 
        'person': '=svgFloorplanPopover',
         //UPDATE 8-MAY-2017
         onDataUpdate: '&'
    ,
    link: function (scope, element, attrs) 
        scope.moveToggle   = true;               //hide move toggle
        scope.addToggle    = true;                //hide add toggle
        scope.submitToggle = true;             //hide submit toggle


        scope.$watch("person", function () 
            if (scope.person) 
                if (scope.person.vacant == true) 
                    scope.addToggle         = false;  //show add button
                    scope.empInfoToggle     = true;   //hide emp info
                
                else
                    scope.moveToggle = false; //show move
            
        );

        //add employee---------------------------------------------------------
        scope.addEmp = function () 
            scope.addToggle = scope.addToggle === false ? true : false;

            scope.buttonToggle = true;
            scope.submitToggle = false;

            var data = 
                deptId: scope.person.deptId,
                divisionId: scope.person.divisionId,
                empId: scope.person.empId,
                floor: scope.person.floor,
                fName: scope.person.fName,
                lName: scope.person.lName,

                jobDesc: scope.person.jobDesc,
                officeCode: scope.person.officeCode,
                phone: scope.person.phone,
                section: scope.person.section,
                seat: scope.person.seat,
                seatId: scope.person.seatId,
                seatTypeId: scope.person.seatTypeId,
                vacant: scope.person.vacant
            ;


            //call to update/move the employee 
            //updateService.putData(scope.person).then(function () 
            addService.putData(data).then(function (response) 
                if (response == false)
                    alert("Create Failed!");
                else 
                    alert("Create Succeeded.");
                      //UPDATE 8-MAY-2017
                      $scope.onDataUpdate( person: $scope.person, moreData: $scope.moreData );
                    //$window.location.reload();
                    //$route.reload();
                    //scope.toggle = false;
                
            );
        


        //cancel function---------------------------------------------------------
        scope.cancel = function () 

        //Term emp---------------------------------------------------------
        scope.termEmp = function () 
            var data = 
                seatId: scope.person.seatId,
                floor: scope.person.floor
            ;
            terminateService.putData(data).then(function (response) 
                if (response == false)
                    alert("Term Failed!");
                else 
                    alert("Term Succeeded.");
                    $window.location.reload();
                    //$route.reload();
                    //scope.toggle = false;
                

            );
        


        //move employee---------------------------------------------------------
        scope.moveEmp = function () 
            scope.toggle = scope.toggle === false ? true : false;
            scope.buttonToggle = true;
            scope.submitToggle = false;
            if (scope.person && scope.person.fullName.indexOf('changed') === -1) 
                //scope.person.fullName += ' move?';
            

            //Json object to send to controller to check for vacancy
            var data = 
                floor: scope.person.floor,
                section: scope.person.section,
                seat: scope.person.offCubeNum
            ;

            //can't send object via $http.get (?) stringigy json and cast to Office object in controller.
            var json = JSON.stringify(data);

            //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            //CHECK VACANCY service call
            //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            vacancyService.getData(json)
                .then(function (response) 
                if (response == false)
                    alert("cube/office occupied");
                else

                    //+++++++++++++++++++++++++++++++++++++++++++
                    //UPDATE service call
                    //+++++++++++++++++++++++++++++++++++++++++++
                    //CONSTS
                    var CONSTFLOORPREFIX    = "f";
                    var CONSTSEAT           = "s";
                    var CONSTC              = "c"

                    var floor   = scope.person.floor;
                    var section = scope.person.section;

                    var offCube = scope.person.offCubeNum;
                    scope.person.oldSeatId = scope.person.seatId;

                    var newOfficeId = CONSTFLOORPREFIX + floor + CONSTSEAT;          //f3s 

                    //IF CUBE
                    if (scope.person.cbCube) 
                        var trimSection = section.trim();
                        newOfficeId += trimSection + CONSTC;                        //f3s313c
                        var trimOffCube = offCube.trim();
                        newOfficeId += trimOffCube;
                    
                    else  
                        newOfficeId += 0 + CONSTC + section;                                  //f3s0c
                    



                    scope.person.seatId = newOfficeId;

                    //Json object to send to controller to check for vacancy
                    var data = 
                        Id: scope.person.Id,
                        seatId: scope.person.seatId,
                        oldSeatId: scope.person.oldSeatId,
                        empId: scope.person.empId,
                        lName: scope.person.lName,
                        fName: scope.person.fName,
                        refacName: scope.person.refacName,
                        deptId: scope.person.deptId,
                        divisionId: scope.person.divisionId,
                        jobDesc: scope.person.jobDesc,
                        seatTypeId: scope.person.seatTypeId,
                        officeCode: scope.person.officeCode,
                        phone: scope.person.phone,
                        floor: scope.person.floor,
                        section: scope.person.section,
                        seat: scope.person.seat,
                        vacant: scope.person.vacant
                    ;


                    //call to update/move the employee 
                    //updateService.putData(scope.person).then(function () 
                    updateService.putData(data).then(function (response) 
                        if (response == false)
                            alert("Move Failed!");
                        else 
                            alert("Move Succeeded.");
                            //$window.location.reload();
                            $state.reload();
                            //$route.reload();
                            //scope.toggle = false;
                        

                    );
                //end else
            );
        

        if (element[0].querySelector('text') != null)
            scope.htmlPopover = './HTML/popoverTemplate.html';
            element[0].setAttribute('uib-popover-template', "htmlPopover");
            element[0].setAttribute('popover-append-to-body', 'true');
            element[0].setAttribute('popover-trigger', "'click'");
            //element[0].setAttribute('popover-trigger', "'mouseenter'");
            element[0].setAttribute('popover-placement', 'right');
            element[0].removeAttribute('svg-floorplan-popover');
            $compile(element)(scope);
        
    

]);

更新日期:2017 年 5 月 8 日 最初有一个额外的数据服务和一个指令,我们在这篇文章中省略了,因为它可能被认为不是必要的信息,但最近添加了,因为它可能是必需的。


SVG 加载指令:

angular.module('FFPA').directive('svgFloorplan', ['$compile', function ($compile) 
return 
    restrict: 'A',  //restrict attributes
    templateUrl: './SVG/HQ3RD-FLOOR3v10.svg',
    scope: 
        'dataset': '=svgFloorplan'
    ,
    link: 
        pre: function (scope, element, attrs) 
            //filter groups based on a cube/office id
            var groups = element[0].querySelectorAll("g[id^='f3']");
            //groups.style("pointer-events", "all");
            scope.changeName = function (groupId) 
                if (scope.dataset[groupId] && scope.dataset[groupId].lastName.indexOf('changed') === -1) 
                    scope.dataset[groupId].lastName += ' changed';
                
            

            groups.forEach(function (group) 
                var groupId = group.getAttribute('id');
                if (groupId) 
                    //set vacancy colors on vacant cubes
                    scope.$watch("dataset", function () 
                        if (scope.dataset) 
                            if (typeof scope.dataset[groupId] !== "undefined") 

                                //vacant cubes and offices hover
                                if (scope.dataset[groupId].vacant == true) 
                                    //seat type id 1 = cube
                                    if (scope.dataset[groupId].seatTypeId == 1)
                                        d3.select(group).select("rect").style("fill", "#99ff33").style("opacity", 0.4)
                                            .style("pointer-events", "all")
                                            .on('mouseover', function () 
                                             d3.select(this).style('opacity', 0.9);
                                         )
                                        .on('mouseout', function () 
                                            d3.select(this).style('opacity', 0.4);
                                        )
                                    
                                    //vacant office
                                    else 
                                        d3.select(group).select("path").style("stroke", "#ffffff").style("opacity", 1.0);
                                        d3.select(group).select("path").style("fill", "#99ff33").style("opacity", 0.4)
                                        .style("pointer-events", "all")
                                         .on('mouseover', function () 
                                             d3.select(this).style('opacity', 0.9);
                                         )
                                        .on('mouseout', function () 
                                            d3.select(this).style('opacity', 0.4);
                                        )
                                    
                                
                                else                               //Occupied 
                                    //seat type id 1 = cube
                                    if (scope.dataset[groupId].seatTypeId == 1) 
                                        d3.select(group).select("rect").style("fill", "#30445d").style("opacity", 0.0)
                                         .style("pointer-events", "all")
                                         .on('mouseover', function () 
                                             d3.select(this).style('opacity', 1.0);
                                             d3.select(group).select('text').style("fill", "#FFFFFF");
                                         )
                                        .on('mouseout', function () 
                                            d3.select(this).style('opacity', 0.0);
                                            d3.select(group).select('text').style("fill", "#000000");
                                        )

                                        //TODO: cubes have rects and on the north side of the building wall, paths.
                                        d3.select(group).select("path").style("fill", "#30445d").style("opacity", 0.0)
                                            .style("pointer-events", "all")
                                            .on('mouseover', function () 
                                                d3.select(this).style('opacity', 1.0);
                                                d3.select(group).select('text').style("fill", "#FFFFFF");
                                            )
                                        .on('mouseout', function () 
                                            d3.select(this).style('opacity', 0.0);
                                            d3.select(group).select('text').style("fill", "#000000");
                                        )
                                    
                                    //occupied office
                                    else 
                                        //d3.select(group).select("path").style("stroke", "#ffffff").style("opacity", 0.8);
                                        d3.select(group).select("path").style("fill", "#5A8CC9").style("opacity", 1.0)
                                         .style("pointer-events", "all")
                                         .on('mouseover', function () 
                                             //alert("office");
                                             d3.select(this).style("fill", "#2d4768").style('opacity', 1.0);
                                             d3.select(group).selectAll('text').style("fill", "#FFFFFF");
                                         )
                                        .on('mouseout', function () 
                                            d3.select(this).style("fill", "#5A8CC9").style('opacity', 1.0);
                                            d3.select(group).selectAll('text').style("fill", "#000000");
                                        )
                                    
                                //end occupied else
                            
                        
                    );
                     //UPDATE 8-MAY-2017->Implementation Question
                    scope.onDataUpdateInController = function (person, moreData)  ;
                    var datasetBinding = "dataset['" + groupId + "']";
                    group.setAttribute('svg-floorplan-popover', datasetBinding);

                    //UPDATE 8-MAY-2017
                    //on-data-update corresponds to onDataUpdate item on svgFloorplanPopover's scope.
                    group.setAttribute('on-data-update', onDataUpdateInController);


                    $compile(group)(scope);
                
            );
        


    

]);

空缺服务(更新前检查):

angular.module('FFPA').service('vacancyService', function ($http) 
...

主要问题是:

如何让我们的应用程序在不重新加载页面的情况下使用更新的数据刷新页面?

过去,我们曾经能够在 ASP.Net 网络表单的 UpdatePanels 中执行此操作。我认为它们是部分回发/AJAX 调用..

2017 年 8 月 2 日编辑

++++++++++++++++++++++++++++++++++++++++

即使赏金是自动授予的,我们仍然没有这个问题的答案。没有任何实现上下文,给出的答案是没有用的。

谁能扩展给出的答案,让我们了解如何解决这个问题?

谢谢

【问题讨论】:

您需要刷新视图还是仅将有界数据更新为返回的 ajax 响应? 我认为只需刷新更新的绑定数据即可。 你能添加一个工作小提琴或 plnkr 吗? 当然。我们有一个有效的 plunk,但我需要向它添加一个更新指令。 plnkr.co/edit/WRh1uESNUCl9swWEiSg5?p=info 好的,你试过我的回答了吗?您还可以尝试将父控制器上的用户初始化函数绑定到您的指令,并使用更新响应调用它。 【参考方案1】:

只需将您的数据添加到$scope 对象并在您的视图中使用它,只要您更新或修改数据即可 例如:考虑你有一个函数来获取你正在对你的数据库进行休息调用的数据

$scope.getdata=function()
$http.get(url).success(function(data)
   $scope.data=data;
);

每当您修改数据时,只需单击关闭指令/弹出窗口就调用此函数

【讨论】:

您能否举例说明如何根据我们当前的实现来实现此解决方案,例如 scope.moveEmp? 据我所知,$scope 无法注入到指令中。 这个没有关于当前应用程序实现的上下文的答案是没有用的。 据我了解,您所做的一切都很好,因为您从dataset 引用person,所以对person 所做的所有更改都将反映到$scope.dataset。但我认为在您的指令中,您已将属性作为svg-floorplan="dataset.seatId" 传递,其中seatIdscope.dataset 中的项目ID。否则你的更新将进入dataset 而不是dataset['seatid'] obj。这是您的实际数据。【参考方案2】:

要刷新您的视图(不绑定接收到的数据),请使用以下问题的答案:

使用 ngRoute 模块 How to reload or re-render the entire page using AngularJS

使用 ui-router 模块 Reloading current state - refresh data

我建议您将接收到的数据分配给您的有界 $scope 属性。

在您提供更新的 plnkr 后,我将添加一个完整示例:)

【讨论】:

终于回到这个问题上并安装了 Angular UI-Router。还实现了以下内容: var app = angular.module('FFPA', ['ngAnimate', 'ngSanitize', 'ui.bootstrap', 'ui.router']);还将 $state 注入到更新的指令中。更新后,添加了这一行: $state.reload();收到此错误:angular.js:14362 错误:无法转换到抽象状态“[object Object]”【参考方案3】:

请尝试以下步骤:

1。在svgFloorplanPopover指令中创建一个方法,并通过传入数据来调用它

在您的 svgFloorplanPopover 指令中,在范围内添加 onDataUpdate 项 声明:

...
scope: 
  'person': '=svgFloorplanPopover',
  onDataUpdate: '&'

...

如果您尝试重新加载状态,而不是重新加载状态或页面,请调用以下代码。这是为了创建一个事件系统,从指令中触发,让控制器或父指令知道数据已更改并且现在可以更新视图。

$scope.onDataUpdate(person: $scope.person, moreData: $scope.moreData);

2。在svgFloorplan中创建一个方法来接受传入的数据

由于您使用的是嵌套指令方法,因此您需要在svgFloorplan 指令中使用以下代码。

group.setAttribute('svg-floorplan-popover', datasetBinding);    
group.setAttribute('on-data-update', onDataUpdateInController);

on-data-update 对应于svgFloorplanPopover 范围内的onDataUpdate 项。

svgFloorplan 指令的范围内声明onDataUpdateInController 方法。

scope.onDataUpdateInController = function(person, moreData) ;    

您从指令中传递的对象属性的布局与参数的数量一致。

如果您需要将此数据进一步传递到声明 svgFloorplan 的控制器。对svgFloorplan 指令重复上述两个步骤。

我希望这种方法是明确的。这与Angular Directives,创建包装其他元素的指令部分以及添加关闭按钮的代码中解释的内容没有什么不同。这是code in plunkr的直接链接。

只是一个问题:你打算分开使用这些指令吗?如果不是,您可以尝试创建一个指令而不是两个。这将降低复杂性。

【讨论】:

1.更新了问题中的代码以反映建议的更新,还包括 SVGFloorPlan 指令代码,但是对以下实现有疑问:scope.onDataUpdateInController = function (person, moreData) ; 2.代码更新后收到错误:angular.js:14362 ReferenceError: onDataUpdateInController is not defined 3.我认为你提供的plunk是空的。感谢您的帮助! 终于回到了这个Ritesh。不确定我是否理解如何“在 svgFloorplan 指令的范围内声明 onDataUpdateInController 方法。” 尽管赏金是自动授予的,但我觉得任何答案都没有真正回答我最初的问题,因为我使用 Angular 的经验水平很低,而且这个应用程序的复杂性有点压倒性的,没有关于如何实施这些添加的明确说明,我很难让你的建议发挥作用。感谢您提供的任何帮助。

以上是关于如何在使用 AngularJS/Web API/Angular-ui-router 的指令中调用 $http.put 服务后更新页面的主要内容,如果未能解决你的问题,请参考以下文章

带有 Spring Security 的 AngularJS Web 应用程序

[Angularjs]asp.net mvc+angularjs+web api单页应用之CRUD操作

[Angularjs]asp.net mvc+angularjs+web api单页应用

是否可以使用 Ibeacon 搜索功能将 angularjs Web 应用程序转换为 IOS 应用程序?

25个超有用的 AngularJS Web 开发工具

AngularJS Web Api AntiForgeryToken CSRF