RTL Support
One property mirrors the whole grid. The two examples below share a columns array, a config object and a data source — they differ by dir and by nothing else, so everything you can see change is what dir changes.
Left to right — the baseline
The default. The pinned # column is frozen at the left edge and Status at the right, the vertical scrollbar sits on the right, and the columns run in array order. Scroll sideways and watch which two stay put.
Nothing here is RTL-specific, which is why it is on the page: it is the grid a reader already has.
Eight columns add up to more than the card is wide, so the body scrolls sideways — and two of them
do not move with it. # is frozen at the left edge and
Status at the right, because that is what pin: 'left' and
pin: 'right' mean here.
Note which edge the vertical scrollbar is drawn on, and where the pager's next button sits. Both move in the example below, and neither is configured anywhere.
Right to left — the same grid, mirrored
dir: 'rtl' and nothing else. Every column keeps its declaration and its place in the array; the axis they are laid out along is what flipped — pins, scrollbar, pager arrows and the arrow keys with it.
The columns array is the one above, unchanged — same order, same widths, same
pin: 'left' on #. Open both directory.component.ts tabs and
the only line that differs is this one:
dir: 'rtl',
# is now frozen at the right edge and Status at the
left. The pin did not change side — the sides changed places. 'left' and
'right' name the start and end of the reading direction, so one
column definition works in both without a branch.
The text is still English on purpose. dir moves the layout and translates nothing;
the strings are a separate switch, and the two are shown together on
Localisation.
What dir moves
config.dir: 'rtl' puts dir="rtl" on the grid's root element. Everything
below follows from that one attribute — there is no second set of RTL styles to opt into, and not
one column definition changes:
- Column order on screen. The first column in your array is painted at the
right and the last at the left. The array is untouched, so
columns[0]is still#. - Text alignment. Cell and header content is right-aligned by inheritance
rather than per cell — so a
cellTemplateof your own is aligned too, with no work. - The vertical scrollbar moves to the left edge of the body, and the horizontal one starts at the right, because that is where the content begins.
- Pinned columns swap edges — see below.
- Horizontal icons. The pager's first/previous/next/last arrows flip, and so do the row-expand caret and the group-toggle chevron. The sort chevron does not: it points up or down, and up is up in every direction.
- The arrow keys. ← moves to the next column, which is the one on your left. Keyboard navigation follows what is on screen, not the array index.
pin: 'left' and pin: 'right' are logical sides
They name the start and end of the reading direction, not the
physical edges of the screen. Under dir: 'rtl' a pin: 'left' column
freezes at the right-hand edge and a pin: 'right' column at the left-hand one — which
is what the two examples above show, from one array. The 1px seam at each frozen edge moves with
them — and so does the optional pin shadow, if you have turned one on.
This is the reason not to branch: an app that swaps 'left' for 'right'
when the language changes ends up pinning the wrong column, because the grid has already mirrored
them.
Direction is not translation
dir and messages are separate options, deliberately: plenty of products
need a right-to-left layout with English strings in it, and plenty of translations read left to
right. The second example above is Arabic-free for that reason — everything you can see change
came out of dir. Pair the two on
Localisation, where the same grid is shown with a
complete Arabic message set beside its French and English siblings.
Switching the whole app at runtime
config.dir is per grid. For an app that flips direction when the reader changes
language, set it once instead and every grid follows — none of them is re-created:
import { GridDirectionService, RTL_LOCALES } from '@gridcraft/angular';
const locale = inject(GridDirectionService);
locale.setDir(RTL_LOCALES.has(lang) ? 'rtl' : 'ltr');
RTL_LOCALES ships with the library and holds 'ar', 'fa' and
'ur', so the switch does not need its own list. Three levels are read, and the
highest one present wins:
config.diron the grid — an explicit per-grid answer.GridDirectionService.setDir()— the app-wide direction, at runtime.provideGridDefaults({ dir })— the app-wide starting value.
Both examples above declare config.dir, which is why the left-to-right one stays left
to right even when this whole site is switched to RTL. A grid that should follow the app simply
leaves dir out.
The page around the grid is yours
Setting config.dir does not touch <html> or
<body> — this page reads left to right while the second grid on it does not.
That keeps a single right-to-left grid embeddable in an otherwise left-to-right screen, and it
leaves the document's own dir your decision rather than the grid's.
Writing your own cellTemplate? Use CSS logical properties —
margin-inline-start, padding-inline-end,
border-inline-start — and it mirrors with the grid instead of needing a second
rule.
Direction options
| Property | Type | Default | Description |
|---|---|---|---|
| config.dir | 'ltr' | 'rtl' | 'ltr' | Per-grid layout direction. Sets dir on the grid root; columns, pins, scrollbar, icons and arrow keys all follow. Beats the app-wide value. |
| column.pin | 'left' | 'right' | unpinned | Freezes a column at the START ('left') or END ('right') of the reading direction — so one array pins correctly in both. See Column Pinning. |
| GridDirectionService.setDir() | (dir: 'ltr' | 'rtl') => void | — | App-wide direction at runtime. Every grid without an explicit config.dir follows immediately; nothing is re-created. |
| GridDirectionService.dir() | Signal<'ltr' | 'rtl'> | 'ltr' | The current app-wide direction, as a read-only signal. |
| provideGridDefaults({ dir }) | 'ltr' | 'rtl' | — | The app-wide starting direction, set once in your application config. setDir() and a per-grid config.dir both override it. |
| RTL_LOCALES | Set<string> | { 'ar', 'fa', 'ur' } | The bundled languages that read right to left — pair it with setDir() so a language switch does not keep its own list. |
| config.messages | Partial<GridMessages> | English | The grid's strings — a separate switch from dir. See Localisation. |