table.js 84 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { coerceBooleanProperty } from '@angular/cdk/coercion';
  9. import { ContentChild, Directive, ElementRef, Input, TemplateRef, ChangeDetectionStrategy, Component, IterableDiffers, ViewContainerRef, ViewEncapsulation, Attribute, ChangeDetectorRef, ContentChildren, Inject, isDevMode, Optional, ViewChild, InjectionToken, NgModule } from '@angular/core';
  10. import { Directionality } from '@angular/cdk/bidi';
  11. import { isDataSource } from '@angular/cdk/collections';
  12. export { DataSource } from '@angular/cdk/collections';
  13. import { Platform } from '@angular/cdk/platform';
  14. import { DOCUMENT, CommonModule } from '@angular/common';
  15. import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
  16. import { takeUntil } from 'rxjs/operators';
  17. /**
  18. * @fileoverview added by tsickle
  19. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  20. */
  21. /**
  22. * Mixin to provide a directive with a function that checks if the sticky input has been
  23. * changed since the last time the function was called. Essentially adds a dirty-check to the
  24. * sticky value.
  25. * \@docs-private
  26. * @template T
  27. * @param {?} base
  28. * @return {?}
  29. */
  30. function mixinHasStickyInput(base) {
  31. return class extends base {
  32. /**
  33. * @param {...?} args
  34. */
  35. constructor(...args) {
  36. super(...args);
  37. this._sticky = false;
  38. /**
  39. * Whether the sticky input has changed since it was last checked.
  40. */
  41. this._hasStickyChanged = false;
  42. }
  43. /**
  44. * Whether sticky positioning should be applied.
  45. * @return {?}
  46. */
  47. get sticky() { return this._sticky; }
  48. /**
  49. * @param {?} v
  50. * @return {?}
  51. */
  52. set sticky(v) {
  53. /** @type {?} */
  54. const prevValue = this._sticky;
  55. this._sticky = coerceBooleanProperty(v);
  56. this._hasStickyChanged = prevValue !== this._sticky;
  57. }
  58. /**
  59. * Whether the sticky value has changed since this was last called.
  60. * @return {?}
  61. */
  62. hasStickyChanged() {
  63. /** @type {?} */
  64. const hasStickyChanged = this._hasStickyChanged;
  65. this._hasStickyChanged = false;
  66. return hasStickyChanged;
  67. }
  68. /**
  69. * Resets the dirty check for cases where the sticky state has been used without checking.
  70. * @return {?}
  71. */
  72. resetStickyChanged() {
  73. this._hasStickyChanged = false;
  74. }
  75. };
  76. }
  77. /**
  78. * @fileoverview added by tsickle
  79. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  80. */
  81. /**
  82. * Cell definition for a CDK table.
  83. * Captures the template of a column's data row cell as well as cell-specific properties.
  84. */
  85. class CdkCellDef {
  86. /**
  87. * @param {?} template
  88. */
  89. constructor(/** @docs-private */ template) {
  90. this.template = template;
  91. }
  92. }
  93. CdkCellDef.decorators = [
  94. { type: Directive, args: [{ selector: '[cdkCellDef]' },] },
  95. ];
  96. /** @nocollapse */
  97. CdkCellDef.ctorParameters = () => [
  98. { type: TemplateRef }
  99. ];
  100. /**
  101. * Header cell definition for a CDK table.
  102. * Captures the template of a column's header cell and as well as cell-specific properties.
  103. */
  104. class CdkHeaderCellDef {
  105. /**
  106. * @param {?} template
  107. */
  108. constructor(/** @docs-private */ template) {
  109. this.template = template;
  110. }
  111. }
  112. CdkHeaderCellDef.decorators = [
  113. { type: Directive, args: [{ selector: '[cdkHeaderCellDef]' },] },
  114. ];
  115. /** @nocollapse */
  116. CdkHeaderCellDef.ctorParameters = () => [
  117. { type: TemplateRef }
  118. ];
  119. /**
  120. * Footer cell definition for a CDK table.
  121. * Captures the template of a column's footer cell and as well as cell-specific properties.
  122. */
  123. class CdkFooterCellDef {
  124. /**
  125. * @param {?} template
  126. */
  127. constructor(/** @docs-private */ template) {
  128. this.template = template;
  129. }
  130. }
  131. CdkFooterCellDef.decorators = [
  132. { type: Directive, args: [{ selector: '[cdkFooterCellDef]' },] },
  133. ];
  134. /** @nocollapse */
  135. CdkFooterCellDef.ctorParameters = () => [
  136. { type: TemplateRef }
  137. ];
  138. // Boilerplate for applying mixins to CdkColumnDef.
  139. /**
  140. * \@docs-private
  141. */
  142. class CdkColumnDefBase {
  143. }
  144. /** @type {?} */
  145. const _CdkColumnDefBase = mixinHasStickyInput(CdkColumnDefBase);
  146. /**
  147. * Column definition for the CDK table.
  148. * Defines a set of cells available for a table column.
  149. */
  150. class CdkColumnDef extends _CdkColumnDefBase {
  151. constructor() {
  152. super(...arguments);
  153. this._stickyEnd = false;
  154. }
  155. /**
  156. * Unique name for this column.
  157. * @return {?}
  158. */
  159. get name() {
  160. return this._name;
  161. }
  162. /**
  163. * @param {?} name
  164. * @return {?}
  165. */
  166. set name(name) {
  167. // If the directive is set without a name (updated programatically), then this setter will
  168. // trigger with an empty string and should not overwrite the programatically set value.
  169. if (!name) {
  170. return;
  171. }
  172. this._name = name;
  173. this.cssClassFriendlyName = name.replace(/[^a-z0-9_-]/ig, '-');
  174. }
  175. /**
  176. * Whether this column should be sticky positioned on the end of the row. Should make sure
  177. * that it mimics the `CanStick` mixin such that `_hasStickyChanged` is set to true if the value
  178. * has been changed.
  179. * @return {?}
  180. */
  181. get stickyEnd() {
  182. return this._stickyEnd;
  183. }
  184. /**
  185. * @param {?} v
  186. * @return {?}
  187. */
  188. set stickyEnd(v) {
  189. /** @type {?} */
  190. const prevValue = this._stickyEnd;
  191. this._stickyEnd = coerceBooleanProperty(v);
  192. this._hasStickyChanged = prevValue !== this._stickyEnd;
  193. }
  194. }
  195. CdkColumnDef.decorators = [
  196. { type: Directive, args: [{
  197. selector: '[cdkColumnDef]',
  198. inputs: ['sticky'],
  199. providers: [{ provide: 'MAT_SORT_HEADER_COLUMN_DEF', useExisting: CdkColumnDef }],
  200. },] },
  201. ];
  202. CdkColumnDef.propDecorators = {
  203. name: [{ type: Input, args: ['cdkColumnDef',] }],
  204. stickyEnd: [{ type: Input, args: ['stickyEnd',] }],
  205. cell: [{ type: ContentChild, args: [CdkCellDef, { static: false },] }],
  206. headerCell: [{ type: ContentChild, args: [CdkHeaderCellDef, { static: false },] }],
  207. footerCell: [{ type: ContentChild, args: [CdkFooterCellDef, { static: false },] }]
  208. };
  209. /**
  210. * Base class for the cells. Adds a CSS classname that identifies the column it renders in.
  211. */
  212. class BaseCdkCell {
  213. /**
  214. * @param {?} columnDef
  215. * @param {?} elementRef
  216. */
  217. constructor(columnDef, elementRef) {
  218. /** @type {?} */
  219. const columnClassName = `cdk-column-${columnDef.cssClassFriendlyName}`;
  220. elementRef.nativeElement.classList.add(columnClassName);
  221. }
  222. }
  223. /**
  224. * Header cell template container that adds the right classes and role.
  225. */
  226. class CdkHeaderCell extends BaseCdkCell {
  227. /**
  228. * @param {?} columnDef
  229. * @param {?} elementRef
  230. */
  231. constructor(columnDef, elementRef) {
  232. super(columnDef, elementRef);
  233. }
  234. }
  235. CdkHeaderCell.decorators = [
  236. { type: Directive, args: [{
  237. selector: 'cdk-header-cell, th[cdk-header-cell]',
  238. host: {
  239. 'class': 'cdk-header-cell',
  240. 'role': 'columnheader',
  241. },
  242. },] },
  243. ];
  244. /** @nocollapse */
  245. CdkHeaderCell.ctorParameters = () => [
  246. { type: CdkColumnDef },
  247. { type: ElementRef }
  248. ];
  249. /**
  250. * Footer cell template container that adds the right classes and role.
  251. */
  252. class CdkFooterCell extends BaseCdkCell {
  253. /**
  254. * @param {?} columnDef
  255. * @param {?} elementRef
  256. */
  257. constructor(columnDef, elementRef) {
  258. super(columnDef, elementRef);
  259. }
  260. }
  261. CdkFooterCell.decorators = [
  262. { type: Directive, args: [{
  263. selector: 'cdk-footer-cell, td[cdk-footer-cell]',
  264. host: {
  265. 'class': 'cdk-footer-cell',
  266. 'role': 'gridcell',
  267. },
  268. },] },
  269. ];
  270. /** @nocollapse */
  271. CdkFooterCell.ctorParameters = () => [
  272. { type: CdkColumnDef },
  273. { type: ElementRef }
  274. ];
  275. /**
  276. * Cell template container that adds the right classes and role.
  277. */
  278. class CdkCell extends BaseCdkCell {
  279. /**
  280. * @param {?} columnDef
  281. * @param {?} elementRef
  282. */
  283. constructor(columnDef, elementRef) {
  284. super(columnDef, elementRef);
  285. }
  286. }
  287. CdkCell.decorators = [
  288. { type: Directive, args: [{
  289. selector: 'cdk-cell, td[cdk-cell]',
  290. host: {
  291. 'class': 'cdk-cell',
  292. 'role': 'gridcell',
  293. },
  294. },] },
  295. ];
  296. /** @nocollapse */
  297. CdkCell.ctorParameters = () => [
  298. { type: CdkColumnDef },
  299. { type: ElementRef }
  300. ];
  301. /**
  302. * @fileoverview added by tsickle
  303. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  304. */
  305. /**
  306. * The row template that can be used by the mat-table. Should not be used outside of the
  307. * material library.
  308. * @type {?}
  309. */
  310. const CDK_ROW_TEMPLATE = `<ng-container cdkCellOutlet></ng-container>`;
  311. /**
  312. * Base class for the CdkHeaderRowDef and CdkRowDef that handles checking their columns inputs
  313. * for changes and notifying the table.
  314. * @abstract
  315. */
  316. class BaseRowDef {
  317. /**
  318. * @param {?} template
  319. * @param {?} _differs
  320. */
  321. constructor(template, _differs) {
  322. this.template = template;
  323. this._differs = _differs;
  324. }
  325. /**
  326. * @param {?} changes
  327. * @return {?}
  328. */
  329. ngOnChanges(changes) {
  330. // Create a new columns differ if one does not yet exist. Initialize it based on initial value
  331. // of the columns property or an empty array if none is provided.
  332. if (!this._columnsDiffer) {
  333. /** @type {?} */
  334. const columns = (changes['columns'] && changes['columns'].currentValue) || [];
  335. this._columnsDiffer = this._differs.find(columns).create();
  336. this._columnsDiffer.diff(columns);
  337. }
  338. }
  339. /**
  340. * Returns the difference between the current columns and the columns from the last diff, or null
  341. * if there is no difference.
  342. * @return {?}
  343. */
  344. getColumnsDiff() {
  345. return this._columnsDiffer.diff(this.columns);
  346. }
  347. /**
  348. * Gets this row def's relevant cell template from the provided column def.
  349. * @param {?} column
  350. * @return {?}
  351. */
  352. extractCellTemplate(column) {
  353. if (this instanceof CdkHeaderRowDef) {
  354. return column.headerCell.template;
  355. }
  356. if (this instanceof CdkFooterRowDef) {
  357. return column.footerCell.template;
  358. }
  359. else {
  360. return column.cell.template;
  361. }
  362. }
  363. }
  364. // Boilerplate for applying mixins to CdkHeaderRowDef.
  365. /**
  366. * \@docs-private
  367. */
  368. class CdkHeaderRowDefBase extends BaseRowDef {
  369. }
  370. /** @type {?} */
  371. const _CdkHeaderRowDefBase = mixinHasStickyInput(CdkHeaderRowDefBase);
  372. /**
  373. * Header row definition for the CDK table.
  374. * Captures the header row's template and other header properties such as the columns to display.
  375. */
  376. class CdkHeaderRowDef extends _CdkHeaderRowDefBase {
  377. /**
  378. * @param {?} template
  379. * @param {?} _differs
  380. */
  381. constructor(template, _differs) {
  382. super(template, _differs);
  383. }
  384. // Prerender fails to recognize that ngOnChanges in a part of this class through inheritance.
  385. // Explicitly define it so that the method is called as part of the Angular lifecycle.
  386. /**
  387. * @param {?} changes
  388. * @return {?}
  389. */
  390. ngOnChanges(changes) {
  391. super.ngOnChanges(changes);
  392. }
  393. }
  394. CdkHeaderRowDef.decorators = [
  395. { type: Directive, args: [{
  396. selector: '[cdkHeaderRowDef]',
  397. inputs: ['columns: cdkHeaderRowDef', 'sticky: cdkHeaderRowDefSticky'],
  398. },] },
  399. ];
  400. /** @nocollapse */
  401. CdkHeaderRowDef.ctorParameters = () => [
  402. { type: TemplateRef },
  403. { type: IterableDiffers }
  404. ];
  405. // Boilerplate for applying mixins to CdkFooterRowDef.
  406. /**
  407. * \@docs-private
  408. */
  409. class CdkFooterRowDefBase extends BaseRowDef {
  410. }
  411. /** @type {?} */
  412. const _CdkFooterRowDefBase = mixinHasStickyInput(CdkFooterRowDefBase);
  413. /**
  414. * Footer row definition for the CDK table.
  415. * Captures the footer row's template and other footer properties such as the columns to display.
  416. */
  417. class CdkFooterRowDef extends _CdkFooterRowDefBase {
  418. /**
  419. * @param {?} template
  420. * @param {?} _differs
  421. */
  422. constructor(template, _differs) {
  423. super(template, _differs);
  424. }
  425. // Prerender fails to recognize that ngOnChanges in a part of this class through inheritance.
  426. // Explicitly define it so that the method is called as part of the Angular lifecycle.
  427. /**
  428. * @param {?} changes
  429. * @return {?}
  430. */
  431. ngOnChanges(changes) {
  432. super.ngOnChanges(changes);
  433. }
  434. }
  435. CdkFooterRowDef.decorators = [
  436. { type: Directive, args: [{
  437. selector: '[cdkFooterRowDef]',
  438. inputs: ['columns: cdkFooterRowDef', 'sticky: cdkFooterRowDefSticky'],
  439. },] },
  440. ];
  441. /** @nocollapse */
  442. CdkFooterRowDef.ctorParameters = () => [
  443. { type: TemplateRef },
  444. { type: IterableDiffers }
  445. ];
  446. /**
  447. * Data row definition for the CDK table.
  448. * Captures the header row's template and other row properties such as the columns to display and
  449. * a when predicate that describes when this row should be used.
  450. * @template T
  451. */
  452. class CdkRowDef extends BaseRowDef {
  453. // TODO(andrewseguin): Add an input for providing a switch function to determine
  454. // if this template should be used.
  455. /**
  456. * @param {?} template
  457. * @param {?} _differs
  458. */
  459. constructor(template, _differs) {
  460. super(template, _differs);
  461. }
  462. }
  463. CdkRowDef.decorators = [
  464. { type: Directive, args: [{
  465. selector: '[cdkRowDef]',
  466. inputs: ['columns: cdkRowDefColumns', 'when: cdkRowDefWhen'],
  467. },] },
  468. ];
  469. /** @nocollapse */
  470. CdkRowDef.ctorParameters = () => [
  471. { type: TemplateRef },
  472. { type: IterableDiffers }
  473. ];
  474. /**
  475. * Outlet for rendering cells inside of a row or header row.
  476. * \@docs-private
  477. */
  478. class CdkCellOutlet {
  479. /**
  480. * @param {?} _viewContainer
  481. */
  482. constructor(_viewContainer) {
  483. this._viewContainer = _viewContainer;
  484. CdkCellOutlet.mostRecentCellOutlet = this;
  485. }
  486. /**
  487. * @return {?}
  488. */
  489. ngOnDestroy() {
  490. // If this was the last outlet being rendered in the view, remove the reference
  491. // from the static property after it has been destroyed to avoid leaking memory.
  492. if (CdkCellOutlet.mostRecentCellOutlet === this) {
  493. CdkCellOutlet.mostRecentCellOutlet = null;
  494. }
  495. }
  496. }
  497. /**
  498. * Static property containing the latest constructed instance of this class.
  499. * Used by the CDK table when each CdkHeaderRow and CdkRow component is created using
  500. * createEmbeddedView. After one of these components are created, this property will provide
  501. * a handle to provide that component's cells and context. After init, the CdkCellOutlet will
  502. * construct the cells with the provided context.
  503. */
  504. CdkCellOutlet.mostRecentCellOutlet = null;
  505. CdkCellOutlet.decorators = [
  506. { type: Directive, args: [{ selector: '[cdkCellOutlet]' },] },
  507. ];
  508. /** @nocollapse */
  509. CdkCellOutlet.ctorParameters = () => [
  510. { type: ViewContainerRef }
  511. ];
  512. /**
  513. * Header template container that contains the cell outlet. Adds the right class and role.
  514. */
  515. class CdkHeaderRow {
  516. }
  517. CdkHeaderRow.decorators = [
  518. { type: Component, args: [{selector: 'cdk-header-row, tr[cdk-header-row]',
  519. template: CDK_ROW_TEMPLATE,
  520. host: {
  521. 'class': 'cdk-header-row',
  522. 'role': 'row',
  523. },
  524. // See note on CdkTable for explanation on why this uses the default change detection strategy.
  525. // tslint:disable-next-line:validate-decorators
  526. changeDetection: ChangeDetectionStrategy.Default,
  527. encapsulation: ViewEncapsulation.None,
  528. },] },
  529. ];
  530. /**
  531. * Footer template container that contains the cell outlet. Adds the right class and role.
  532. */
  533. class CdkFooterRow {
  534. }
  535. CdkFooterRow.decorators = [
  536. { type: Component, args: [{selector: 'cdk-footer-row, tr[cdk-footer-row]',
  537. template: CDK_ROW_TEMPLATE,
  538. host: {
  539. 'class': 'cdk-footer-row',
  540. 'role': 'row',
  541. },
  542. // See note on CdkTable for explanation on why this uses the default change detection strategy.
  543. // tslint:disable-next-line:validate-decorators
  544. changeDetection: ChangeDetectionStrategy.Default,
  545. encapsulation: ViewEncapsulation.None,
  546. },] },
  547. ];
  548. /**
  549. * Data row template container that contains the cell outlet. Adds the right class and role.
  550. */
  551. class CdkRow {
  552. }
  553. CdkRow.decorators = [
  554. { type: Component, args: [{selector: 'cdk-row, tr[cdk-row]',
  555. template: CDK_ROW_TEMPLATE,
  556. host: {
  557. 'class': 'cdk-row',
  558. 'role': 'row',
  559. },
  560. // See note on CdkTable for explanation on why this uses the default change detection strategy.
  561. // tslint:disable-next-line:validate-decorators
  562. changeDetection: ChangeDetectionStrategy.Default,
  563. encapsulation: ViewEncapsulation.None,
  564. },] },
  565. ];
  566. /**
  567. * @fileoverview added by tsickle
  568. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  569. */
  570. /**
  571. * List of all possible directions that can be used for sticky positioning.
  572. * \@docs-private
  573. * @type {?}
  574. */
  575. const STICKY_DIRECTIONS = ['top', 'bottom', 'left', 'right'];
  576. /**
  577. * Applies and removes sticky positioning styles to the `CdkTable` rows and columns cells.
  578. * \@docs-private
  579. */
  580. class StickyStyler {
  581. /**
  582. * @param {?} _isNativeHtmlTable Whether the sticky logic should be based on a table
  583. * that uses the native `<table>` element.
  584. * @param {?} _stickCellCss The CSS class that will be applied to every row/cell that has
  585. * sticky positioning applied.
  586. * @param {?} direction The directionality context of the table (ltr/rtl); affects column positioning
  587. * by reversing left/right positions.
  588. * @param {?=} _isBrowser Whether the table is currently being rendered on the server or the client.
  589. */
  590. constructor(_isNativeHtmlTable, _stickCellCss, direction, _isBrowser = true) {
  591. this._isNativeHtmlTable = _isNativeHtmlTable;
  592. this._stickCellCss = _stickCellCss;
  593. this.direction = direction;
  594. this._isBrowser = _isBrowser;
  595. }
  596. /**
  597. * Clears the sticky positioning styles from the row and its cells by resetting the `position`
  598. * style, setting the zIndex to 0, and unsetting each provided sticky direction.
  599. * @param {?} rows The list of rows that should be cleared from sticking in the provided directions
  600. * @param {?} stickyDirections The directions that should no longer be set as sticky on the rows.
  601. * @return {?}
  602. */
  603. clearStickyPositioning(rows, stickyDirections) {
  604. for (const row of rows) {
  605. // If the row isn't an element (e.g. if it's an `ng-container`),
  606. // it won't have inline styles or `children` so we skip it.
  607. if (row.nodeType !== row.ELEMENT_NODE) {
  608. continue;
  609. }
  610. this._removeStickyStyle(row, stickyDirections);
  611. for (let i = 0; i < row.children.length; i++) {
  612. /** @type {?} */
  613. const cell = (/** @type {?} */ (row.children[i]));
  614. this._removeStickyStyle(cell, stickyDirections);
  615. }
  616. }
  617. }
  618. /**
  619. * Applies sticky left and right positions to the cells of each row according to the sticky
  620. * states of the rendered column definitions.
  621. * @param {?} rows The rows that should have its set of cells stuck according to the sticky states.
  622. * @param {?} stickyStartStates A list of boolean states where each state represents whether the cell
  623. * in this index position should be stuck to the start of the row.
  624. * @param {?} stickyEndStates A list of boolean states where each state represents whether the cell
  625. * in this index position should be stuck to the end of the row.
  626. * @return {?}
  627. */
  628. updateStickyColumns(rows, stickyStartStates, stickyEndStates) {
  629. /** @type {?} */
  630. const hasStickyColumns = stickyStartStates.some((/**
  631. * @param {?} state
  632. * @return {?}
  633. */
  634. state => state)) || stickyEndStates.some((/**
  635. * @param {?} state
  636. * @return {?}
  637. */
  638. state => state));
  639. if (!rows.length || !hasStickyColumns || !this._isBrowser) {
  640. return;
  641. }
  642. /** @type {?} */
  643. const firstRow = rows[0];
  644. /** @type {?} */
  645. const numCells = firstRow.children.length;
  646. /** @type {?} */
  647. const cellWidths = this._getCellWidths(firstRow);
  648. /** @type {?} */
  649. const startPositions = this._getStickyStartColumnPositions(cellWidths, stickyStartStates);
  650. /** @type {?} */
  651. const endPositions = this._getStickyEndColumnPositions(cellWidths, stickyEndStates);
  652. /** @type {?} */
  653. const isRtl = this.direction === 'rtl';
  654. for (const row of rows) {
  655. for (let i = 0; i < numCells; i++) {
  656. /** @type {?} */
  657. const cell = (/** @type {?} */ (row.children[i]));
  658. if (stickyStartStates[i]) {
  659. this._addStickyStyle(cell, isRtl ? 'right' : 'left', startPositions[i]);
  660. }
  661. if (stickyEndStates[i]) {
  662. this._addStickyStyle(cell, isRtl ? 'left' : 'right', endPositions[i]);
  663. }
  664. }
  665. }
  666. }
  667. /**
  668. * Applies sticky positioning to the row's cells if using the native table layout, and to the
  669. * row itself otherwise.
  670. * @param {?} rowsToStick The list of rows that should be stuck according to their corresponding
  671. * sticky state and to the provided top or bottom position.
  672. * @param {?} stickyStates A list of boolean states where each state represents whether the row
  673. * should be stuck in the particular top or bottom position.
  674. * @param {?} position The position direction in which the row should be stuck if that row should be
  675. * sticky.
  676. *
  677. * @return {?}
  678. */
  679. stickRows(rowsToStick, stickyStates, position) {
  680. // Since we can't measure the rows on the server, we can't stick the rows properly.
  681. if (!this._isBrowser) {
  682. return;
  683. }
  684. // If positioning the rows to the bottom, reverse their order when evaluating the sticky
  685. // position such that the last row stuck will be "bottom: 0px" and so on.
  686. /** @type {?} */
  687. const rows = position === 'bottom' ? rowsToStick.reverse() : rowsToStick;
  688. /** @type {?} */
  689. let stickyHeight = 0;
  690. for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
  691. if (!stickyStates[rowIndex]) {
  692. continue;
  693. }
  694. /** @type {?} */
  695. const row = rows[rowIndex];
  696. if (this._isNativeHtmlTable) {
  697. for (let j = 0; j < row.children.length; j++) {
  698. /** @type {?} */
  699. const cell = (/** @type {?} */ (row.children[j]));
  700. this._addStickyStyle(cell, position, stickyHeight);
  701. }
  702. }
  703. else {
  704. // Flex does not respect the stick positioning on the cells, needs to be applied to the row.
  705. // If this is applied on a native table, Safari causes the header to fly in wrong direction.
  706. this._addStickyStyle(row, position, stickyHeight);
  707. }
  708. if (rowIndex === rows.length - 1) {
  709. // prevent unnecessary reflow from getBoundingClientRect()
  710. return;
  711. }
  712. stickyHeight += row.getBoundingClientRect().height;
  713. }
  714. }
  715. /**
  716. * When using the native table in Safari, sticky footer cells do not stick. The only way to stick
  717. * footer rows is to apply sticky styling to the tfoot container. This should only be done if
  718. * all footer rows are sticky. If not all footer rows are sticky, remove sticky positioning from
  719. * the tfoot element.
  720. * @param {?} tableElement
  721. * @param {?} stickyStates
  722. * @return {?}
  723. */
  724. updateStickyFooterContainer(tableElement, stickyStates) {
  725. if (!this._isNativeHtmlTable) {
  726. return;
  727. }
  728. /** @type {?} */
  729. const tfoot = (/** @type {?} */ (tableElement.querySelector('tfoot')));
  730. if (stickyStates.some((/**
  731. * @param {?} state
  732. * @return {?}
  733. */
  734. state => !state))) {
  735. this._removeStickyStyle(tfoot, ['bottom']);
  736. }
  737. else {
  738. this._addStickyStyle(tfoot, 'bottom', 0);
  739. }
  740. }
  741. /**
  742. * Removes the sticky style on the element by removing the sticky cell CSS class, re-evaluating
  743. * the zIndex, removing each of the provided sticky directions, and removing the
  744. * sticky position if there are no more directions.
  745. * @param {?} element
  746. * @param {?} stickyDirections
  747. * @return {?}
  748. */
  749. _removeStickyStyle(element, stickyDirections) {
  750. for (const dir of stickyDirections) {
  751. element.style[dir] = '';
  752. }
  753. element.style.zIndex = this._getCalculatedZIndex(element);
  754. // If the element no longer has any more sticky directions, remove sticky positioning and
  755. // the sticky CSS class.
  756. /** @type {?} */
  757. const hasDirection = STICKY_DIRECTIONS.some((/**
  758. * @param {?} dir
  759. * @return {?}
  760. */
  761. dir => !!element.style[dir]));
  762. if (!hasDirection) {
  763. element.style.position = '';
  764. element.classList.remove(this._stickCellCss);
  765. }
  766. }
  767. /**
  768. * Adds the sticky styling to the element by adding the sticky style class, changing position
  769. * to be sticky (and -webkit-sticky), setting the appropriate zIndex, and adding a sticky
  770. * direction and value.
  771. * @param {?} element
  772. * @param {?} dir
  773. * @param {?} dirValue
  774. * @return {?}
  775. */
  776. _addStickyStyle(element, dir, dirValue) {
  777. element.classList.add(this._stickCellCss);
  778. element.style[dir] = `${dirValue}px`;
  779. element.style.cssText += 'position: -webkit-sticky; position: sticky; ';
  780. element.style.zIndex = this._getCalculatedZIndex(element);
  781. }
  782. /**
  783. * Calculate what the z-index should be for the element, depending on what directions (top,
  784. * bottom, left, right) have been set. It should be true that elements with a top direction
  785. * should have the highest index since these are elements like a table header. If any of those
  786. * elements are also sticky in another direction, then they should appear above other elements
  787. * that are only sticky top (e.g. a sticky column on a sticky header). Bottom-sticky elements
  788. * (e.g. footer rows) should then be next in the ordering such that they are below the header
  789. * but above any non-sticky elements. Finally, left/right sticky elements (e.g. sticky columns)
  790. * should minimally increment so that they are above non-sticky elements but below top and bottom
  791. * elements.
  792. * @param {?} element
  793. * @return {?}
  794. */
  795. _getCalculatedZIndex(element) {
  796. /** @type {?} */
  797. const zIndexIncrements = {
  798. top: 100,
  799. bottom: 10,
  800. left: 1,
  801. right: 1,
  802. };
  803. /** @type {?} */
  804. let zIndex = 0;
  805. for (const dir of STICKY_DIRECTIONS) {
  806. if (element.style[dir]) {
  807. zIndex += zIndexIncrements[dir];
  808. }
  809. }
  810. return zIndex ? `${zIndex}` : '';
  811. }
  812. /**
  813. * Gets the widths for each cell in the provided row.
  814. * @param {?} row
  815. * @return {?}
  816. */
  817. _getCellWidths(row) {
  818. /** @type {?} */
  819. const cellWidths = [];
  820. /** @type {?} */
  821. const firstRowCells = row.children;
  822. for (let i = 0; i < firstRowCells.length; i++) {
  823. /** @type {?} */
  824. let cell = (/** @type {?} */ (firstRowCells[i]));
  825. cellWidths.push(cell.getBoundingClientRect().width);
  826. }
  827. return cellWidths;
  828. }
  829. /**
  830. * Determines the left and right positions of each sticky column cell, which will be the
  831. * accumulation of all sticky column cell widths to the left and right, respectively.
  832. * Non-sticky cells do not need to have a value set since their positions will not be applied.
  833. * @param {?} widths
  834. * @param {?} stickyStates
  835. * @return {?}
  836. */
  837. _getStickyStartColumnPositions(widths, stickyStates) {
  838. /** @type {?} */
  839. const positions = [];
  840. /** @type {?} */
  841. let nextPosition = 0;
  842. for (let i = 0; i < widths.length; i++) {
  843. if (stickyStates[i]) {
  844. positions[i] = nextPosition;
  845. nextPosition += widths[i];
  846. }
  847. }
  848. return positions;
  849. }
  850. /**
  851. * Determines the left and right positions of each sticky column cell, which will be the
  852. * accumulation of all sticky column cell widths to the left and right, respectively.
  853. * Non-sticky cells do not need to have a value set since their positions will not be applied.
  854. * @param {?} widths
  855. * @param {?} stickyStates
  856. * @return {?}
  857. */
  858. _getStickyEndColumnPositions(widths, stickyStates) {
  859. /** @type {?} */
  860. const positions = [];
  861. /** @type {?} */
  862. let nextPosition = 0;
  863. for (let i = widths.length; i > 0; i--) {
  864. if (stickyStates[i]) {
  865. positions[i] = nextPosition;
  866. nextPosition += widths[i];
  867. }
  868. }
  869. return positions;
  870. }
  871. }
  872. /**
  873. * @fileoverview added by tsickle
  874. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  875. */
  876. /**
  877. * Returns an error to be thrown when attempting to find an unexisting column.
  878. * \@docs-private
  879. * @param {?} id Id whose lookup failed.
  880. * @return {?}
  881. */
  882. function getTableUnknownColumnError(id) {
  883. return Error(`Could not find column with id "${id}".`);
  884. }
  885. /**
  886. * Returns an error to be thrown when two column definitions have the same name.
  887. * \@docs-private
  888. * @param {?} name
  889. * @return {?}
  890. */
  891. function getTableDuplicateColumnNameError(name) {
  892. return Error(`Duplicate column definition name provided: "${name}".`);
  893. }
  894. /**
  895. * Returns an error to be thrown when there are multiple rows that are missing a when function.
  896. * \@docs-private
  897. * @return {?}
  898. */
  899. function getTableMultipleDefaultRowDefsError() {
  900. return Error(`There can only be one default row without a when predicate function.`);
  901. }
  902. /**
  903. * Returns an error to be thrown when there are no matching row defs for a particular set of data.
  904. * \@docs-private
  905. * @param {?} data
  906. * @return {?}
  907. */
  908. function getTableMissingMatchingRowDefError(data) {
  909. return Error(`Could not find a matching row definition for the` +
  910. `provided row data: ${JSON.stringify(data)}`);
  911. }
  912. /**
  913. * Returns an error to be thrown when there is no row definitions present in the content.
  914. * \@docs-private
  915. * @return {?}
  916. */
  917. function getTableMissingRowDefsError() {
  918. return Error('Missing definitions for header, footer, and row; ' +
  919. 'cannot determine which columns should be rendered.');
  920. }
  921. /**
  922. * Returns an error to be thrown when the data source does not match the compatible types.
  923. * \@docs-private
  924. * @return {?}
  925. */
  926. function getTableUnknownDataSourceError() {
  927. return Error(`Provided data source did not match an array, Observable, or DataSource`);
  928. }
  929. /**
  930. * Returns an error to be thrown when the text column cannot find a parent table to inject.
  931. * \@docs-private
  932. * @return {?}
  933. */
  934. function getTableTextColumnMissingParentTableError() {
  935. return Error(`Text column could not find a parent table for registration.`);
  936. }
  937. /**
  938. * Returns an error to be thrown when a table text column doesn't have a name.
  939. * \@docs-private
  940. * @return {?}
  941. */
  942. function getTableTextColumnMissingNameError() {
  943. return Error(`Table text column must have a name.`);
  944. }
  945. /**
  946. * @fileoverview added by tsickle
  947. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  948. */
  949. /**
  950. * Provides a handle for the table to grab the view container's ng-container to insert data rows.
  951. * \@docs-private
  952. */
  953. class DataRowOutlet {
  954. /**
  955. * @param {?} viewContainer
  956. * @param {?} elementRef
  957. */
  958. constructor(viewContainer, elementRef) {
  959. this.viewContainer = viewContainer;
  960. this.elementRef = elementRef;
  961. }
  962. }
  963. DataRowOutlet.decorators = [
  964. { type: Directive, args: [{ selector: '[rowOutlet]' },] },
  965. ];
  966. /** @nocollapse */
  967. DataRowOutlet.ctorParameters = () => [
  968. { type: ViewContainerRef },
  969. { type: ElementRef }
  970. ];
  971. /**
  972. * Provides a handle for the table to grab the view container's ng-container to insert the header.
  973. * \@docs-private
  974. */
  975. class HeaderRowOutlet {
  976. /**
  977. * @param {?} viewContainer
  978. * @param {?} elementRef
  979. */
  980. constructor(viewContainer, elementRef) {
  981. this.viewContainer = viewContainer;
  982. this.elementRef = elementRef;
  983. }
  984. }
  985. HeaderRowOutlet.decorators = [
  986. { type: Directive, args: [{ selector: '[headerRowOutlet]' },] },
  987. ];
  988. /** @nocollapse */
  989. HeaderRowOutlet.ctorParameters = () => [
  990. { type: ViewContainerRef },
  991. { type: ElementRef }
  992. ];
  993. /**
  994. * Provides a handle for the table to grab the view container's ng-container to insert the footer.
  995. * \@docs-private
  996. */
  997. class FooterRowOutlet {
  998. /**
  999. * @param {?} viewContainer
  1000. * @param {?} elementRef
  1001. */
  1002. constructor(viewContainer, elementRef) {
  1003. this.viewContainer = viewContainer;
  1004. this.elementRef = elementRef;
  1005. }
  1006. }
  1007. FooterRowOutlet.decorators = [
  1008. { type: Directive, args: [{ selector: '[footerRowOutlet]' },] },
  1009. ];
  1010. /** @nocollapse */
  1011. FooterRowOutlet.ctorParameters = () => [
  1012. { type: ViewContainerRef },
  1013. { type: ElementRef }
  1014. ];
  1015. /**
  1016. * The table template that can be used by the mat-table. Should not be used outside of the
  1017. * material library.
  1018. * \@docs-private
  1019. * @type {?}
  1020. */
  1021. const CDK_TABLE_TEMPLATE =
  1022. // Note that according to MDN, the `caption` element has to be projected as the **first**
  1023. // element in the table. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption
  1024. `
  1025. <ng-content select="caption"></ng-content>
  1026. <ng-container headerRowOutlet></ng-container>
  1027. <ng-container rowOutlet></ng-container>
  1028. <ng-container footerRowOutlet></ng-container>
  1029. `;
  1030. /**
  1031. * A data table that can render a header row, data rows, and a footer row.
  1032. * Uses the dataSource input to determine the data to be rendered. The data can be provided either
  1033. * as a data array, an Observable stream that emits the data array to render, or a DataSource with a
  1034. * connect function that will return an Observable stream that emits the data array to render.
  1035. * @template T
  1036. */
  1037. class CdkTable {
  1038. /**
  1039. * @param {?} _differs
  1040. * @param {?} _changeDetectorRef
  1041. * @param {?} _elementRef
  1042. * @param {?} role
  1043. * @param {?} _dir
  1044. * @param {?} _document
  1045. * @param {?} _platform
  1046. */
  1047. constructor(_differs, _changeDetectorRef, _elementRef, role, _dir, _document, _platform) {
  1048. this._differs = _differs;
  1049. this._changeDetectorRef = _changeDetectorRef;
  1050. this._elementRef = _elementRef;
  1051. this._dir = _dir;
  1052. this._platform = _platform;
  1053. /**
  1054. * Subject that emits when the component has been destroyed.
  1055. */
  1056. this._onDestroy = new Subject();
  1057. /**
  1058. * Map of all the user's defined columns (header, data, and footer cell template) identified by
  1059. * name. Collection populated by the column definitions gathered by `ContentChildren` as well as
  1060. * any custom column definitions added to `_customColumnDefs`.
  1061. */
  1062. this._columnDefsByName = new Map();
  1063. /**
  1064. * Column definitions that were defined outside of the direct content children of the table.
  1065. * These will be defined when, e.g., creating a wrapper around the cdkTable that has
  1066. * column definitions as *its* content child.
  1067. */
  1068. this._customColumnDefs = new Set();
  1069. /**
  1070. * Data row definitions that were defined outside of the direct content children of the table.
  1071. * These will be defined when, e.g., creating a wrapper around the cdkTable that has
  1072. * built-in data rows as *its* content child.
  1073. */
  1074. this._customRowDefs = new Set();
  1075. /**
  1076. * Header row definitions that were defined outside of the direct content children of the table.
  1077. * These will be defined when, e.g., creating a wrapper around the cdkTable that has
  1078. * built-in header rows as *its* content child.
  1079. */
  1080. this._customHeaderRowDefs = new Set();
  1081. /**
  1082. * Footer row definitions that were defined outside of the direct content children of the table.
  1083. * These will be defined when, e.g., creating a wrapper around the cdkTable that has a
  1084. * built-in footer row as *its* content child.
  1085. */
  1086. this._customFooterRowDefs = new Set();
  1087. /**
  1088. * Whether the header row definition has been changed. Triggers an update to the header row after
  1089. * content is checked. Initialized as true so that the table renders the initial set of rows.
  1090. */
  1091. this._headerRowDefChanged = true;
  1092. /**
  1093. * Whether the footer row definition has been changed. Triggers an update to the footer row after
  1094. * content is checked. Initialized as true so that the table renders the initial set of rows.
  1095. */
  1096. this._footerRowDefChanged = true;
  1097. /**
  1098. * Cache of the latest rendered `RenderRow` objects as a map for easy retrieval when constructing
  1099. * a new list of `RenderRow` objects for rendering rows. Since the new list is constructed with
  1100. * the cached `RenderRow` objects when possible, the row identity is preserved when the data
  1101. * and row template matches, which allows the `IterableDiffer` to check rows by reference
  1102. * and understand which rows are added/moved/removed.
  1103. *
  1104. * Implemented as a map of maps where the first key is the `data: T` object and the second is the
  1105. * `CdkRowDef<T>` object. With the two keys, the cache points to a `RenderRow<T>` object that
  1106. * contains an array of created pairs. The array is necessary to handle cases where the data
  1107. * array contains multiple duplicate data objects and each instantiated `RenderRow` must be
  1108. * stored.
  1109. */
  1110. this._cachedRenderRowsMap = new Map();
  1111. /**
  1112. * CSS class added to any row or cell that has sticky positioning applied. May be overriden by
  1113. * table subclasses.
  1114. */
  1115. this.stickyCssClass = 'cdk-table-sticky';
  1116. this._multiTemplateDataRows = false;
  1117. // TODO(andrewseguin): Remove max value as the end index
  1118. // and instead calculate the view on init and scroll.
  1119. /**
  1120. * Stream containing the latest information on what rows are being displayed on screen.
  1121. * Can be used by the data source to as a heuristic of what data should be provided.
  1122. *
  1123. * \@docs-private
  1124. */
  1125. this.viewChange = new BehaviorSubject({ start: 0, end: Number.MAX_VALUE });
  1126. if (!role) {
  1127. this._elementRef.nativeElement.setAttribute('role', 'grid');
  1128. }
  1129. this._document = _document;
  1130. this._isNativeHtmlTable = this._elementRef.nativeElement.nodeName === 'TABLE';
  1131. }
  1132. /**
  1133. * Tracking function that will be used to check the differences in data changes. Used similarly
  1134. * to `ngFor` `trackBy` function. Optimize row operations by identifying a row based on its data
  1135. * relative to the function to know if a row should be added/removed/moved.
  1136. * Accepts a function that takes two parameters, `index` and `item`.
  1137. * @return {?}
  1138. */
  1139. get trackBy() {
  1140. return this._trackByFn;
  1141. }
  1142. /**
  1143. * @param {?} fn
  1144. * @return {?}
  1145. */
  1146. set trackBy(fn) {
  1147. if (isDevMode() && fn != null && typeof fn !== 'function' && (/** @type {?} */ (console)) &&
  1148. (/** @type {?} */ (console.warn))) {
  1149. console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}.`);
  1150. }
  1151. this._trackByFn = fn;
  1152. }
  1153. /**
  1154. * The table's source of data, which can be provided in three ways (in order of complexity):
  1155. * - Simple data array (each object represents one table row)
  1156. * - Stream that emits a data array each time the array changes
  1157. * - `DataSource` object that implements the connect/disconnect interface.
  1158. *
  1159. * If a data array is provided, the table must be notified when the array's objects are
  1160. * added, removed, or moved. This can be done by calling the `renderRows()` function which will
  1161. * render the diff since the last table render. If the data array reference is changed, the table
  1162. * will automatically trigger an update to the rows.
  1163. *
  1164. * When providing an Observable stream, the table will trigger an update automatically when the
  1165. * stream emits a new array of data.
  1166. *
  1167. * Finally, when providing a `DataSource` object, the table will use the Observable stream
  1168. * provided by the connect function and trigger updates when that stream emits new data array
  1169. * values. During the table's ngOnDestroy or when the data source is removed from the table, the
  1170. * table will call the DataSource's `disconnect` function (may be useful for cleaning up any
  1171. * subscriptions registered during the connect process).
  1172. * @return {?}
  1173. */
  1174. get dataSource() {
  1175. return this._dataSource;
  1176. }
  1177. /**
  1178. * @param {?} dataSource
  1179. * @return {?}
  1180. */
  1181. set dataSource(dataSource) {
  1182. if (this._dataSource !== dataSource) {
  1183. this._switchDataSource(dataSource);
  1184. }
  1185. }
  1186. /**
  1187. * Whether to allow multiple rows per data object by evaluating which rows evaluate their 'when'
  1188. * predicate to true. If `multiTemplateDataRows` is false, which is the default value, then each
  1189. * dataobject will render the first row that evaluates its when predicate to true, in the order
  1190. * defined in the table, or otherwise the default row which does not have a when predicate.
  1191. * @return {?}
  1192. */
  1193. get multiTemplateDataRows() {
  1194. return this._multiTemplateDataRows;
  1195. }
  1196. /**
  1197. * @param {?} v
  1198. * @return {?}
  1199. */
  1200. set multiTemplateDataRows(v) {
  1201. this._multiTemplateDataRows = coerceBooleanProperty(v);
  1202. // In Ivy if this value is set via a static attribute (e.g. <table multiTemplateDataRows>),
  1203. // this setter will be invoked before the row outlet has been defined hence the null check.
  1204. if (this._rowOutlet && this._rowOutlet.viewContainer.length) {
  1205. this._forceRenderDataRows();
  1206. }
  1207. }
  1208. /**
  1209. * @return {?}
  1210. */
  1211. ngOnInit() {
  1212. this._setupStickyStyler();
  1213. if (this._isNativeHtmlTable) {
  1214. this._applyNativeTableSections();
  1215. }
  1216. // Set up the trackBy function so that it uses the `RenderRow` as its identity by default. If
  1217. // the user has provided a custom trackBy, return the result of that function as evaluated
  1218. // with the values of the `RenderRow`'s data and index.
  1219. this._dataDiffer = this._differs.find([]).create((/**
  1220. * @param {?} _i
  1221. * @param {?} dataRow
  1222. * @return {?}
  1223. */
  1224. (_i, dataRow) => {
  1225. return this.trackBy ? this.trackBy(dataRow.dataIndex, dataRow.data) : dataRow;
  1226. }));
  1227. }
  1228. /**
  1229. * @return {?}
  1230. */
  1231. ngAfterContentChecked() {
  1232. // Cache the row and column definitions gathered by ContentChildren and programmatic injection.
  1233. this._cacheRowDefs();
  1234. this._cacheColumnDefs();
  1235. // Make sure that the user has at least added header, footer, or data row def.
  1236. if (!this._headerRowDefs.length && !this._footerRowDefs.length && !this._rowDefs.length) {
  1237. throw getTableMissingRowDefsError();
  1238. }
  1239. // Render updates if the list of columns have been changed for the header, row, or footer defs.
  1240. this._renderUpdatedColumns();
  1241. // If the header row definition has been changed, trigger a render to the header row.
  1242. if (this._headerRowDefChanged) {
  1243. this._forceRenderHeaderRows();
  1244. this._headerRowDefChanged = false;
  1245. }
  1246. // If the footer row definition has been changed, trigger a render to the footer row.
  1247. if (this._footerRowDefChanged) {
  1248. this._forceRenderFooterRows();
  1249. this._footerRowDefChanged = false;
  1250. }
  1251. // If there is a data source and row definitions, connect to the data source unless a
  1252. // connection has already been made.
  1253. if (this.dataSource && this._rowDefs.length > 0 && !this._renderChangeSubscription) {
  1254. this._observeRenderChanges();
  1255. }
  1256. this._checkStickyStates();
  1257. }
  1258. /**
  1259. * @return {?}
  1260. */
  1261. ngOnDestroy() {
  1262. this._rowOutlet.viewContainer.clear();
  1263. this._headerRowOutlet.viewContainer.clear();
  1264. this._footerRowOutlet.viewContainer.clear();
  1265. this._cachedRenderRowsMap.clear();
  1266. this._onDestroy.next();
  1267. this._onDestroy.complete();
  1268. if (isDataSource(this.dataSource)) {
  1269. this.dataSource.disconnect(this);
  1270. }
  1271. }
  1272. /**
  1273. * Renders rows based on the table's latest set of data, which was either provided directly as an
  1274. * input or retrieved through an Observable stream (directly or from a DataSource).
  1275. * Checks for differences in the data since the last diff to perform only the necessary
  1276. * changes (add/remove/move rows).
  1277. *
  1278. * If the table's data source is a DataSource or Observable, this will be invoked automatically
  1279. * each time the provided Observable stream emits a new data array. Otherwise if your data is
  1280. * an array, this function will need to be called to render any changes.
  1281. * @return {?}
  1282. */
  1283. renderRows() {
  1284. this._renderRows = this._getAllRenderRows();
  1285. /** @type {?} */
  1286. const changes = this._dataDiffer.diff(this._renderRows);
  1287. if (!changes) {
  1288. return;
  1289. }
  1290. /** @type {?} */
  1291. const viewContainer = this._rowOutlet.viewContainer;
  1292. changes.forEachOperation((/**
  1293. * @param {?} record
  1294. * @param {?} prevIndex
  1295. * @param {?} currentIndex
  1296. * @return {?}
  1297. */
  1298. (record, prevIndex, currentIndex) => {
  1299. if (record.previousIndex == null) {
  1300. this._insertRow(record.item, (/** @type {?} */ (currentIndex)));
  1301. }
  1302. else if (currentIndex == null) {
  1303. viewContainer.remove((/** @type {?} */ (prevIndex)));
  1304. }
  1305. else {
  1306. /** @type {?} */
  1307. const view = (/** @type {?} */ (viewContainer.get((/** @type {?} */ (prevIndex)))));
  1308. viewContainer.move((/** @type {?} */ (view)), currentIndex);
  1309. }
  1310. }));
  1311. // Update the meta context of a row's context data (index, count, first, last, ...)
  1312. this._updateRowIndexContext();
  1313. // Update rows that did not get added/removed/moved but may have had their identity changed,
  1314. // e.g. if trackBy matched data on some property but the actual data reference changed.
  1315. changes.forEachIdentityChange((/**
  1316. * @param {?} record
  1317. * @return {?}
  1318. */
  1319. (record) => {
  1320. /** @type {?} */
  1321. const rowView = (/** @type {?} */ (viewContainer.get((/** @type {?} */ (record.currentIndex)))));
  1322. rowView.context.$implicit = record.item.data;
  1323. }));
  1324. this.updateStickyColumnStyles();
  1325. }
  1326. /**
  1327. * Sets the header row definition to be used. Overrides the header row definition gathered by
  1328. * using `ContentChild`, if one exists. Sets a flag that will re-render the header row after the
  1329. * table's content is checked.
  1330. * \@docs-private
  1331. * @deprecated Use `addHeaderRowDef` and `removeHeaderRowDef` instead
  1332. * \@breaking-change 8.0.0
  1333. * @param {?} headerRowDef
  1334. * @return {?}
  1335. */
  1336. setHeaderRowDef(headerRowDef) {
  1337. this._customHeaderRowDefs = new Set([headerRowDef]);
  1338. this._headerRowDefChanged = true;
  1339. }
  1340. /**
  1341. * Sets the footer row definition to be used. Overrides the footer row definition gathered by
  1342. * using `ContentChild`, if one exists. Sets a flag that will re-render the footer row after the
  1343. * table's content is checked.
  1344. * \@docs-private
  1345. * @deprecated Use `addFooterRowDef` and `removeFooterRowDef` instead
  1346. * \@breaking-change 8.0.0
  1347. * @param {?} footerRowDef
  1348. * @return {?}
  1349. */
  1350. setFooterRowDef(footerRowDef) {
  1351. this._customFooterRowDefs = new Set([footerRowDef]);
  1352. this._footerRowDefChanged = true;
  1353. }
  1354. /**
  1355. * Adds a column definition that was not included as part of the content children.
  1356. * @param {?} columnDef
  1357. * @return {?}
  1358. */
  1359. addColumnDef(columnDef) {
  1360. this._customColumnDefs.add(columnDef);
  1361. }
  1362. /**
  1363. * Removes a column definition that was not included as part of the content children.
  1364. * @param {?} columnDef
  1365. * @return {?}
  1366. */
  1367. removeColumnDef(columnDef) {
  1368. this._customColumnDefs.delete(columnDef);
  1369. }
  1370. /**
  1371. * Adds a row definition that was not included as part of the content children.
  1372. * @param {?} rowDef
  1373. * @return {?}
  1374. */
  1375. addRowDef(rowDef) {
  1376. this._customRowDefs.add(rowDef);
  1377. }
  1378. /**
  1379. * Removes a row definition that was not included as part of the content children.
  1380. * @param {?} rowDef
  1381. * @return {?}
  1382. */
  1383. removeRowDef(rowDef) {
  1384. this._customRowDefs.delete(rowDef);
  1385. }
  1386. /**
  1387. * Adds a header row definition that was not included as part of the content children.
  1388. * @param {?} headerRowDef
  1389. * @return {?}
  1390. */
  1391. addHeaderRowDef(headerRowDef) {
  1392. this._customHeaderRowDefs.add(headerRowDef);
  1393. this._headerRowDefChanged = true;
  1394. }
  1395. /**
  1396. * Removes a header row definition that was not included as part of the content children.
  1397. * @param {?} headerRowDef
  1398. * @return {?}
  1399. */
  1400. removeHeaderRowDef(headerRowDef) {
  1401. this._customHeaderRowDefs.delete(headerRowDef);
  1402. this._headerRowDefChanged = true;
  1403. }
  1404. /**
  1405. * Adds a footer row definition that was not included as part of the content children.
  1406. * @param {?} footerRowDef
  1407. * @return {?}
  1408. */
  1409. addFooterRowDef(footerRowDef) {
  1410. this._customFooterRowDefs.add(footerRowDef);
  1411. this._footerRowDefChanged = true;
  1412. }
  1413. /**
  1414. * Removes a footer row definition that was not included as part of the content children.
  1415. * @param {?} footerRowDef
  1416. * @return {?}
  1417. */
  1418. removeFooterRowDef(footerRowDef) {
  1419. this._customFooterRowDefs.delete(footerRowDef);
  1420. this._footerRowDefChanged = true;
  1421. }
  1422. /**
  1423. * Updates the header sticky styles. First resets all applied styles with respect to the cells
  1424. * sticking to the top. Then, evaluating which cells need to be stuck to the top. This is
  1425. * automatically called when the header row changes its displayed set of columns, or if its
  1426. * sticky input changes. May be called manually for cases where the cell content changes outside
  1427. * of these events.
  1428. * @return {?}
  1429. */
  1430. updateStickyHeaderRowStyles() {
  1431. /** @type {?} */
  1432. const headerRows = this._getRenderedRows(this._headerRowOutlet);
  1433. /** @type {?} */
  1434. const tableElement = (/** @type {?} */ (this._elementRef.nativeElement));
  1435. // Hide the thead element if there are no header rows. This is necessary to satisfy
  1436. // overzealous a11y checkers that fail because the `rowgroup` element does not contain
  1437. // required child `row`.
  1438. /** @type {?} */
  1439. const thead = tableElement.querySelector('thead');
  1440. if (thead) {
  1441. thead.style.display = headerRows.length ? '' : 'none';
  1442. }
  1443. /** @type {?} */
  1444. const stickyStates = this._headerRowDefs.map((/**
  1445. * @param {?} def
  1446. * @return {?}
  1447. */
  1448. def => def.sticky));
  1449. this._stickyStyler.clearStickyPositioning(headerRows, ['top']);
  1450. this._stickyStyler.stickRows(headerRows, stickyStates, 'top');
  1451. // Reset the dirty state of the sticky input change since it has been used.
  1452. this._headerRowDefs.forEach((/**
  1453. * @param {?} def
  1454. * @return {?}
  1455. */
  1456. def => def.resetStickyChanged()));
  1457. }
  1458. /**
  1459. * Updates the footer sticky styles. First resets all applied styles with respect to the cells
  1460. * sticking to the bottom. Then, evaluating which cells need to be stuck to the bottom. This is
  1461. * automatically called when the footer row changes its displayed set of columns, or if its
  1462. * sticky input changes. May be called manually for cases where the cell content changes outside
  1463. * of these events.
  1464. * @return {?}
  1465. */
  1466. updateStickyFooterRowStyles() {
  1467. /** @type {?} */
  1468. const footerRows = this._getRenderedRows(this._footerRowOutlet);
  1469. /** @type {?} */
  1470. const tableElement = (/** @type {?} */ (this._elementRef.nativeElement));
  1471. // Hide the tfoot element if there are no footer rows. This is necessary to satisfy
  1472. // overzealous a11y checkers that fail because the `rowgroup` element does not contain
  1473. // required child `row`.
  1474. /** @type {?} */
  1475. const tfoot = tableElement.querySelector('tfoot');
  1476. if (tfoot) {
  1477. tfoot.style.display = footerRows.length ? '' : 'none';
  1478. }
  1479. /** @type {?} */
  1480. const stickyStates = this._footerRowDefs.map((/**
  1481. * @param {?} def
  1482. * @return {?}
  1483. */
  1484. def => def.sticky));
  1485. this._stickyStyler.clearStickyPositioning(footerRows, ['bottom']);
  1486. this._stickyStyler.stickRows(footerRows, stickyStates, 'bottom');
  1487. this._stickyStyler.updateStickyFooterContainer(this._elementRef.nativeElement, stickyStates);
  1488. // Reset the dirty state of the sticky input change since it has been used.
  1489. this._footerRowDefs.forEach((/**
  1490. * @param {?} def
  1491. * @return {?}
  1492. */
  1493. def => def.resetStickyChanged()));
  1494. }
  1495. /**
  1496. * Updates the column sticky styles. First resets all applied styles with respect to the cells
  1497. * sticking to the left and right. Then sticky styles are added for the left and right according
  1498. * to the column definitions for each cell in each row. This is automatically called when
  1499. * the data source provides a new set of data or when a column definition changes its sticky
  1500. * input. May be called manually for cases where the cell content changes outside of these events.
  1501. * @return {?}
  1502. */
  1503. updateStickyColumnStyles() {
  1504. /** @type {?} */
  1505. const headerRows = this._getRenderedRows(this._headerRowOutlet);
  1506. /** @type {?} */
  1507. const dataRows = this._getRenderedRows(this._rowOutlet);
  1508. /** @type {?} */
  1509. const footerRows = this._getRenderedRows(this._footerRowOutlet);
  1510. // Clear the left and right positioning from all columns in the table across all rows since
  1511. // sticky columns span across all table sections (header, data, footer)
  1512. this._stickyStyler.clearStickyPositioning([...headerRows, ...dataRows, ...footerRows], ['left', 'right']);
  1513. // Update the sticky styles for each header row depending on the def's sticky state
  1514. headerRows.forEach((/**
  1515. * @param {?} headerRow
  1516. * @param {?} i
  1517. * @return {?}
  1518. */
  1519. (headerRow, i) => {
  1520. this._addStickyColumnStyles([headerRow], this._headerRowDefs[i]);
  1521. }));
  1522. // Update the sticky styles for each data row depending on its def's sticky state
  1523. this._rowDefs.forEach((/**
  1524. * @param {?} rowDef
  1525. * @return {?}
  1526. */
  1527. rowDef => {
  1528. // Collect all the rows rendered with this row definition.
  1529. /** @type {?} */
  1530. const rows = [];
  1531. for (let i = 0; i < dataRows.length; i++) {
  1532. if (this._renderRows[i].rowDef === rowDef) {
  1533. rows.push(dataRows[i]);
  1534. }
  1535. }
  1536. this._addStickyColumnStyles(rows, rowDef);
  1537. }));
  1538. // Update the sticky styles for each footer row depending on the def's sticky state
  1539. footerRows.forEach((/**
  1540. * @param {?} footerRow
  1541. * @param {?} i
  1542. * @return {?}
  1543. */
  1544. (footerRow, i) => {
  1545. this._addStickyColumnStyles([footerRow], this._footerRowDefs[i]);
  1546. }));
  1547. // Reset the dirty state of the sticky input change since it has been used.
  1548. Array.from(this._columnDefsByName.values()).forEach((/**
  1549. * @param {?} def
  1550. * @return {?}
  1551. */
  1552. def => def.resetStickyChanged()));
  1553. }
  1554. /**
  1555. * Get the list of RenderRow objects to render according to the current list of data and defined
  1556. * row definitions. If the previous list already contained a particular pair, it should be reused
  1557. * so that the differ equates their references.
  1558. * @private
  1559. * @return {?}
  1560. */
  1561. _getAllRenderRows() {
  1562. /** @type {?} */
  1563. const renderRows = [];
  1564. // Store the cache and create a new one. Any re-used RenderRow objects will be moved into the
  1565. // new cache while unused ones can be picked up by garbage collection.
  1566. /** @type {?} */
  1567. const prevCachedRenderRows = this._cachedRenderRowsMap;
  1568. this._cachedRenderRowsMap = new Map();
  1569. // For each data object, get the list of rows that should be rendered, represented by the
  1570. // respective `RenderRow` object which is the pair of `data` and `CdkRowDef`.
  1571. for (let i = 0; i < this._data.length; i++) {
  1572. /** @type {?} */
  1573. let data = this._data[i];
  1574. /** @type {?} */
  1575. const renderRowsForData = this._getRenderRowsForData(data, i, prevCachedRenderRows.get(data));
  1576. if (!this._cachedRenderRowsMap.has(data)) {
  1577. this._cachedRenderRowsMap.set(data, new WeakMap());
  1578. }
  1579. for (let j = 0; j < renderRowsForData.length; j++) {
  1580. /** @type {?} */
  1581. let renderRow = renderRowsForData[j];
  1582. /** @type {?} */
  1583. const cache = (/** @type {?} */ (this._cachedRenderRowsMap.get(renderRow.data)));
  1584. if (cache.has(renderRow.rowDef)) {
  1585. (/** @type {?} */ (cache.get(renderRow.rowDef))).push(renderRow);
  1586. }
  1587. else {
  1588. cache.set(renderRow.rowDef, [renderRow]);
  1589. }
  1590. renderRows.push(renderRow);
  1591. }
  1592. }
  1593. return renderRows;
  1594. }
  1595. /**
  1596. * Gets a list of `RenderRow<T>` for the provided data object and any `CdkRowDef` objects that
  1597. * should be rendered for this data. Reuses the cached RenderRow objects if they match the same
  1598. * `(T, CdkRowDef)` pair.
  1599. * @private
  1600. * @param {?} data
  1601. * @param {?} dataIndex
  1602. * @param {?=} cache
  1603. * @return {?}
  1604. */
  1605. _getRenderRowsForData(data, dataIndex, cache) {
  1606. /** @type {?} */
  1607. const rowDefs = this._getRowDefs(data, dataIndex);
  1608. return rowDefs.map((/**
  1609. * @param {?} rowDef
  1610. * @return {?}
  1611. */
  1612. rowDef => {
  1613. /** @type {?} */
  1614. const cachedRenderRows = (cache && cache.has(rowDef)) ? (/** @type {?} */ (cache.get(rowDef))) : [];
  1615. if (cachedRenderRows.length) {
  1616. /** @type {?} */
  1617. const dataRow = (/** @type {?} */ (cachedRenderRows.shift()));
  1618. dataRow.dataIndex = dataIndex;
  1619. return dataRow;
  1620. }
  1621. else {
  1622. return { data, rowDef, dataIndex };
  1623. }
  1624. }));
  1625. }
  1626. /**
  1627. * Update the map containing the content's column definitions.
  1628. * @private
  1629. * @return {?}
  1630. */
  1631. _cacheColumnDefs() {
  1632. this._columnDefsByName.clear();
  1633. /** @type {?} */
  1634. const columnDefs = mergeQueryListAndSet(this._contentColumnDefs, this._customColumnDefs);
  1635. columnDefs.forEach((/**
  1636. * @param {?} columnDef
  1637. * @return {?}
  1638. */
  1639. columnDef => {
  1640. if (this._columnDefsByName.has(columnDef.name)) {
  1641. throw getTableDuplicateColumnNameError(columnDef.name);
  1642. }
  1643. this._columnDefsByName.set(columnDef.name, columnDef);
  1644. }));
  1645. }
  1646. /**
  1647. * Update the list of all available row definitions that can be used.
  1648. * @private
  1649. * @return {?}
  1650. */
  1651. _cacheRowDefs() {
  1652. this._headerRowDefs =
  1653. mergeQueryListAndSet(this._contentHeaderRowDefs, this._customHeaderRowDefs);
  1654. this._footerRowDefs =
  1655. mergeQueryListAndSet(this._contentFooterRowDefs, this._customFooterRowDefs);
  1656. this._rowDefs = mergeQueryListAndSet(this._contentRowDefs, this._customRowDefs);
  1657. // After all row definitions are determined, find the row definition to be considered default.
  1658. /** @type {?} */
  1659. const defaultRowDefs = this._rowDefs.filter((/**
  1660. * @param {?} def
  1661. * @return {?}
  1662. */
  1663. def => !def.when));
  1664. if (!this.multiTemplateDataRows && defaultRowDefs.length > 1) {
  1665. throw getTableMultipleDefaultRowDefsError();
  1666. }
  1667. this._defaultRowDef = defaultRowDefs[0];
  1668. }
  1669. /**
  1670. * Check if the header, data, or footer rows have changed what columns they want to display or
  1671. * whether the sticky states have changed for the header or footer. If there is a diff, then
  1672. * re-render that section.
  1673. * @private
  1674. * @return {?}
  1675. */
  1676. _renderUpdatedColumns() {
  1677. /** @type {?} */
  1678. const columnsDiffReducer = (/**
  1679. * @param {?} acc
  1680. * @param {?} def
  1681. * @return {?}
  1682. */
  1683. (acc, def) => acc || !!def.getColumnsDiff());
  1684. // Force re-render data rows if the list of column definitions have changed.
  1685. if (this._rowDefs.reduce(columnsDiffReducer, false)) {
  1686. this._forceRenderDataRows();
  1687. }
  1688. // Force re-render header/footer rows if the list of column definitions have changed..
  1689. if (this._headerRowDefs.reduce(columnsDiffReducer, false)) {
  1690. this._forceRenderHeaderRows();
  1691. }
  1692. if (this._footerRowDefs.reduce(columnsDiffReducer, false)) {
  1693. this._forceRenderFooterRows();
  1694. }
  1695. }
  1696. /**
  1697. * Switch to the provided data source by resetting the data and unsubscribing from the current
  1698. * render change subscription if one exists. If the data source is null, interpret this by
  1699. * clearing the row outlet. Otherwise start listening for new data.
  1700. * @private
  1701. * @param {?} dataSource
  1702. * @return {?}
  1703. */
  1704. _switchDataSource(dataSource) {
  1705. this._data = [];
  1706. if (isDataSource(this.dataSource)) {
  1707. this.dataSource.disconnect(this);
  1708. }
  1709. // Stop listening for data from the previous data source.
  1710. if (this._renderChangeSubscription) {
  1711. this._renderChangeSubscription.unsubscribe();
  1712. this._renderChangeSubscription = null;
  1713. }
  1714. if (!dataSource) {
  1715. if (this._dataDiffer) {
  1716. this._dataDiffer.diff([]);
  1717. }
  1718. this._rowOutlet.viewContainer.clear();
  1719. }
  1720. this._dataSource = dataSource;
  1721. }
  1722. /**
  1723. * Set up a subscription for the data provided by the data source.
  1724. * @private
  1725. * @return {?}
  1726. */
  1727. _observeRenderChanges() {
  1728. // If no data source has been set, there is nothing to observe for changes.
  1729. if (!this.dataSource) {
  1730. return;
  1731. }
  1732. /** @type {?} */
  1733. let dataStream;
  1734. if (isDataSource(this.dataSource)) {
  1735. dataStream = this.dataSource.connect(this);
  1736. }
  1737. else if (this.dataSource instanceof Observable) {
  1738. dataStream = this.dataSource;
  1739. }
  1740. else if (Array.isArray(this.dataSource)) {
  1741. dataStream = of(this.dataSource);
  1742. }
  1743. if (dataStream === undefined) {
  1744. throw getTableUnknownDataSourceError();
  1745. }
  1746. this._renderChangeSubscription = dataStream.pipe(takeUntil(this._onDestroy)).subscribe((/**
  1747. * @param {?} data
  1748. * @return {?}
  1749. */
  1750. data => {
  1751. this._data = data || [];
  1752. this.renderRows();
  1753. }));
  1754. }
  1755. /**
  1756. * Clears any existing content in the header row outlet and creates a new embedded view
  1757. * in the outlet using the header row definition.
  1758. * @private
  1759. * @return {?}
  1760. */
  1761. _forceRenderHeaderRows() {
  1762. // Clear the header row outlet if any content exists.
  1763. if (this._headerRowOutlet.viewContainer.length > 0) {
  1764. this._headerRowOutlet.viewContainer.clear();
  1765. }
  1766. this._headerRowDefs.forEach((/**
  1767. * @param {?} def
  1768. * @param {?} i
  1769. * @return {?}
  1770. */
  1771. (def, i) => this._renderRow(this._headerRowOutlet, def, i)));
  1772. this.updateStickyHeaderRowStyles();
  1773. this.updateStickyColumnStyles();
  1774. }
  1775. /**
  1776. * Clears any existing content in the footer row outlet and creates a new embedded view
  1777. * in the outlet using the footer row definition.
  1778. * @private
  1779. * @return {?}
  1780. */
  1781. _forceRenderFooterRows() {
  1782. // Clear the footer row outlet if any content exists.
  1783. if (this._footerRowOutlet.viewContainer.length > 0) {
  1784. this._footerRowOutlet.viewContainer.clear();
  1785. }
  1786. this._footerRowDefs.forEach((/**
  1787. * @param {?} def
  1788. * @param {?} i
  1789. * @return {?}
  1790. */
  1791. (def, i) => this._renderRow(this._footerRowOutlet, def, i)));
  1792. this.updateStickyFooterRowStyles();
  1793. this.updateStickyColumnStyles();
  1794. }
  1795. /**
  1796. * Adds the sticky column styles for the rows according to the columns' stick states.
  1797. * @private
  1798. * @param {?} rows
  1799. * @param {?} rowDef
  1800. * @return {?}
  1801. */
  1802. _addStickyColumnStyles(rows, rowDef) {
  1803. /** @type {?} */
  1804. const columnDefs = Array.from(rowDef.columns || []).map((/**
  1805. * @param {?} columnName
  1806. * @return {?}
  1807. */
  1808. columnName => {
  1809. /** @type {?} */
  1810. const columnDef = this._columnDefsByName.get(columnName);
  1811. if (!columnDef) {
  1812. throw getTableUnknownColumnError(columnName);
  1813. }
  1814. return (/** @type {?} */ (columnDef));
  1815. }));
  1816. /** @type {?} */
  1817. const stickyStartStates = columnDefs.map((/**
  1818. * @param {?} columnDef
  1819. * @return {?}
  1820. */
  1821. columnDef => columnDef.sticky));
  1822. /** @type {?} */
  1823. const stickyEndStates = columnDefs.map((/**
  1824. * @param {?} columnDef
  1825. * @return {?}
  1826. */
  1827. columnDef => columnDef.stickyEnd));
  1828. this._stickyStyler.updateStickyColumns(rows, stickyStartStates, stickyEndStates);
  1829. }
  1830. /**
  1831. * Gets the list of rows that have been rendered in the row outlet.
  1832. * @param {?} rowOutlet
  1833. * @return {?}
  1834. */
  1835. _getRenderedRows(rowOutlet) {
  1836. /** @type {?} */
  1837. const renderedRows = [];
  1838. for (let i = 0; i < rowOutlet.viewContainer.length; i++) {
  1839. /** @type {?} */
  1840. const viewRef = ((/** @type {?} */ ((/** @type {?} */ (rowOutlet.viewContainer.get(i))))));
  1841. renderedRows.push(viewRef.rootNodes[0]);
  1842. }
  1843. return renderedRows;
  1844. }
  1845. /**
  1846. * Get the matching row definitions that should be used for this row data. If there is only
  1847. * one row definition, it is returned. Otherwise, find the row definitions that has a when
  1848. * predicate that returns true with the data. If none return true, return the default row
  1849. * definition.
  1850. * @param {?} data
  1851. * @param {?} dataIndex
  1852. * @return {?}
  1853. */
  1854. _getRowDefs(data, dataIndex) {
  1855. if (this._rowDefs.length == 1) {
  1856. return [this._rowDefs[0]];
  1857. }
  1858. /** @type {?} */
  1859. let rowDefs = [];
  1860. if (this.multiTemplateDataRows) {
  1861. rowDefs = this._rowDefs.filter((/**
  1862. * @param {?} def
  1863. * @return {?}
  1864. */
  1865. def => !def.when || def.when(dataIndex, data)));
  1866. }
  1867. else {
  1868. /** @type {?} */
  1869. let rowDef = this._rowDefs.find((/**
  1870. * @param {?} def
  1871. * @return {?}
  1872. */
  1873. def => def.when && def.when(dataIndex, data))) || this._defaultRowDef;
  1874. if (rowDef) {
  1875. rowDefs.push(rowDef);
  1876. }
  1877. }
  1878. if (!rowDefs.length) {
  1879. throw getTableMissingMatchingRowDefError(data);
  1880. }
  1881. return rowDefs;
  1882. }
  1883. /**
  1884. * Create the embedded view for the data row template and place it in the correct index location
  1885. * within the data row view container.
  1886. * @private
  1887. * @param {?} renderRow
  1888. * @param {?} renderIndex
  1889. * @return {?}
  1890. */
  1891. _insertRow(renderRow, renderIndex) {
  1892. /** @type {?} */
  1893. const rowDef = renderRow.rowDef;
  1894. /** @type {?} */
  1895. const context = { $implicit: renderRow.data };
  1896. this._renderRow(this._rowOutlet, rowDef, renderIndex, context);
  1897. }
  1898. /**
  1899. * Creates a new row template in the outlet and fills it with the set of cell templates.
  1900. * Optionally takes a context to provide to the row and cells, as well as an optional index
  1901. * of where to place the new row template in the outlet.
  1902. * @private
  1903. * @param {?} outlet
  1904. * @param {?} rowDef
  1905. * @param {?} index
  1906. * @param {?=} context
  1907. * @return {?}
  1908. */
  1909. _renderRow(outlet, rowDef, index, context = {}) {
  1910. // TODO(andrewseguin): enforce that one outlet was instantiated from createEmbeddedView
  1911. outlet.viewContainer.createEmbeddedView(rowDef.template, context, index);
  1912. for (let cellTemplate of this._getCellTemplates(rowDef)) {
  1913. if (CdkCellOutlet.mostRecentCellOutlet) {
  1914. CdkCellOutlet.mostRecentCellOutlet._viewContainer.createEmbeddedView(cellTemplate, context);
  1915. }
  1916. }
  1917. this._changeDetectorRef.markForCheck();
  1918. }
  1919. /**
  1920. * Updates the index-related context for each row to reflect any changes in the index of the rows,
  1921. * e.g. first/last/even/odd.
  1922. * @private
  1923. * @return {?}
  1924. */
  1925. _updateRowIndexContext() {
  1926. /** @type {?} */
  1927. const viewContainer = this._rowOutlet.viewContainer;
  1928. for (let renderIndex = 0, count = viewContainer.length; renderIndex < count; renderIndex++) {
  1929. /** @type {?} */
  1930. const viewRef = (/** @type {?} */ (viewContainer.get(renderIndex)));
  1931. /** @type {?} */
  1932. const context = (/** @type {?} */ (viewRef.context));
  1933. context.count = count;
  1934. context.first = renderIndex === 0;
  1935. context.last = renderIndex === count - 1;
  1936. context.even = renderIndex % 2 === 0;
  1937. context.odd = !context.even;
  1938. if (this.multiTemplateDataRows) {
  1939. context.dataIndex = this._renderRows[renderIndex].dataIndex;
  1940. context.renderIndex = renderIndex;
  1941. }
  1942. else {
  1943. context.index = this._renderRows[renderIndex].dataIndex;
  1944. }
  1945. }
  1946. }
  1947. /**
  1948. * Gets the column definitions for the provided row def.
  1949. * @private
  1950. * @param {?} rowDef
  1951. * @return {?}
  1952. */
  1953. _getCellTemplates(rowDef) {
  1954. if (!rowDef || !rowDef.columns) {
  1955. return [];
  1956. }
  1957. return Array.from(rowDef.columns, (/**
  1958. * @param {?} columnId
  1959. * @return {?}
  1960. */
  1961. columnId => {
  1962. /** @type {?} */
  1963. const column = this._columnDefsByName.get(columnId);
  1964. if (!column) {
  1965. throw getTableUnknownColumnError(columnId);
  1966. }
  1967. return rowDef.extractCellTemplate(column);
  1968. }));
  1969. }
  1970. /**
  1971. * Adds native table sections (e.g. tbody) and moves the row outlets into them.
  1972. * @private
  1973. * @return {?}
  1974. */
  1975. _applyNativeTableSections() {
  1976. /** @type {?} */
  1977. const documentFragment = this._document.createDocumentFragment();
  1978. /** @type {?} */
  1979. const sections = [
  1980. { tag: 'thead', outlet: this._headerRowOutlet },
  1981. { tag: 'tbody', outlet: this._rowOutlet },
  1982. { tag: 'tfoot', outlet: this._footerRowOutlet },
  1983. ];
  1984. for (const section of sections) {
  1985. /** @type {?} */
  1986. const element = this._document.createElement(section.tag);
  1987. element.setAttribute('role', 'rowgroup');
  1988. element.appendChild(section.outlet.elementRef.nativeElement);
  1989. documentFragment.appendChild(element);
  1990. }
  1991. // Use a DocumentFragment so we don't hit the DOM on each iteration.
  1992. this._elementRef.nativeElement.appendChild(documentFragment);
  1993. }
  1994. /**
  1995. * Forces a re-render of the data rows. Should be called in cases where there has been an input
  1996. * change that affects the evaluation of which rows should be rendered, e.g. toggling
  1997. * `multiTemplateDataRows` or adding/removing row definitions.
  1998. * @private
  1999. * @return {?}
  2000. */
  2001. _forceRenderDataRows() {
  2002. this._dataDiffer.diff([]);
  2003. this._rowOutlet.viewContainer.clear();
  2004. this.renderRows();
  2005. this.updateStickyColumnStyles();
  2006. }
  2007. /**
  2008. * Checks if there has been a change in sticky states since last check and applies the correct
  2009. * sticky styles. Since checking resets the "dirty" state, this should only be performed once
  2010. * during a change detection and after the inputs are settled (after content check).
  2011. * @private
  2012. * @return {?}
  2013. */
  2014. _checkStickyStates() {
  2015. /** @type {?} */
  2016. const stickyCheckReducer = (/**
  2017. * @param {?} acc
  2018. * @param {?} d
  2019. * @return {?}
  2020. */
  2021. (acc, d) => {
  2022. return acc || d.hasStickyChanged();
  2023. });
  2024. // Note that the check needs to occur for every definition since it notifies the definition
  2025. // that it can reset its dirty state. Using another operator like `some` may short-circuit
  2026. // remaining definitions and leave them in an unchecked state.
  2027. if (this._headerRowDefs.reduce(stickyCheckReducer, false)) {
  2028. this.updateStickyHeaderRowStyles();
  2029. }
  2030. if (this._footerRowDefs.reduce(stickyCheckReducer, false)) {
  2031. this.updateStickyFooterRowStyles();
  2032. }
  2033. if (Array.from(this._columnDefsByName.values()).reduce(stickyCheckReducer, false)) {
  2034. this.updateStickyColumnStyles();
  2035. }
  2036. }
  2037. /**
  2038. * Creates the sticky styler that will be used for sticky rows and columns. Listens
  2039. * for directionality changes and provides the latest direction to the styler. Re-applies column
  2040. * stickiness when directionality changes.
  2041. * @private
  2042. * @return {?}
  2043. */
  2044. _setupStickyStyler() {
  2045. /** @type {?} */
  2046. const direction = this._dir ? this._dir.value : 'ltr';
  2047. this._stickyStyler = new StickyStyler(this._isNativeHtmlTable, this.stickyCssClass, direction, this._platform.isBrowser);
  2048. (this._dir ? this._dir.change : of())
  2049. .pipe(takeUntil(this._onDestroy))
  2050. .subscribe((/**
  2051. * @param {?} value
  2052. * @return {?}
  2053. */
  2054. value => {
  2055. this._stickyStyler.direction = value;
  2056. this.updateStickyColumnStyles();
  2057. }));
  2058. }
  2059. }
  2060. CdkTable.decorators = [
  2061. { type: Component, args: [{selector: 'cdk-table, table[cdk-table]',
  2062. exportAs: 'cdkTable',
  2063. template: CDK_TABLE_TEMPLATE,
  2064. host: {
  2065. 'class': 'cdk-table',
  2066. },
  2067. encapsulation: ViewEncapsulation.None,
  2068. // The "OnPush" status for the `MatTable` component is effectively a noop, so we are removing it.
  2069. // The view for `MatTable` consists entirely of templates declared in other views. As they are
  2070. // declared elsewhere, they are checked when their declaration points are checked.
  2071. // tslint:disable-next-line:validate-decorators
  2072. changeDetection: ChangeDetectionStrategy.Default,
  2073. },] },
  2074. ];
  2075. /** @nocollapse */
  2076. CdkTable.ctorParameters = () => [
  2077. { type: IterableDiffers },
  2078. { type: ChangeDetectorRef },
  2079. { type: ElementRef },
  2080. { type: String, decorators: [{ type: Attribute, args: ['role',] }] },
  2081. { type: Directionality, decorators: [{ type: Optional }] },
  2082. { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
  2083. { type: Platform }
  2084. ];
  2085. CdkTable.propDecorators = {
  2086. trackBy: [{ type: Input }],
  2087. dataSource: [{ type: Input }],
  2088. multiTemplateDataRows: [{ type: Input }],
  2089. _rowOutlet: [{ type: ViewChild, args: [DataRowOutlet, { static: true },] }],
  2090. _headerRowOutlet: [{ type: ViewChild, args: [HeaderRowOutlet, { static: true },] }],
  2091. _footerRowOutlet: [{ type: ViewChild, args: [FooterRowOutlet, { static: true },] }],
  2092. _contentColumnDefs: [{ type: ContentChildren, args: [CdkColumnDef,] }],
  2093. _contentRowDefs: [{ type: ContentChildren, args: [CdkRowDef,] }],
  2094. _contentHeaderRowDefs: [{ type: ContentChildren, args: [CdkHeaderRowDef,] }],
  2095. _contentFooterRowDefs: [{ type: ContentChildren, args: [CdkFooterRowDef,] }]
  2096. };
  2097. /**
  2098. * Utility function that gets a merged list of the entries in a QueryList and values of a Set.
  2099. * @template T
  2100. * @param {?} queryList
  2101. * @param {?} set
  2102. * @return {?}
  2103. */
  2104. function mergeQueryListAndSet(queryList, set) {
  2105. return queryList.toArray().concat(Array.from(set));
  2106. }
  2107. /**
  2108. * @fileoverview added by tsickle
  2109. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2110. */
  2111. /**
  2112. * Injection token that can be used to specify the text column options.
  2113. * @type {?}
  2114. */
  2115. const TEXT_COLUMN_OPTIONS = new InjectionToken('text-column-options');
  2116. /**
  2117. * Column that simply shows text content for the header and row cells. Assumes that the table
  2118. * is using the native table implementation (`<table>`).
  2119. *
  2120. * By default, the name of this column will be the header text and data property accessor.
  2121. * The header text can be overridden with the `headerText` input. Cell values can be overridden with
  2122. * the `dataAccessor` input. Change the text justification to the start or end using the `justify`
  2123. * input.
  2124. * @template T
  2125. */
  2126. class CdkTextColumn {
  2127. /**
  2128. * @param {?} _table
  2129. * @param {?} _options
  2130. */
  2131. constructor(_table, _options) {
  2132. this._table = _table;
  2133. this._options = _options;
  2134. /**
  2135. * Alignment of the cell values.
  2136. */
  2137. this.justify = 'start';
  2138. this._options = _options || {};
  2139. }
  2140. /**
  2141. * Column name that should be used to reference this column.
  2142. * @return {?}
  2143. */
  2144. get name() {
  2145. return this._name;
  2146. }
  2147. /**
  2148. * @param {?} name
  2149. * @return {?}
  2150. */
  2151. set name(name) {
  2152. this._name = name;
  2153. // With Ivy, inputs can be initialized before static query results are
  2154. // available. In that case, we defer the synchronization until "ngOnInit" fires.
  2155. this._syncColumnDefName();
  2156. }
  2157. /**
  2158. * @return {?}
  2159. */
  2160. ngOnInit() {
  2161. this._syncColumnDefName();
  2162. if (this.headerText === undefined) {
  2163. this.headerText = this._createDefaultHeaderText();
  2164. }
  2165. if (!this.dataAccessor) {
  2166. this.dataAccessor =
  2167. this._options.defaultDataAccessor || ((/**
  2168. * @param {?} data
  2169. * @param {?} name
  2170. * @return {?}
  2171. */
  2172. (data, name) => ((/** @type {?} */ (data)))[name]));
  2173. }
  2174. if (this._table) {
  2175. // Provide the cell and headerCell directly to the table with the static `ViewChild` query,
  2176. // since the columnDef will not pick up its content by the time the table finishes checking
  2177. // its content and initializing the rows.
  2178. this.columnDef.cell = this.cell;
  2179. this.columnDef.headerCell = this.headerCell;
  2180. this._table.addColumnDef(this.columnDef);
  2181. }
  2182. else {
  2183. throw getTableTextColumnMissingParentTableError();
  2184. }
  2185. }
  2186. /**
  2187. * @return {?}
  2188. */
  2189. ngOnDestroy() {
  2190. if (this._table) {
  2191. this._table.removeColumnDef(this.columnDef);
  2192. }
  2193. }
  2194. /**
  2195. * Creates a default header text. Use the options' header text transformation function if one
  2196. * has been provided. Otherwise simply capitalize the column name.
  2197. * @return {?}
  2198. */
  2199. _createDefaultHeaderText() {
  2200. /** @type {?} */
  2201. const name = this.name;
  2202. if (isDevMode() && !name) {
  2203. throw getTableTextColumnMissingNameError();
  2204. }
  2205. if (this._options && this._options.defaultHeaderTextTransform) {
  2206. return this._options.defaultHeaderTextTransform(name);
  2207. }
  2208. return name[0].toUpperCase() + name.slice(1);
  2209. }
  2210. /**
  2211. * Synchronizes the column definition name with the text column name.
  2212. * @private
  2213. * @return {?}
  2214. */
  2215. _syncColumnDefName() {
  2216. if (this.columnDef) {
  2217. this.columnDef.name = this.name;
  2218. }
  2219. }
  2220. }
  2221. CdkTextColumn.decorators = [
  2222. { type: Component, args: [{selector: 'cdk-text-column',
  2223. template: `
  2224. <ng-container cdkColumnDef>
  2225. <th cdk-header-cell *cdkHeaderCellDef [style.text-align]="justify">
  2226. {{headerText}}
  2227. </th>
  2228. <td cdk-cell *cdkCellDef="let data" [style.text-align]="justify">
  2229. {{dataAccessor(data, name)}}
  2230. </td>
  2231. </ng-container>
  2232. `,
  2233. encapsulation: ViewEncapsulation.None,
  2234. // Change detection is intentionally not set to OnPush. This component's template will be provided
  2235. // to the table to be inserted into its view. This is problematic when change detection runs since
  2236. // the bindings in this template will be evaluated _after_ the table's view is evaluated, which
  2237. // mean's the template in the table's view will not have the updated value (and in fact will cause
  2238. // an ExpressionChangedAfterItHasBeenCheckedError).
  2239. // tslint:disable-next-line:validate-decorators
  2240. changeDetection: ChangeDetectionStrategy.Default,
  2241. },] },
  2242. ];
  2243. /** @nocollapse */
  2244. CdkTextColumn.ctorParameters = () => [
  2245. { type: CdkTable, decorators: [{ type: Optional }] },
  2246. { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [TEXT_COLUMN_OPTIONS,] }] }
  2247. ];
  2248. CdkTextColumn.propDecorators = {
  2249. name: [{ type: Input }],
  2250. headerText: [{ type: Input }],
  2251. dataAccessor: [{ type: Input }],
  2252. justify: [{ type: Input }],
  2253. columnDef: [{ type: ViewChild, args: [CdkColumnDef, { static: true },] }],
  2254. cell: [{ type: ViewChild, args: [CdkCellDef, { static: true },] }],
  2255. headerCell: [{ type: ViewChild, args: [CdkHeaderCellDef, { static: true },] }]
  2256. };
  2257. /**
  2258. * @fileoverview added by tsickle
  2259. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2260. */
  2261. /** @type {?} */
  2262. const EXPORTED_DECLARATIONS = [
  2263. CdkTable,
  2264. CdkRowDef,
  2265. CdkCellDef,
  2266. CdkCellOutlet,
  2267. CdkHeaderCellDef,
  2268. CdkFooterCellDef,
  2269. CdkColumnDef,
  2270. CdkCell,
  2271. CdkRow,
  2272. CdkHeaderCell,
  2273. CdkFooterCell,
  2274. CdkHeaderRow,
  2275. CdkHeaderRowDef,
  2276. CdkFooterRow,
  2277. CdkFooterRowDef,
  2278. DataRowOutlet,
  2279. HeaderRowOutlet,
  2280. FooterRowOutlet,
  2281. CdkTextColumn,
  2282. ];
  2283. class CdkTableModule {
  2284. }
  2285. CdkTableModule.decorators = [
  2286. { type: NgModule, args: [{
  2287. imports: [CommonModule],
  2288. exports: EXPORTED_DECLARATIONS,
  2289. declarations: EXPORTED_DECLARATIONS
  2290. },] },
  2291. ];
  2292. /**
  2293. * @fileoverview added by tsickle
  2294. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2295. */
  2296. /**
  2297. * @fileoverview added by tsickle
  2298. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  2299. */
  2300. export { DataRowOutlet, HeaderRowOutlet, FooterRowOutlet, CDK_TABLE_TEMPLATE, CdkTable, CdkCellDef, CdkHeaderCellDef, CdkFooterCellDef, CdkColumnDef, BaseCdkCell, CdkHeaderCell, CdkFooterCell, CdkCell, CDK_ROW_TEMPLATE, BaseRowDef, CdkHeaderRowDef, CdkFooterRowDef, CdkRowDef, CdkCellOutlet, CdkHeaderRow, CdkFooterRow, CdkRow, CdkTableModule, STICKY_DIRECTIONS, StickyStyler, mixinHasStickyInput, TEXT_COLUMN_OPTIONS, CdkTextColumn };
  2301. //# sourceMappingURL=table.js.map