AngularJS中自定義有關一個表格的Directive

Darren Ji發表於2016-01-24

 

本篇體驗在AngularJS中自定義一個有關表格的Directive。表格的需求包括:

● 表格結構

<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Street</th>
            <th>Age</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>></td>
            <td>></td>
            <td>></td>
        </tr>
    </tbody>
</table>
<div>4行</div>

● 點選某個th,就對該列進行排序
● 可以給表頭取別名
● 可以設定某個列是否顯示
● 表格下方有一行顯示總行數

我們希望表格按如下方式展示:

<table-helper datasource="customers" clumnmap="[{name: 'Name'}, {street: 'Street'}, {age: 'Age'}, {url: 'URL', hidden: true}]"></table-helper>

以上,

datasource的資料來源來自controller中$scope.customers,大致是{name: 'David',street: '1234 Anywhere St.',age: 25,url: 'index.html'}這樣的格式,具體略去。

columnmap負責給列取別名,並且決定是否顯示某個列。

如何實現呢?

Directive大致是這樣的:

 

var tableHelper = function(){
    var template = '',
        link = function(scope, element, attrs){
        
        }
        
        return {
            restrict: 'E',
            scope: {
                columnmap: '=',
                datasource: '='
            },
            link:link,
            template:template
        };        
}

angular.module('directiveModule')
    .directive('tableHelper', tableHelper);

 

具體來說,

首先要監視datasource的變化,一旦有變化,就重新載入表格。

 

scope.$watchCollection('datasource', render);

//初始化表格
function render(){
    if(scope.datasource && scope.datasource.length){
        table += tableStart;
        table += renderHeader();
        table += renderRows() + tableEnd;

        //載入統計行
        renderTable();
    }
}

 

載入表格大致分成了三個步驟,載入表頭,載入表格體,載入統計行。

 

//載入頭部
function renderHeader(){
    var tr = '<tr>';
    for(var prop in scope.datasource[0]){
        //{name: 'David',street: '1234 Anywhere St.',age: 25,url: 'index.html'}
        //根據原始列名獲取別名,並考慮了是否顯示列的情況
        var val = getColumnName(prop);
        if(val){

            //visibleProps儲存的是原始列名
            visibleProps.push(prop);
            tr += '<th>' + val + '</th>';
        }
    }
    tr += '</tr>';
    tr = '<thead>' + tr '</thead>';
    return tr;
}

//載入行
function renderRows(){
    var rows = '';
    for(var i = 0, len = scope.datasource.length; i < len; i++){
        rows += '<tr>';
        var row = scope.datasource[i];
        for(var prop in row){
            //當前遍歷的原始列名是否在visibleProps集合中,該集合儲存的是原始列名
            if(visibleProps.indexOf(prop) > -1){
                rows += '<td>' + row[prop] + '</td>';
            }
        }
        rows += '</tr>';
    }
    rows = '<tbody>' + rows + '</tbody>';
    return rows;
}

//載入統計行
function renderTable(){
    table += '<br /><div class="rowCount">' + scope.datasource.length + '行</div>';
    element.html(table);
    table = '';
}

 

載入表頭的時候,用到了一個根據原始列名獲取別名的方法。

 

//根據原始列名獲取列的別名,並考慮是否隱藏列的情況
function getColumnName(prop){
    if(!scope.columnmap) return prop;

    //得到[{name: 'Name'}]
    var val = filterColumnMap(prop);
    if(val && val.length && !val[0].hidden) return val[0][prop];
    else return null;
}

 

在getColumnName方法中,用到了一個根據原始列名

 

//比如根據name屬性,這裡得到[{name: 'Name'}]
//[{name: 'Name'}, {street: 'Street'}, {age: 'Age'}, {url: 'URL', hidden: true}]
function filterColumnMap(prop) {
    var val = scope.columnmap.filter(function(map) {
        if (map[prop]) {
            return true;
        }
        return false;
    });
    return val;
}   

 

具體程式碼如下:

(function(){
    var tableHelper = fucntion(){
        var template = '<div class="tableHelper"></div>',
            link = function(scope, element, attrs){
                var headerCols = [], //表頭列們
                    tableStart = '<table>',
                    tableEnd = '</table>',
                    table = '',
                    visibleProps = [],//可見列
                    sortCol = null,//排序列
                    sortDir =1;
                
                //監視集合
                sscope.$watchCollection('datasource', render);
                
                //給表頭th繫結事件
                wireEvents();
                
                //初始化表格
                function render(){
                    if(scope.datasource && scope.datasource.length){
                        table += tableStart;
                        table += renderHeader();
                        table += renderRows() + tableEnd;
                        
                        //載入統計行
                        renderTable();
                    }
                }
                
                //給th新增click事件
                function wireEvents()
                {
                    element.on('click', function(event){
                        if(event.srcElement.nodeName === 'TH'){
                            //獲取列的名稱
                            var val = event.srcElement.innerHTML;
                            
                            //根據列的別名獲取原始列名
                            var col = (scope.columnmap) ? getRawColumnName(val) : val;
                            if(col){
                            
                                //對該列進行排序
                                sort(col);
                            }
                        }
                    });
                }
                
                //給某列排序
                function sort(col){
                    if(sortCol === col){
                        sortDir = sortDir * -1;
                    }
                    sortCol = col;
                    scope.datasource.sort(function(a,b){
                        if(a[col] > b[col]) return 1 * sortDir;
                        if(a[col] < b[col]) return -1 * sortDir;
                        return 0;
                    });
                    
                    //重新載入表格
                    render();
                }
                
                //載入頭部
                function renderHeader(){
                    var tr = '<tr>';
                    for(var prop in scope.datasource[0]){
                        //{name: 'David',street: '1234 Anywhere St.',age: 25,url: 'index.html'}
                        //根據原始列名獲取別名,並考慮了是否顯示列的情況
                        var val = getColumnName(prop);
                        if(val){
                        
                            //visibleProps儲存的是原始列名
                            visibleProps.push(prop);
                            tr += '<th>' + val + '</th>';
                        }
                    }
                    tr += '</tr>';
                    tr = '<thead>' + tr '</thead>';
                    return tr;
                }
                
                //載入行
                function renderRows(){
                    var rows = '';
                    for(var i = 0, len = scope.datasource.length; i < len; i++){
                        rows += '<tr>';
                        var row = scope.datasource[i];
                        for(var prop in row){
                            //當前遍歷的原始列名是否在visibleProps集合中,該集合儲存的是原始列名
                            if(visibleProps.indexOf(prop) > -1){
                                rows += '<td>' + row[prop] + '</td>';
                            }
                        }
                        rows += '</tr>';
                    }
                    rows = '<tbody>' + rows + '</tbody>';
                    return rows;
                }
                
                //載入統計行
                function renderTable(){
                    table += '<br /><div class="rowCount">' + scope.datasource.length + '行</div>';
                    element.html(table);
                    table = '';
                }
                
                //根據列的別名獲取原始列名
                function getRawColumnName(friendlyCol) {
                    var rawCol;
                    //columnmap =[{name: 'Name'}, {street: 'Street'}, {age: 'Age'}, {url: 'URL', hidden: true}]
                    scope.columnmap.forEach(function(colMap) {
                        //{name: 'Name'}
                        for (var prop in colMap) {
                          if (colMap[prop] === friendlyCol) {
                             rawCol = prop;
                             break;
                          }
                        }
                        return null;
                    });
                    return rawCol;
                }

                function pushColumns(rawCol, renamedCol) {
                    visibleProps.push(rawCol);
                    scope.columns.push(renamedCol);
                }
                
                //比如根據name屬性,這裡得到[{name: 'Name'}]
                //[{name: 'Name'}, {street: 'Street'}, {age: 'Age'}, {url: 'URL', hidden: true}]
                function filterColumnMap(prop) {
                    var val = scope.columnmap.filter(function(map) {
                        if (map[prop]) {
                            return true;
                        }
                        return false;
                    });
                    return val;
                }                
                
                //根據原始列名獲取列的別名,並考慮是否隱藏列的情況
                function getColumnName(prop){
                    if(!scope.columnmap) return prop;
                    
                    //得到[{name: 'Name'}]
                    var val = filterColumnMap(prop);
                    if(val && val.length && !val[0].hidden) return val[0][prop];
                    else return null;
                }
            };
        
        return {
            restrict: 'E',
            scope: {
                columnmap: '=',
                datasource: '='
            },
            link:link,
            template:template
        };
    };
    
    angular.module('directiveModule')
        .directive('tableHelper', tableHelper);
}());

 

相關文章