2015年7月23日 星期四

ASP.NET MVC+ Angular分頁

      目前剛好配合公司的需求,開發簡易的平台簡化繁雜的作業流程,正好練練我的MVCAngular 的分頁效果,當然還是有遇到不少困難啦~ 可以當作借鏡也給各位參考啦!











一、分頁的頁數
本案例用到async Task<T>的宣告型別,能非同步的傳輸模式,可以加快瀏覽的速度,不過注意的是宣告async Task,呼叫方式要用await
   
Model: entityTestData.cs
public class entityTestData
    {
double paging = 5; //設定分頁=5
public async Task<int> totalCount()
        {
            return i_cpNewDb.test_notice.Count();  
//作者省略try{}catch{} 比較方便看code,各位還是要加上
        }
        public async Task<int> pageCount(int totalCount)
        {
                double pageCount = Math.Ceiling(totalCount / paging);
                return Convert.ToInt32(pageCount);
         }
//列表
     public IEnumerable<viewAnnounce> search(int currentPage, string productType, string fileName, string fileVersion)
        {          
                  int intPagging = 0;
                  intPagging = Convert.ToInt32(paging);
                    var query = from q in i_cpNewDb.test_notice
                                orderby q.ikey descending
                                select new viewAnnounce()
                                {
                                    ikey = q.ikey,
                                    productType = q.產品類別,
                                    fileName = q.檔案名稱,
                                    fileVersion = q.版本,
                                    context = q.主旨,
                                    updateTime = q.修改日期,
                                    testFilePath = q.測試檔案路徑,
                                    downloadFile = q.測試文件路徑,
                                    isRelease = q.釋出正式版
                                };
                    if (!string.IsNullOrEmpty(productType))
                    {
                        query = query.Where(q => q.productType.Contains(productType.Trim()));
                    }
                    if (!string.IsNullOrEmpty(fileName))
                    {
                        query = query.Where(q => q.fileName.Contains(fileName.Trim()));
                    }
                    if (!string.IsNullOrEmpty(fileVersion))
                    {
                        query = query.Where(q => q.fileVersion.Contains(fileVersion.Trim()));
                    }
//分頁時需要注意第 ? 筆開始取 n 筆,本案例設為分頁5
                    IEnumerable<viewAnnounce> liResult = query.AsEnumerable<viewAnnounce>().Skip((currentPage - 1) * intPagging).Take(intPagging).AsQueryable();
                    return liResult;                     
        }

      //因為有用到跨資料庫的案例,供給大家參考範例,和分頁的本身無相關喔!
public IEnumerable<viewBug> listBugs(int announceIkey)
        {          
                string eSQL = @"select a.ikey as ikey, a.announcement_ikey as announcementIkey,a.uid as uid,b.產品類別 as productType, a.測試檔案 as bugDocumentFile , a.回報結果 as bugContext,b.檔案名稱 as fileName,b.版本 as fileVersion,c.name as userName from bug_notice  a
 left join test_notice b on a.announcement_ikey = b.ikey
left join TCS.dbo.alluser c on (a.uid = c.uid) where a.announcement_ikey=@Announcement";                   
                    SqlParameter sp = new SqlParameter("Announcement", announceIkey);
                    sp.SqlDbType = System.Data.SqlDbType.Int;
                    var results = i_cpNewDb.Database.SqlQuery<viewBug>(eSQL, sp ).AsEnumerable();
                    IList<viewBug> liBugs = new List<viewBug>();                   
                    foreach (var data in results)
                    {
                        liBugs.Add(data);
                    }
                    return liBugs.AsEnumerable<viewBug>();
        }
}

Controller:
public class TestController : Controller
{
//將總頁數傳回頁面
  public async Task<ActionResult> Announce()
        {
            int totalCount = await entityTestObj.totalCount();          
            ViewBag.totalCount = await entityTestObj.pageCount(totalCount);
            return View(ViewBag);
        }
        [HttpPost]
        public ActionResult SearchAnnounce(int currentPage, string searchProductType, string searchFileName, string searchFileVersion)
        {
            var result = entityTestObj.search(currentPage, searchProductType, searchFileName, searchFileVersion);
          return Json(result);   //json格式傳送資料給angularjs時,接收參數有datetime格式,需另外處理喔~
        }
}

API Controller
    在函式上方加上 [HttpGet] [Route("api/…/}")] 就可以用GET協定分別呼叫URL,舉例 $ http.Get('../api/ApiTest/listBugNote/' +$scope.hiddenAnnounceIkey)

    public class ApiTestController : ApiController
    {
        entityTestData entityTestObj = new entityTestData(); //呼叫實體層
        public IEnumerable<viewAnnounce> Get()
        {
            return entityTestObj.listNewNotice();
        }
        [HttpGet]
        [Route("api/ApiTest/readTestNote/{announceIkey}")]
        public viewAnnounce Get(int announceIkey)
        {
            return entityTestObj.read(ikey);
        }
        [HttpGet]
        [Route("api/ApiTest/listBugNote/{announceIkey}")]
        public IEnumerable<viewBug> listBugNote(int announceIkey)
        {
            return entityTestObj.listBugs(announceIkey);
        }
}

View:
    如果Form表單中含有傳送檔案時,請記得在FORM的屬性加上enctype="multipart/form-data"你的檔案才能送到後端。

<form ng-controller="announceController" enctype="multipart/form-data">   
    <div class="container">
        <div class="row">         
            <div class="col-md-12">
                <div class="table-responsive">                  
                    <table style="border:0px;border-spacing: 10px;">
                        <tr>
                          <td style="padding: 6px;"><p data-placement="top"><button class="btn btn-primary btn-xs" data-title="search" data-toggle="modal" data-target="#search"><span class="glyphicon glyphicon-search"></span></button></p></td>
                        </tr>
                    </table>
                    <table id="mytable" class="table table-bordred table-striped">
                        <thead>
                        <th>編號</th>
                        <th>類別</th>
                        <th>名稱</th>
                        <th>版本</th>
                        <th>更新日期</th>
                        <th>主旨</th>
                        <th>文件檔案</th>
                        <th>測試檔案路徑</th>
                        <th>釋出</th>                      
                        <th>Line</th>
                        </thead>
                        <tbody>
                            <tr ng-repeat="testData in testNodes">
                                <td>{{testData.ikey}}</td>
                                <td>{{testData.productType}}</td>
                                <td>{{testData.fileName}}</td>
                                <td>{{testData.fileVersion}}</td>
                                <td>
                           //JSON格式的傳回值中,如果有DATETIME,需要用Angularfilter特別處理字串
{{testData.updateTime | mydate | date:"yyyy/MM/dd ' ' h:mma"}}
</td>
                                <td>{{testData.context | limitTo: 15}} {{testData.context.length < 15 ? '' : '...'}}</td>
                                <td><a href="~/documentFile/{{testData.downloadFile}}">{{testData.downloadFile}}</a></td>
                                <td>{{testData.testFilePath}}</td>
                                <td>{{testData.isRelease}}</td>
                                <td>                                   
                                    <a href="#" ng-click="line(testData.fileName, testData.fileVersion)"><img src="~/Images/linebutton_20x20_en.png" width="20" height="20" /></a>
                                </td>
                            </tr>
                        </tbody>
                    </table>                  
                    <ul class="pagination pull-right">
                        <li><a href="#" ng-click="prePage(currentPage)"><span class="glyphicon glyphicon-chevron-left"></span></a></li>
                        <li ng-repeat="page in pages" ng-click="setPage(page)"  ng-class="{'active': page == currentPage}"><a href="#">{{page}}</a></li>                       
                        <li><a href="#" ng-click="nextPage(currentPage)"><span class="glyphicon glyphicon-chevron-right"></span></a></li>                      
                    </ul>
                </div>
            </div>
        </div>
    </div>   
//查詢畫面
    <div class="modal fade" id="search" tabindex="-1" role="dialog" aria-labelledby="search" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" id="closeAddForm" class="close" data-dismiss="modal" aria-hidden="true"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
                    <h4 class="modal-title custom_align" id="Heading">查詢</h4>
                </div>
                <div class="modal-body">
                    <div class="form-group">
                        <label>類別</label>
                        <select class="form-control" ng-model="searchProductType"
                                ng-options="productType.key for productType in productsClassification ">
                            <option value=""></option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label>檔名</label>
                        <input ng-model="searchFileName" class="form-control " type="text" placeholder="請輸入檔名">
                    </div>
                    <div class="form-group">
                        <label>版本</label>
                        <input ng-model="searchFileVersion" class="form-control " type="text" placeholder="1.0.0.0">
                    </div>                   
                </div>
                <div class="modal-footer ">
                    <button type="button" class="btn btn-warning btn-lg" ng-click="searchSubmit()" style="width: 100%;"><span class="glyphicon glyphicon-ok-sign"></span>查詢</button>
                </div>
            </div>
        </div>
    </div>
</form>

Mydate :  json的時間格式傳回值會變成DATE("數字"),所以要取字串第六碼以後的值,再將其轉換成數字型態,才能透過Angular的內建filter格式直接轉換日期
   app.filter("mydate", function() {
        return function (x) {
            return new Date(parseInt(x.substr(6)));
        };
    });

pageFactory:用來處理畫面上的上一頁、下一頁、點選的頁面的頁數
   app.factory('pageFactory', function ($http ) {
     var totalCount = @ViewBag.totalCount; //Asp.net MVC參數可以直接在angular中使用
        var currentPage =1;
        return{          
            getData:function (url) {  
                return $http.get(url).then(function(res){
                    return res.data;
                });
            },          
            postData:function(url, formData)       
            {
                return($http.post(url,formData, {
                    transformRequest: angular.identity,
                    headers: {'Content-Type': undefined}
                }).then(function (res) {
                    return res.data;
                }));
            },
            pageList:function(){
                var arrayPage=[];
                for(var i=1;i<=totalCount;i++){
                    arrayPage.push(i);
                }
                return arrayPage;
            },
            nextPage:function(thispage)
            {
                currentPage=  thispage+1;
                if(currentPage > totalCount)
                {
                    currentPage=totalCount;
                }
                return currentPage;
            },
            prePage:function (thispage)
            {
                currentPage=  thispage-1;
                if(currentPage <= 0)
                {
                    currentPage=1;
                }
                return currentPage;
            },
            setPage:function (thispage) {
                currentPage = thispage;
                return currentPage;
            },
            nowPage:function(){
                return currentPage;
            }
        }
    });

announceController
    分頁中如果有查詢功能,就要把值submit送出查詢,再把取回的資料設給列表的$scope變數,所以使用angularpost協定送出查詢的結果

    var app = angular.module("servFileModel", []);
    app.controller("announceController", function ($scope, $http, pageFactory,$q) {
        $scope.currentPage =pageFactory.nowPage();
        $scope.pages=pageFactory.pageList(); //取得所有分頁陣列
        //預設第一頁,將查詢的參數值往後端送
        var tempObj = null;     
        var defaultFormData = new FormData();
        defaultFormData.append("currentPage", 1);
        defaultFormData.append("searchProductType", "");
        defaultFormData.append("searchFileName", "");
        defaultFormData.append("searchFileVersion", "");      
        var promise =   pageFactory.postData('../Test/SearchAnnounce',defaultFormData).then(function(response) {
            $scope.testNodes=response;
        });
        $scope.nextPage = function (currentPage) {
            $scope.currentPage = pageFactory.nextPage(currentPage);
            var nextFormData = new FormData();
            nextFormData.append("currentPage", $scope.currentPage);
            nextFormData.append("searchProductType", ($scope.searchProductType==undefined) ? "" : $scope.searchProductType.key);
            nextFormData.append("searchFileName", ($scope.searchFileName==undefined)? "" : $scope.searchFileName);
            nextFormData.append("searchFileVersion", ($scope.searchFileVersion==undefined)? "" : $scope.searchFileVersion);
            var promise =   pageFactory.postData('../Test/SearchAnnounce',nextFormData).then(function(response) {
                $scope.testNodes=response;
            });
        }
        $scope.prePage=function (currentPage) {
            $scope.currentPage = pageFactory.prePage(currentPage);            
            var preFormData = new FormData();
            preFormData.append("currentPage", $scope.currentPage);
            preFormData.append("searchProductType", ($scope.searchProductType==undefined) ? "" : $scope.searchProductType.key);
            preFormData.append("searchFileName", ($scope.searchFileName==undefined)? "" : $scope.searchFileName);
            preFormData.append("searchFileVersion", ($scope.searchFileVersion==undefined)? "" : $scope.searchFileVersion);           
            var promise =   pageFactory.postData('../Test/SearchAnnounce',preFormData).then(function(response) {
                $scope.testNodes=response;
            });
        }
        $scope.setPage=function (currentPage) {           
            $scope.currentPage = pageFactory.setPage(currentPage);
            var setCurrentFormData = new FormData();
            setCurrentFormData.append("currentPage", $scope.currentPage);
            setCurrentFormData.append("searchProductType", ($scope.searchProductType==undefined) ? "" : $scope.searchProductType.key);
            setCurrentFormData.append("searchFileName", ($scope.searchFileName==undefined)? "" : $scope.searchFileName);
            setCurrentFormData.append("searchFileVersion", ($scope.searchFileVersion==undefined)? "" : $scope.searchFileVersion);                       
            var promise =   pageFactory.postData('../Test/SearchAnnounce',setCurrentFormData).then(function(response) {
                $scope.testNodes=response;
            });
        }
        $scope.productsClassification=[
           {"key": "COOPER"},{"key":"i_cpnews"},{"key":"健康檢查"},{"key":"簡訊"},{"key":"沖帳"},{"key":"口檢"},{"key":"病歷簽章"},{"key":"病歷抽審"},{"key":"巡迴醫療"},{"key":"其他"},];

        $scope.searchSubmit=function () {
            $('#search').modal('hide');
            var reflashFormData = new FormData();
            reflashFormData.append("currentPage", $scope.currentPage);
            reflashFormData.append("searchProductType", ($scope.searchProductType==undefined) ? "" : $scope.searchProductType.key);
            reflashFormData.append("searchFileName", ($scope.searchFileName==undefined)? "" : $scope.searchFileName);
            reflashFormData.append("searchFileVersion", ($scope.searchFileVersion==undefined)? "" : $scope.searchFileVersion);              
            var promise =   pageFactory.postData('../Test/SearchAnnounce',reflashFormData).then(function(response) {
                $scope.testNodes=response;               
            });
        }
        $scope.line=function (fileName,fileVersion) {    
// line 的分享,目前只支援到行動APP,所以電腦版的LINE 目前並不支援
            var lineURL = "line://msg/text/檔名:"+fileName.trim()+" 版本:"+fileVersion.trim()+" 目前已釋出測試版";
            window.open(lineURL);
        }
        $("[data-toggle=tooltip]").tooltip();
    });

成果展示:


















以上是作者開發MVCAngular的經驗談,感謝各位有耐心的讀完這篇文章,希望這篇文章對大家有幫助。

沒有留言:

張貼留言