├── .gitignore ├── README.md ├── examples ├── js │ ├── app.factory.js │ └── angular-sanitize.js ├── css │ └── style.css ├── example12.html ├── example01.html ├── example10.html ├── example03.html ├── example02.html ├── example07.html ├── example08.html ├── example09.html ├── example11.html ├── example14-load-data-from-server.html ├── example04.html ├── example05.html └── example13.html ├── LICENSE └── src └── solo.table.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | solo-angular-table 2 | ================== 3 | 4 | AngularJS HTML table (grid) with sorting, filters and pagination. 5 | 6 | AngularJS таблица (список) с сортировкой, фильтрами и постраничной навигацией. 7 | 8 | Примеры и документация (в разработке) см. http://solo-framework.github.io/solo-angular-table/ 9 | -------------------------------------------------------------------------------- /examples/js/app.factory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Создание нового Angular приложения с заданными настройками. 3 | * Позволяет разместить на одной странице несколько приложений. 4 | * 5 | * @param elementId ID DOM-элемента, к которому прикрепляется приложение 6 | * @param appName Имя приложения (главного модуля) 7 | * @param modules Список модулей, подключаемых к приложению 8 | * @constructor 9 | */ 10 | var AppFactory = function(elementId, appName, modules) 11 | { 12 | 'use strict'; 13 | 14 | if (!modules) 15 | modules = []; 16 | 17 | var el = document.getElementById(elementId); 18 | 19 | angular.module(appName, modules).config([ 20 | 21 | /** 22 | * Т.к. используется Smarty, то символы {{ и }} нужно заменить, 23 | * например, на <[ и ]> 24 | */ 25 | "$interpolateProvider", function($interpolateProvider){ 26 | $interpolateProvider.startSymbol('<['); 27 | $interpolateProvider.endSymbol(']>'); 28 | } 29 | ]); 30 | modules.push(appName); 31 | angular.bootstrap(el, modules); 32 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 afi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /* Валидаторы */ 5 | label.error, div.error 6 | { 7 | display: block; 8 | color: red; 9 | font-size: .9em; 10 | 11 | margin-top: -8px; 12 | } 13 | label.success, div.success 14 | { 15 | display: block; 16 | color: #1b7007; 17 | font-size: .9em; 18 | 19 | margin-top: -8px; 20 | } 21 | 22 | 23 | /* Переопределения bootstrap */ 24 | .form-horizontal .control-label 25 | { 26 | text-align: left; 27 | } 28 | .form-horizontal .controls 29 | { 30 | margin-left: 0; 31 | } 32 | 33 | .control-label 34 | { 35 | font-weight: bold; 36 | } 37 | 38 | .form-actions 39 | { 40 | background-color: #ffffff; 41 | padding-left: 0px; 42 | } 43 | 44 | .help-block 45 | { 46 | font-size: 0.7em; 47 | } 48 | 49 | .table thead th { 50 | vertical-align: middle; 51 | } 52 | 53 | /* remove a border around buttons and links*/ 54 | a:focus { outline: none; } 55 | .modal-open .modal,.btn:focus{ 56 | outline: none!important 57 | } 58 | 59 | .modal{ 60 | width: auto; 61 | } 62 | 63 | /* eo Переопределения bootstrap */ 64 | 65 | .login-form 66 | { 67 | margin-top: 12px; 68 | background-color: #ffffff !important; 69 | } 70 | 71 | 72 | .login-form > form 73 | { 74 | margin-bottom: 0; 75 | } 76 | 77 | 78 | .current-box-row { 79 | background-color: greenyellow !important; 80 | } 81 | 82 | .sidebar-block 83 | { 84 | background-color: #ffffff; 85 | border: 1px solid rgba(0, 0, 0, 0.15); 86 | border-radius: 0px; 87 | display: block; 88 | font-size: 1em; 89 | line-height: 18px; 90 | margin-bottom: 5px; 91 | margin-right: -15px; 92 | padding: 0px 6px 6px 15px; 93 | } 94 | 95 | #quickCheckForm 96 | { 97 | margin-bottom: 0; 98 | } 99 | 100 | #quick-check-allowed 101 | { 102 | font-size: 16px; 103 | color: green; 104 | } 105 | 106 | #quick-check-disallowed 107 | { 108 | font-size: 16px; 109 | color: red; 110 | } 111 | 112 | .sub-tilte 113 | { 114 | font-style: italic; 115 | margin-top: -15px; 116 | margin-bottom: 15px; 117 | } 118 | 119 | .solo-table-sort-asc > .solo-column-arrow 120 | { 121 | position: relative; 122 | top: 10px; 123 | margin-left: 5px; 124 | border-color: black transparent; 125 | border-style: solid; 126 | border-width: 5px 5px 0px 5px; 127 | height: 0px; 128 | width: 0px; 129 | } 130 | .solo-table-sort-desc > .solo-column-arrow 131 | { 132 | position: relative; 133 | top: -10px; 134 | margin-left: 5px; 135 | border-color: black transparent; 136 | border-style: solid; 137 | border-width: 0px 5px 5px 5px; 138 | height: 0px; 139 | width: 0px; 140 | } 141 | 142 | .solo-table-column-cursor 143 | { 144 | cursor: pointer; 145 | } 146 | 147 | .rule-result-text 148 | { 149 | width: 550px; 150 | } -------------------------------------------------------------------------------- /examples/example12.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |57 | You can pass your data into grid via JSON object 58 |
59 | 60 | 61 || Id | 88 |Name | 89 |Address | 90 |
|---|---|---|
| <[item.id]> | 102 |103 | | 104 | |
24 | The simplest example with a pager 25 |
26 | 27 | 28 || Id | 52 |Name | 53 |Address | 54 |
|---|---|---|
| <[item.id]> | 65 |<[item.name]> | 66 |<[item.address]> | 67 |
55 | In this example there is no table. 56 | You can use any HTML elements to build a list. 57 |
58 | 59 | 60 |83 | You can sort them 84 | by name 85 | or 86 | by id 87 |
88 | 89 |55 | This example with filter 56 |
57 | 58 | 59 || Id | 88 |Name | 89 |Address | 90 |
|---|---|---|
| <[item.id]> | 100 |<[item.name]> | 101 |<[item.address]> | 102 |
56 | This example with pager and sortable columns. In this example you can sort data by Id and Name columns.
57 | There are 2 mode of sorting. The first is ASC and DESC (this example). The second is DEFAULT, ASC and DESC (see example #8).
58 |
| Id | 89 |Name | 90 |Address | 91 |
|---|---|---|
| <[item.id]> | 102 |<[item.name]> | 103 |<[item.address]> | 104 |
55 | This example with pager and sortable columns. You can define a default sorting for a column using default-sort directive. 56 |
57 | 58 | 59 || Id | 86 | 87 | 91 |Name | 92 |Address | 93 |
|---|---|---|
| <[item.id]> | 104 |<[item.name]> | 105 |<[item.address]> | 106 |
55 | This example with pager and sortable columns. You can sort data by Id and Name columns.
56 | In this example you can see the 2nd mode of sorting in action.
57 | There are 3 state: DEFAULT, ASC and DESC.
58 |
| Id | 90 |Name | 91 |Address | 92 |
|---|---|---|
| <[item.id]> | 103 |<[item.name]> | 104 |<[item.address]> | 105 |
55 | In this example you can change size of page dynamically. 56 |
57 | 58 | 59 || Id | 97 |Name | 98 |Address | 99 |
|---|---|---|
| <[item.id]> | 110 |<[item.name]> | 111 |<[item.address]> | 112 |
57 | You can highlight substrings during the searching 58 |
59 | 60 | 61 || Id | 90 |Name | 91 |Address | 92 |
|---|---|---|
| <[item.id]> | 104 |105 | | 106 | |
56 | This example shows how can you load data from remote server 57 |
58 | 59 | 60 || Id | 89 |Name | 90 |Address | 91 |
|---|---|---|
| <[item.id]> | 103 |<[item.name]> | 104 |<[item.address]> | 105 |
55 | This example shows how to use several different filters. 56 | You can filter items by dropdown list and by selected fields. 57 |
58 | 59 | 60 || Id | 96 |Prefix | 97 |Name | 98 |Address | 99 |
|---|---|---|---|
| <[item.id]> | 109 |<[item.prefix]> | 110 |<[item.name]> | 111 |<[item.address]> | 112 |
55 | You can write your own filter.
56 | For example, you can select persons older than selected year.
57 |
58 |
| Id | 90 |Name | 91 |Year | 92 |Address | 93 |
|---|---|---|---|
| <[item.id]> | 103 |<[item.name]> | 104 |<[item.year]> | 105 |<[item.address]> | 106 |
24 | You can place more then one independent tables within an application. 25 |
26 | 27 || Id | 56 |Name | 57 |Address | 58 |
|---|---|---|
| <[item.id]> | 69 |<[item.name]> | 70 |<[item.address]> | 71 |
| Id | 105 |Name | 106 |Address | 107 |
|---|---|---|
| <[item.id]> | 118 |<[item.name]> | 119 |<[item.address]> | 120 |
| Directive | 79 |How | 80 |Source | 81 |Rendered | 82 |
| ng-bind-html | 85 |Automatically uses $sanitize | 86 |<div ng-bind-html="snippet"> |
87 | 88 | |
| ng-bind-html | 91 |Bypass $sanitize by explicitly trusting the dangerous value | 92 |
93 | <div ng-bind-html="deliberatelyTrustDangerousSnippet()"> 94 | </div>95 | |
96 | 97 | |
| ng-bind | 100 |Automatically escapes | 101 |<div ng-bind="snippet"> |
102 | 103 | |
an html\nclick here\nsnippet
'); 111 | }); 112 | 113 | it('should inline raw snippet if bound to a trusted value', function() { 114 | expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()). 115 | toBe("an html\n" + 116 | "click here\n" + 117 | "snippet
"); 118 | }); 119 | 120 | it('should escape snippet without any filter', function() { 121 | expect(element(by.css('#bind-default div')).getInnerHtml()). 122 | toBe("<p style=\"color:blue\">an html\n" + 123 | "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + 124 | "snippet</p>"); 125 | }); 126 | 127 | it('should update', function() { 128 | element(by.model('snippet')).clear(); 129 | element(by.model('snippet')).sendKeys('new text'); 130 | expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()). 131 | toBe('new text'); 132 | expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe( 133 | 'new text'); 134 | expect(element(by.css('#bind-default div')).getInnerHtml()).toBe( 135 | "new <b onclick=\"alert(1)\">text</b>"); 136 | }); 137 || Filter | 567 |Source | 568 |Rendered | 569 |
| linky filter | 572 |
573 | <div ng-bind-html="snippet | linky">574 | |
575 | 576 | 577 | | 578 |
| linky target | 581 |
582 | <div ng-bind-html="snippetWithTarget | linky:'_blank'">583 | |
584 | 585 | 586 | | 587 |
| no filter | 590 |<div ng-bind="snippet"> |
591 | 592 | |