tableSpec.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. describe('ng-table', function() {
  2. var data = [
  3. { id: 1, name: "Moroni", age: 50, money: -10 },
  4. { id: 2, name: "Tiancum", age: 43, money: 120 },
  5. { id: 3, name: "Jacob", age: 27, money: 5.5 },
  6. { id: 4, name: "Nephi", age: 29, money: -54 },
  7. { id: 5, name: "Enos", age: 34, money: 110 },
  8. { id: 6, name: "Tiancum", age: 43, money: 1000 },
  9. { id: 7, name: "Jacob", age: 27, money: -201 },
  10. { id: 8, name: "Nephi", age: 29, money: 100 },
  11. { id: 9, name: "Enos", age: 34, money: -52.5 },
  12. { id: 10, name: "Tiancum", age: 43, money: 52.1 },
  13. { id: 11, name: "Jacob", age: 27, money: 110 },
  14. { id: 12, name: "Nephi", age: 29, money: -55 },
  15. { id: 13, name: "Enos", age: 34, money: 551 },
  16. { id: 14, name: "Tiancum", age: 43, money: -1410 },
  17. { id: 15, name: "Jacob", age: 27, money: 410 },
  18. { id: 16, name: "Nephi", age: 29, money: 100 },
  19. { id: 17, name: "Enos", age: 34, money: -100 }
  20. ];
  21. var NgTableParams;
  22. beforeEach(module('ngTable'));
  23. var scope;
  24. beforeEach(inject(function($rootScope, _NgTableParams_) {
  25. scope = $rootScope.$new(true);
  26. NgTableParams = _NgTableParams_;
  27. }));
  28. function createNgTableParams(settings) {
  29. var initialParams;
  30. if (arguments.length === 2){
  31. initialParams = arguments[0];
  32. settings = arguments[1];
  33. }
  34. settings = angular.extend({}, {
  35. filterDelay: 0
  36. }, settings);
  37. var tableParams = new NgTableParams(initialParams, settings);
  38. spyOn(tableParams.settings(), 'getData').and.callThrough();
  39. return tableParams;
  40. }
  41. describe('basics', function(){
  42. var elm;
  43. beforeEach(inject(function($compile, $q) {
  44. elm = angular.element(
  45. '<div>' +
  46. '<table ng-table="tableParams" show-filter="true">' +
  47. '<tr ng-repeat="user in $data">' +
  48. '<td data-header-title="\'Sort by Name\'" data-title="nameTitle()" filter="{ \'name\': \'text\' }" sortable="\'name\'" data-header-class="getCustomClass($column)"' +
  49. ' ng-if="showName">' +
  50. '{{user.name}}' +
  51. '</td>' +
  52. '<td x-data-header-title="\'Sort by Age\'" x-data-title="ageTitle()" sortable="\'age\'" x-data-header-class="getCustomClass($column)"' +
  53. ' ng-if="showAge">' +
  54. '{{user.age}}' +
  55. '</td>' +
  56. '<td header-title="\'Sort by Money\'" title="moneyTitle()" filter="{ \'action\': \'select\' }" filter-data="money($column)" header-class="getCustomClass($column)"' +
  57. ' ng-if="showMoney">' +
  58. '{{user.money}}' +
  59. '</td>' +
  60. '</tr>' +
  61. '</table>' +
  62. '</div>');
  63. scope.nameTitle = function(){
  64. return 'Name of person';
  65. };
  66. scope.ageTitle = function(){
  67. return 'Age';
  68. };
  69. scope.moneyTitle = function(){
  70. return 'Money';
  71. };
  72. scope.showName = true;
  73. scope.showAge = true;
  74. scope.showMoney = true;
  75. scope.ageTitle = function(){
  76. return 'Age';
  77. };
  78. scope.moneyTitle = function(){
  79. return 'Money';
  80. };
  81. scope.getCustomClass = function($column){
  82. if ($column.title().indexOf('Money') !== -1){
  83. return 'moneyHeaderClass';
  84. } else{
  85. return 'customClass';
  86. }
  87. };
  88. scope.money = function(/*$column*/) {
  89. var def = $q.defer();
  90. def.resolve([{
  91. 'id': 10,
  92. 'title': '10'
  93. }]);
  94. return def;
  95. };
  96. $compile(elm)(scope);
  97. scope.$digest();
  98. }));
  99. it('should create table header', function() {
  100. var thead = elm.find('thead');
  101. expect(thead.length).toBe(1);
  102. var rows = thead.find('tr');
  103. expect(rows.length).toBe(2);
  104. var titles = angular.element(rows[0]).find('th');
  105. expect(titles.length).toBe(3);
  106. expect(angular.element(titles[0]).text().trim()).toBe('Name of person');
  107. expect(angular.element(titles[1]).text().trim()).toBe('Age');
  108. expect(angular.element(titles[2]).text().trim()).toBe('Money');
  109. expect(angular.element(rows[1]).hasClass('ng-table-filters')).toBeTruthy();
  110. var filters = angular.element(rows[1]).find('th');
  111. expect(filters.length).toBe(3);
  112. expect(angular.element(filters[0]).hasClass('filter')).toBeTruthy();
  113. expect(angular.element(filters[1]).hasClass('filter')).toBeTruthy();
  114. expect(angular.element(filters[2]).hasClass('filter')).toBeTruthy();
  115. });
  116. it('should create table header classes', function() {
  117. var thead = elm.find('thead');
  118. var rows = thead.find('tr');
  119. var titles = angular.element(rows[0]).find('th');
  120. expect(angular.element(titles[0]).hasClass('header')).toBeTruthy();
  121. expect(angular.element(titles[1]).hasClass('header')).toBeTruthy();
  122. expect(angular.element(titles[2]).hasClass('header')).toBeTruthy();
  123. expect(angular.element(titles[0]).hasClass('sortable')).toBeTruthy();
  124. expect(angular.element(titles[1]).hasClass('sortable')).toBeTruthy();
  125. expect(angular.element(titles[2]).hasClass('sortable')).toBeFalsy();
  126. expect(angular.element(titles[0]).hasClass('customClass')).toBeTruthy();
  127. expect(angular.element(titles[1]).hasClass('customClass')).toBeTruthy();
  128. expect(angular.element(titles[2]).hasClass('moneyHeaderClass')).toBeTruthy();
  129. });
  130. it('should create table header titles', function() {
  131. var thead = elm.find('thead');
  132. var rows = thead.find('tr');
  133. var titles = angular.element(rows[0]).find('th');
  134. expect(angular.element(titles[0]).attr('title').trim()).toBe('Sort by Name');
  135. expect(angular.element(titles[1]).attr('title').trim()).toBe('Sort by Age');
  136. expect(angular.element(titles[2]).attr('title').trim()).toBe('Sort by Money');
  137. });
  138. it('should show scope data', function() {
  139. var tbody = elm.find('tbody');
  140. expect(tbody.length).toBe(1);
  141. var rows = tbody.find('tr');
  142. expect(rows.length).toBe(0);
  143. var params = new NgTableParams({
  144. page: 1, // show first page
  145. count: 10 // count per page
  146. }, {
  147. total: data.length, // length of data
  148. getData: function($defer, params) {
  149. $defer.resolve(data.slice((params.page() - 1) * params.count(), params.page() * params.count()));
  150. }
  151. });
  152. scope.tableParams = params;
  153. scope.$digest();
  154. rows = tbody.find('tr');
  155. expect(rows.length).toBe(10);
  156. scope.tableParams.page(2);
  157. scope.$digest();
  158. rows = tbody.find('tr');
  159. expect(rows.length).toBe(7);
  160. params.total(20);
  161. scope.$digest();
  162. rows = tbody.find('tr');
  163. expect(rows.length).toBe(7);
  164. });
  165. it('should show data-title-text', function() {
  166. var tbody = elm.find('tbody');
  167. var params = new NgTableParams({
  168. page: 1, // show first page
  169. count: 10 // count per page
  170. }, {
  171. total: data.length, // length of data
  172. getData: function($defer, params) {
  173. $defer.resolve(data);
  174. }
  175. });
  176. scope.tableParams = params;
  177. scope.$digest();
  178. var filterRow = angular.element(elm.find('thead').find('tr')[1]);
  179. var filterCells = filterRow.find('th');
  180. expect(angular.element(filterCells[0]).attr('data-title-text').trim()).toBe('Name of person');
  181. expect(angular.element(filterCells[1]).attr('data-title-text').trim()).toBe('Age');
  182. expect(angular.element(filterCells[2]).attr('data-title-text').trim()).toBe('Money');
  183. var dataRows = elm.find('tbody').find('tr');
  184. var dataCells = angular.element(dataRows[0]).find('td');
  185. expect(angular.element(dataCells[0]).attr('data-title-text').trim()).toBe('Name of person');
  186. expect(angular.element(dataCells[1]).attr('data-title-text').trim()).toBe('Age');
  187. expect(angular.element(dataCells[2]).attr('data-title-text').trim()).toBe('Money');
  188. });
  189. it('should show/hide columns', function() {
  190. var tbody = elm.find('tbody');
  191. scope.tableParams = new NgTableParams({
  192. page: 1, // show first page
  193. count: 10 // count per page
  194. }, {
  195. total: data.length,
  196. data: data
  197. });
  198. scope.$digest();
  199. var headerRow = angular.element(elm.find('thead').find('tr')[0]);
  200. expect(headerRow.find('th').length).toBe(3);
  201. var filterRow = angular.element(elm.find('thead').find('tr')[1]);
  202. expect(filterRow.find('th').length).toBe(3);
  203. var dataRow = angular.element(elm.find('tbody').find('tr')[0]);
  204. expect(dataRow.find('td').length).toBe(3);
  205. scope.showName = false;
  206. scope.$digest();
  207. expect(headerRow.find('th').length).toBe(2);
  208. expect(filterRow.find('th').length).toBe(2);
  209. expect(dataRow.find('td').length).toBe(2);
  210. expect(angular.element(headerRow.find('th')[0]).text().trim()).toBe('Age');
  211. expect(angular.element(headerRow.find('th')[1]).text().trim()).toBe('Money');
  212. expect(angular.element(filterRow.find('th')[0]).find('input').length).toBe(0);
  213. expect(angular.element(filterRow.find('th')[1]).find('select').length).toBe(1);
  214. });
  215. });
  216. describe('title-alt', function() {
  217. var elm;
  218. beforeEach(inject(function($compile) {
  219. elm = angular.element(
  220. '<table ng-table="tableParams">' +
  221. '<tr ng-repeat="user in $data">' +
  222. '<td title="\'Name of person\'" title-alt="\'Name\'">{{user.name}}</td>' +
  223. '<td title="\'Age of person\'" data-title-alt="\'Age\'">{{user.age}}</td>' +
  224. '<td title="\'Money earned\'" x-data-title-alt="\'£\'">{{user.money}}</td>' +
  225. '</tr>' +
  226. '</table>');
  227. $compile(elm)(scope);
  228. scope.$digest();
  229. var params = new NgTableParams({
  230. page: 1, // show first page
  231. count: 10 // count per page
  232. }, {
  233. total: data.length, // length of data
  234. getData: function($defer, params) {
  235. $defer.resolve(data);
  236. }
  237. });
  238. scope.tableParams = params;
  239. scope.$digest();
  240. }));
  241. it('should show as data-title-text', inject(function($compile) {
  242. var filterRow = angular.element(elm.find('thead').find('tr')[1]);
  243. var filterCells = filterRow.find('th');
  244. expect(angular.element(filterCells[0]).attr('data-title-text').trim()).toBe('Name');
  245. expect(angular.element(filterCells[1]).attr('data-title-text').trim()).toBe('Age');
  246. expect(angular.element(filterCells[2]).attr('data-title-text').trim()).toBe('£');
  247. var dataRows = elm.find('tbody').find('tr');
  248. var dataCells = angular.element(dataRows[0]).find('td');
  249. expect(angular.element(dataCells[0]).attr('data-title-text').trim()).toBe('Name');
  250. expect(angular.element(dataCells[1]).attr('data-title-text').trim()).toBe('Age');
  251. expect(angular.element(dataCells[2]).attr('data-title-text').trim()).toBe('£');
  252. }));
  253. });
  254. describe('sorting', function() {
  255. it('should provide $column definition', inject(function($compile) {
  256. var columnDef;
  257. var elm = angular.element(
  258. '<table ng-table="tableParams">' +
  259. '<tr ng-repeat="user in $data">' +
  260. '<td title="\'Age\'" sortable="captureColumn($column)">{{user.age}}</td>' +
  261. '</tr>' +
  262. '</table>');
  263. scope.captureColumn = function($column){
  264. columnDef = $column;
  265. return 'age'
  266. };
  267. $compile(elm)(scope);
  268. scope.$digest();
  269. expect(columnDef).toBeDefined();
  270. }));
  271. it('should apply initial sort', inject(function ($compile) {
  272. var elm = angular.element(
  273. '<table ng-table="tableParams">' +
  274. '<tr ng-repeat="user in $data"><td title="\'Age\'" sortable="\'age\'">{{user.age}}</td></tr>' +
  275. '</table>');
  276. $compile(elm)(scope);
  277. var actualSort;
  278. scope.tableParams = new NgTableParams({
  279. sorting: { age: 'desc' }
  280. }, {
  281. getData: function($defer, params){
  282. actualSort = params.sorting();
  283. $defer.resolve([]);
  284. }});
  285. scope.$digest();
  286. expect(actualSort.age).toBe('desc');
  287. }));
  288. it('when sorting changes should trigger reload of table', inject(function ($compile) {
  289. var elm = angular.element(
  290. '<table ng-table="tableParams">' +
  291. '<tr ng-repeat="user in $data"><td title="\'Age\'" sortable="\'age\'">{{user.age}}</td></tr>' +
  292. '</table>');
  293. $compile(elm)(scope);
  294. var params = createNgTableParams();
  295. scope.tableParams = params;
  296. scope.$digest();
  297. params.settings().getData.calls.reset();
  298. params.sorting()['age'] = 'desc';
  299. scope.$digest();
  300. expect(params.settings().getData.calls.count()).toBe(1);
  301. params.sorting()['age'] = 'asc';
  302. scope.$digest();
  303. expect(params.settings().getData.calls.count()).toBe(2);
  304. // setting the same sort order should not trigger reload
  305. params.sorting({ age: 'asc'});
  306. scope.$digest();
  307. expect(params.settings().getData.calls.count()).toBe(2);
  308. }));
  309. });
  310. describe('paging', function() {
  311. var elm;
  312. beforeEach(inject(function($compile) {
  313. elm = angular.element(
  314. '<table ng-table="tableParams">' +
  315. '<tr ng-repeat="user in $data">' +
  316. '<td title="\'Age\'">{{user.age}}</td>' +
  317. '</tr>' +
  318. '</table>');
  319. dataCallCount = 0;
  320. $compile(elm)(scope);
  321. scope.$digest();
  322. }));
  323. function verifyPageWas(expectedPage){
  324. expect(scope.tableParams.settings().getData.calls.argsFor(0)[0].page()).toBe(expectedPage);
  325. }
  326. it('should use initial NgTableParams constructor value', function(){
  327. var params = createNgTableParams({ page: 2}, null);
  328. scope.tableParams = params;
  329. scope.$digest();
  330. verifyPageWas(2);
  331. expect(params.settings().getData.calls.count()).toBe(1);
  332. });
  333. it('should use initial NgTableParams constructor value combined with filter', function(){
  334. var params = createNgTableParams({ page: 2, filter: { age: 5}}, null);
  335. scope.tableParams = params;
  336. scope.$digest();
  337. verifyPageWas(2);
  338. expect(params.settings().getData.calls.count()).toBe(1);
  339. });
  340. it('changing page # should trigger reload of data', function(){
  341. var params = createNgTableParams({ page: 3}, null);
  342. scope.tableParams = params;
  343. scope.$digest();
  344. verifyPageWas(3);
  345. params.settings().getData.calls.reset();
  346. scope.tableParams.page(5);
  347. scope.$digest();
  348. verifyPageWas(5);
  349. });
  350. });
  351. describe('filters', function(){
  352. var $capturedColumn;
  353. beforeEach(inject(function() {
  354. // stash a reference to $column definition so that its available in asserts
  355. scope.captureColumn = function ($column) {
  356. $capturedColumn = $column;
  357. };
  358. }));
  359. describe('filter specified as alias', function(){
  360. var elm,
  361. tp;
  362. beforeEach(inject(function($compile) {
  363. elm = angular.element(
  364. '<div>' +
  365. '<table ng-table="tableParams" show-filter="true">' +
  366. '<tr ng-repeat="user in $data">' +
  367. '<td header-class="captureColumn($column)" title="\'Name\'" ' +
  368. 'filter="usernameFilter">{{user.name}}</td>' +
  369. '</tr>' +
  370. '</table>' +
  371. '</div>');
  372. $compile(elm)(scope);
  373. scope.$digest();
  374. // 'text' is a shortcut alias for the template ng-table/filters/text
  375. scope.usernameFilter = {username: 'text'};
  376. tp = scope.tableParams = createNgTableParams({ filterDelay: 10 });
  377. scope.$digest();
  378. }));
  379. it('should render named filter template', function() {
  380. var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input');
  381. expect(inputs.length).toBe(1);
  382. expect(inputs.eq(0).attr('type')).toBe('text');
  383. expect(inputs.eq(0).attr('ng-model')).not.toBeUndefined();
  384. expect(inputs.eq(0).attr('name')).toBe('username');
  385. });
  386. it('should databind ngTableParams.filter to filter input', function () {
  387. scope.tableParams.filter()['username'] = 'my name is...';
  388. scope.$digest();
  389. var input = elm.find('thead').find('tr').eq(1).find('th').find('input');
  390. expect(input.val()).toBe('my name is...');
  391. });
  392. it('should make filter def available on $column', function () {
  393. expect($capturedColumn).toBeDefined();
  394. expect($capturedColumn.filter).toBeDefined();
  395. expect($capturedColumn.filter()['username']).toBe('text');
  396. });
  397. it('when filter changes should trigger reload of table', inject(function ($timeout) {
  398. tp.settings().getData.calls.reset();
  399. tp.filter()['username'] = 'new value';
  400. scope.$digest();
  401. $timeout.flush(); // trigger delayed filter
  402. tp.filter()['username'] = 'another value';
  403. scope.$digest();
  404. $timeout.flush(); // trigger delayed filter
  405. expect(tp.settings().getData.calls.count()).toBe(2);
  406. // same value - should not trigger reload
  407. tp.filter()['username'] = 'another value';
  408. scope.$digest();
  409. try{
  410. $timeout.flush(); // trigger delayed filter
  411. } catch (ex) {
  412. }
  413. expect(tp.settings().getData.calls.count()).toBe(2);
  414. }));
  415. it('when filter changes should reset page number to 1', inject(function ($timeout) {
  416. // trigger initial load of data so that subsequent changes to filter will trigger reset of page #
  417. tp.filter()['username'] = 'initial value';
  418. scope.$digest();
  419. $timeout.flush(); // trigger delayed filter
  420. // set page to something other than 1
  421. tp.page(5);
  422. expect(tp.page()).toBe(5); // checking assumptions
  423. // when
  424. tp.filter()['username'] = 'new value';
  425. scope.$digest();
  426. $timeout.flush(); // trigger delayed filter
  427. expect(tp.page()).toBe(1);
  428. }));
  429. });
  430. describe('filter specified with url', function(){
  431. var elm;
  432. beforeEach(inject(function($compile) {
  433. elm = angular.element(
  434. '<div>' +
  435. '<script type="text/ng-template" id="ng-table/filters/customNum.html"><input type="number" id="{{name}}"/></script>' +
  436. '<table ng-table="tableParams" show-filter="true">' +
  437. '<tr ng-repeat="user in $data">' +
  438. '<td header-class="captureColumn($column)" title="\'Age\'" ' +
  439. 'filter="{ \'age\': \'ng-table/filters/customNum.html\' }">{{user.age}}</td>' +
  440. '</tr>' +
  441. '</table>' +
  442. '</div>');
  443. $compile(elm)(scope);
  444. scope.tableParams = createNgTableParams();
  445. scope.$digest();
  446. }));
  447. it('should render filter template specified by url', function() {
  448. var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input');
  449. expect(inputs.length).toBe(1);
  450. expect(inputs.eq(0).attr('type')).toBe('number');
  451. expect(inputs.eq(0).attr('id')).toBe('age');
  452. });
  453. });
  454. describe('multiple filter inputs', function(){
  455. var elm;
  456. beforeEach(inject(function($compile) {
  457. elm = angular.element(
  458. '<div>' +
  459. '<table ng-table="tableParams" show-filter="true">' +
  460. '<tr ng-repeat="user in $data">' +
  461. '<td header-class="captureColumn($column)" title="\'Name\'" ' +
  462. 'filter="{ \'name\': \'text\', \'age\': \'text\' }">{{user.name}}</td>' +
  463. '</tr>' +
  464. '</table>' +
  465. '</div>');
  466. $compile(elm)(scope);
  467. scope.$digest();
  468. scope.tableParams = createNgTableParams();
  469. scope.$digest();
  470. }));
  471. it('should render filter template for each key/value pair ordered by key', function() {
  472. var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input');
  473. expect(inputs.length).toBe(2);
  474. expect(inputs.eq(0).attr('type')).toBe('text');
  475. expect(inputs.eq(0).attr('ng-model')).not.toBeUndefined();
  476. expect(inputs.eq(1).attr('type')).toBe('text');
  477. expect(inputs.eq(1).attr('ng-model')).not.toBeUndefined();
  478. });
  479. it('should databind ngTableParams.filter to filter inputs', function () {
  480. scope.tableParams.filter()['name'] = 'my name is...';
  481. scope.tableParams.filter()['age'] = '10';
  482. scope.$digest();
  483. var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input');
  484. expect(inputs.eq(0).val()).toBe('my name is...');
  485. expect(inputs.eq(1).val()).toBe('10');
  486. });
  487. it('should make filter def available on $column', function () {
  488. expect($capturedColumn).toBeDefined();
  489. expect($capturedColumn.filter).toBeDefined();
  490. expect($capturedColumn.filter()['name']).toBe('text');
  491. expect($capturedColumn.filter()['age']).toBe('text');
  492. });
  493. });
  494. describe('dynamic filter', function(){
  495. var elm, ageFilter;
  496. beforeEach(inject(function($compile) {
  497. ageFilter = {age: 'text'};
  498. elm = angular.element(
  499. '<div>' +
  500. '<script type="text/ng-template" id="ng-table/filters/number.html"><input type="number" name="{{name}}"/></script>' +
  501. '<table ng-table="tableParams" show-filter="true">' +
  502. '<tr ng-repeat="user in $data">' +
  503. '<td title="\'Name\'" filter="getFilter($column)">{{user.name}}</td>' +
  504. '<td title="\'Age\'" filter="getFilter($column)">{{user.age}}</td>' +
  505. '</tr>' +
  506. '</table>' +
  507. '</div>');
  508. $compile(elm)(scope);
  509. scope.$digest();
  510. scope.getFilter = function(colDef){
  511. if (colDef.id === 0) {
  512. return {username: 'text'};
  513. } else if (colDef.id === 1) {
  514. return ageFilter;
  515. }
  516. };
  517. scope.tableParams = createNgTableParams();
  518. scope.$digest();
  519. }));
  520. it('should render named filter template', function() {
  521. var usernameInput = elm.find('thead').find('tr').eq(1).find('th').eq(0).find('input');
  522. expect(usernameInput.attr('type')).toBe('text');
  523. expect(usernameInput.attr('name')).toBe('username');
  524. var ageInput = elm.find('thead').find('tr').eq(1).find('th').eq(1).find('input');
  525. expect(ageInput.attr('type')).toBe('text');
  526. expect(ageInput.attr('name')).toBe('age');
  527. });
  528. it('should databind ngTableParams.filter to filter input', function () {
  529. scope.tableParams.filter()['username'] = 'my name is...';
  530. scope.tableParams.filter()['age'] = '10';
  531. scope.$digest();
  532. var usernameInput = elm.find('thead').find('tr').eq(1).find('th').eq(0).find('input');
  533. expect(usernameInput.val()).toBe('my name is...');
  534. var ageInput = elm.find('thead').find('tr').eq(1).find('th').eq(1).find('input');
  535. expect(ageInput.val()).toBe('10');
  536. });
  537. it('should render new template as filter changes', function() {
  538. ageFilter.age = 'number';
  539. scope.$digest();
  540. var ageInput = elm.find('thead').find('tr').eq(1).find('th').eq(1).find('input');
  541. expect(ageInput.attr('type')).toBe('number');
  542. expect(ageInput.attr('name')).toBe('age');
  543. });
  544. });
  545. });
  546. describe('internals', function(){
  547. var elm,
  548. $timeout;
  549. beforeEach(inject(function($compile, _$timeout_) {
  550. $timeout = _$timeout_;
  551. elm = angular.element(
  552. '<table ng-table="tableParams">' +
  553. '<tr ng-repeat="user in $data">' +
  554. '<td title="\'Age\'">{{user.age}}</td>' +
  555. '</tr>' +
  556. '</table>');
  557. $compile(elm)(scope);
  558. scope.$digest();
  559. }));
  560. it('should reload when binding a new tableParams to scope', function(){
  561. var tp = createNgTableParams();
  562. scope.tableParams = tp;
  563. scope.$digest();
  564. expect(tp.settings().getData.calls.count()).toBe(1);
  565. });
  566. it('should reload 1 time when binding a new tableParams that has an initial settings data field', function(){
  567. var tp = createNgTableParams({ data: [1,2,3] });
  568. scope.tableParams = tp;
  569. scope.$digest();
  570. expect(tp.settings().getData.calls.count()).toBe(1);
  571. });
  572. it('should reload 1 time when binding a new tableParams with initial filter that has an initial settings data field', function(){
  573. var tp = createNgTableParams({filter: {age: 1}}, { data: [1,2,3] });
  574. scope.tableParams = tp;
  575. scope.$digest();
  576. expect(tp.settings().getData.calls.count()).toBe(1);
  577. });
  578. it('should reload when binding a new tableParams to scope multiple times', function(){
  579. var tp1 = createNgTableParams();
  580. scope.tableParams = tp1;
  581. scope.$digest();
  582. expect(tp1.settings().getData.calls.count()).toBe(1);
  583. var tp2 = createNgTableParams();
  584. scope.tableParams = tp2;
  585. scope.$digest();
  586. expect(tp2.settings().getData.calls.count()).toBe(1);
  587. });
  588. it('should reload 1 time when binding a new settings data value and changing the filter', function(){
  589. // given
  590. var tp = createNgTableParams({filterDelay: 100, data: [{age: 1}, {age: 2}]});
  591. scope.tableParams = tp;
  592. scope.$digest();
  593. tp.settings().getData.calls.reset();
  594. // when
  595. tp.filter({ age: 1 });
  596. tp.settings({ data: [{ age: 1 }, { age: 11 }, { age: 22 }]});
  597. scope.$digest();
  598. $timeout.flush(); // trigger the delayed reload
  599. expect(tp.settings().getData.calls.count()).toBe(1);
  600. });
  601. it('should reload 1 time when multiple filter changes are debounced', function(){
  602. // given
  603. var tp = createNgTableParams({filterDelay: 100, data: [{age: 1}, {age: 2}]});
  604. scope.tableParams = tp;
  605. scope.$digest();
  606. tp.settings().getData.calls.reset();
  607. // when
  608. tp.filter({ age: 1 });
  609. scope.$digest();
  610. tp.filter({ age: 2 });
  611. scope.$digest();
  612. $timeout.flush(); // trigger the delayed reload
  613. expect(tp.settings().getData.calls.count()).toBe(1);
  614. });
  615. it('should reload 1 time with page reset to 1 when binding a new settings data value and changing the filter', function(){
  616. var settings = {
  617. counts: [1],
  618. data: [{age: 1}, {age: 2}],
  619. filterDelay: 100
  620. };
  621. var tp = createNgTableParams({ count: 1, page: 2 }, settings);
  622. scope.tableParams = tp;
  623. scope.$digest();
  624. expect(tp.page()).toBe(2); // checking assumptions
  625. tp.settings().getData.calls.reset();
  626. // when
  627. tp.filter({ age: 1 });
  628. tp.settings({ data: [{ age: 1 }, { age: 11 }, { age: 22 }]});
  629. scope.$digest();
  630. $timeout.flush(); // trigger the delayed reload
  631. expect(tp.settings().getData.calls.count()).toBe(1);
  632. expect(tp.page()).toBe(1);
  633. });
  634. it('changing filter, orderBy, or page and then calling reload should not invoke getData twice', function(){
  635. // todo: refactor the watches in ngTableController to handle this case
  636. var tp = createNgTableParams();
  637. scope.tableParams = tp;
  638. scope.$digest();
  639. tp.settings().getData.calls.reset();
  640. // when
  641. tp.filter({ age: 5 });
  642. tp.reload();
  643. scope.$digest();
  644. // then
  645. expect(tp.settings().getData.calls.count()).toBe(1);
  646. });
  647. it('changing filter, orderBy, or page in a callback to reload should re-invoke getData 1 time only', function(){
  648. var tp = createNgTableParams();
  649. scope.tableParams = tp;
  650. scope.$digest();
  651. tp.settings().getData.calls.reset();
  652. // when
  653. tp.filter({ age: 5 });
  654. tp.reload().then(function(){
  655. tp.sorting({ age: 'desc'});
  656. // note: better to call tp.reload() here rather than rely on a watch firing later to do it for us
  657. // that way the second reload is chained to the first and returned as a single promise
  658. });
  659. scope.$digest();
  660. // then
  661. // ie calls.count() === (1 x reload) + (1 x sorting)
  662. expect(tp.settings().getData.calls.count()).toBe(2);
  663. });
  664. it('changing filter, orderBy, or page then reload in a callback to reload should re-invoke getData 1 time only', function(){
  665. // todo: refactor the watches in ngTableController to handle this case
  666. var tp = createNgTableParams();
  667. scope.tableParams = tp;
  668. scope.$digest();
  669. tp.settings().getData.calls.reset();
  670. // when
  671. tp.filter({ age: 5 });
  672. tp.reload().then(function(){
  673. tp.sorting({ age: 'desc'});
  674. return tp.reload();
  675. });
  676. scope.$digest();
  677. // then
  678. // ie calls.count() === (1 x reload) + (1 x sorting)
  679. expect(tp.settings().getData.calls.count()).toBe(2);
  680. });
  681. it('should not reload when filter value is assigned the same value', function(){
  682. // given
  683. var tp = createNgTableParams({ filter: {age: 10} }, { filterDelay: 0 });
  684. scope.tableParams = tp;
  685. scope.$digest();
  686. tp.settings().getData.calls.reset();
  687. // when
  688. tp.filter({ age: 10});
  689. scope.$digest();
  690. expect(tp.settings().getData.calls.count()).toBe(0);
  691. });
  692. it('should reload when filter value changes', function(){
  693. // given
  694. var tp = createNgTableParams({ filter: {age: 10} }, { filterDelay: 0 });
  695. scope.tableParams = tp;
  696. scope.$digest();
  697. tp.settings().getData.calls.reset();
  698. // when
  699. tp.filter({ age: 12});
  700. scope.$digest();
  701. expect(tp.settings().getData.calls.count()).toBe(1);
  702. });
  703. it('should reload when new dataset supplied', function(){
  704. // given
  705. var initialDataset = [
  706. {age: 1},
  707. {age: 2}
  708. ];
  709. var tp = createNgTableParams();
  710. scope.tableParams = tp;
  711. scope.$digest();
  712. tp.settings().getData.calls.reset();
  713. // when
  714. tp.settings({ data: [{ age: 10}, { age: 11}, { age: 12}]});
  715. scope.$digest();
  716. expect(tp.settings().getData.calls.count()).toBe(1);
  717. });
  718. });
  719. });