# @delon/* Component Documentation (English) This file contains aggregated content from all component docs. Total 92 components. --- # Documentation --- order: 20 title: Architecture type: Basic i18n: need-update --- NG-ALAIN scaffold is a front-end solution to support middleware and back-end application. It is based on [Angular](https://angular.io/) and [ng-zorro-antd](https://ng.ant.design/docs/introduce/en)(Angular implementation of Ant Design). The scaffold includes a set of common functionalities and business component libraries. It reduces lots of infrastructure development overwhelmingly and let you focus on business logic development. ## Architecture Diagram ![](https://github.com/ng-alain/delon/raw/master/_screenshot/architecture.png | width=700) **@delon/theme** Theme library of scaffold, we publish the theme library to NPM, which extremely simplifies upgrade effort. Theme library includes not only essential styles(CSS tool like bootstrap) required by NG-ALAIN, but also a set of common data rendering(Pipe) and service tools(page title, scroll bar, etc..), which are needed during daily development. **@delon/abc** Scaffold provides a default set of [business components](/components/), which abstract commonly used block/area in console service. We will continue to maintain and iterate the components. Provide more advanced abstract components than Ant Design base components to middleware and back-end business. **@delon/chart** [@delon/chart](/chart) chart library is customised based on G2. Providing commonly used chart suite in business development. It can be used alone or composed together to achieve more complex and fancy display effect. **@delon/form** [@delon/form](/form) follows standard of [JSON Schema](http://json-schema.org/) to build dynamic forms. **@delon/auth** [User Authentication](/auth) module is used for resolving steps to obtain, save and use authentication. **@delon/acl** [ACL](/acl) Access Control List is a very simple role based permission control. It can even reach the granularity of controlling the visibility of a button. **@delon/cache** Reduce the Http request by storing [cache](/cache) of the dictionary, city data, etc. into memory or persistence storage. **@delon/mock** [Mock](/mock) will intercept Angular HTTP request and return testing data. **@delon/util** [Daily Utilities](/util)。 **@delon/testing** Commonly used testing suite. **CLI Schematics** [CLI Tool](/cli) ## Directory Structure Schematic diagram of directory structure: ``` ├── _mock # Mock Data rule ├── src │   ├── app │   │   ├── core # Core │   │   │   ├── i18n │   │   │   ├── net │   │   │   │   └── default.interceptor.ts # Default HTTP interceptor │   │   │   ├── services │   │   │   │   └── startup.service.ts # Initialize project configuration │   │   │   └── index.ts # Core index.ts │   │   ├── layout # Core layout │   │   ├── routes │   │   │   ├── ** # Business directory │   │   │   └── routes.ts # Service routes registration │   │   ├── shared # Shared module │   │   │   ├── shared-imports.ts # A collection of frequently shared components │   │   │   └── index.ts # Shared index.ts │   │   ├── app.ts # Root component │   │   └── app.config.ts # Global config │   ├── assets # Local static resource │   ├── environments # Environment variable configuration │   ├── styles # Style directory └── └── style.less # Style guide entry ``` The following is a description and use of each directory and file. **_mock** The Mock data rules directory, if you create a project via [Command Line Tools](/cli), you can specify the `--mock` parameter to determine if the Mock function is required. **src/app/core/index.ts** Some core business services (for example: messaging, data access, etc.) **src/app/core/i18n** [Internationalization](/docs/i18n) data loading and processing related classes. If you create a project via [Command Line Tool](/cli), you can specify the `-di` parameter to determine whether internationalization support is required. **src/app/core/net** The default interceptor, where you can handle request parameters, request exceptions, business exceptions, and so on. **src/app/core/startup/startup.service.ts** Useful when you need to execute some remote data (eg application information, user information, etc.) before Angular launches. > It is a simple method and returns a `Promise` object, unless Angular will abort the launch unless `resolve(null)` is explicitly executed. **src/app/layout** Layout file code, refer to the page structure section. **src/app/routes** Business module, all your business code will be here. **src/app/shared/index.ts** A collection of some frequently shared components, The means that some third-party modules, custom components, and custom instructions that you need to use for the entire business module should exist here. In addition, for @delon & NG-ZORRO, two shared secondary module imports, `shared-delon.module.ts` and` shared-zorro.module.ts`. **src/app/app.config.ts** Global configuration for project. **src/environments** The application environment variable contains the following values: - `SERVER_URL` All HTTP request prefixes - `production` Whether the production environment - `useHash` Whether the route is useHash mode --- --- order: 70 title: en-US: Build & Deploy zh-CN: 构建和发布 type: Dev --- ## Construct When the project is developed, you can package your app with just one line of command: ```bash npm run build ``` NG-ALAIN itself is an Angular CLI project, so you can also complete more complex build requirements with [Build](https://angular.io/cli/build). After the package is successfully packaged, the `dist` folder will be generated in the root directory, which is to build the packaged file, including several static files such as `*.js`, `*.css`, `index.html`. ### JavaScript heap out of memory Avoid executing `ng build` when throw error **JavaScript heap out of memory**: ```json { "scripts": { "build": "node --max_old_space_size=5120 ./node_modules/@angular/cli/bin/ng build" } } ``` ### Environmental variables When you need to distinguish between development and deployment, and test environments, you can configure the corresponding parameters according to different environments through the `src/environments` folder. The configuration items can also be called directly in the application. At the same time, you need to configure the configuration items in `angular.json`. Finally, you can change the environment configuration through commands. ### Analyze the build file volume If the build file is large, you can optimize your code by building and analyzing the volume distribution of dependent modules with the `analyze` command. ```bash npm run analyze ``` View the analyze page: ```bash npm run analyze:view ``` ![](./assets/screenshot/bundle-size.png) ## Release When you are ready to deploy (release) your the app, you need only to publish the generated build artifacts - that is, the files in the `dist/browser` folder - to your cdn or static server. Note that the `index.html` is usually the entry page for your app and handles all missing file requests. You may need to change the import path of the page after determining the static of js and css. If your static resource is deployed to another domain name (such as a separate CDN address), you can specify a CDN address with the `--base-href` parameter. ```bash ng build --base-href=https://cdn.ng-alain.com/ ``` ### Routing strategy Angular front-end routing has two different strategies: `HashLocationStrategy` and `PathLocationStrategy`. The former is routed by appending a `#` before a path, and the front-end routing management is performed by [HTML5 History](//developer.mozilla.org/en-US/docs/Web/API/History_API), while the latter is similar, but the path does not have `#` appended before it. Through the configuration of the server, the specified URL can be accessed to the current page, enabling front-end routing management. If you want to use `PathLocationStrategy`, you need to replace `RouterModule.forRoot(routes, { useHash: true })` of `./src/app/routes/routes-routing.module.ts` with `RouterModule.forRoot(routes)`, since that is the default Angular behavior. You might notice that this can also be done by changing the `useHash` variable in the `src/environments/environment.*.ts` files, depending on your project configulation. Importantly, your server needs to be correctly configured, such that is returns the `index.html` for any 404 errors: Express server example: ```js app.use(express.static(path.join(__dirname, 'build'))); app.get('/*', function (req, res) { res.sendFile(path.join(__dirname, 'build', 'index.html')); }); ``` Egg server example: ```js // controller exports.index = function* () { yield this.render('App.jsx', { context: { user: this.session.user, }, }); }; // router app.get('home', '/*', 'home.index'); ``` For more questions about Angular routing, please read [official website](//angular.io/guide/router). ### Docker NG-ALAIN provides a complete image file for building Angular projects based on the nginx WEB service. Where nginx is a mirror using [nginx:1.13.5-alpine](https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine/Dockerfile), which basically satisfies NG-ALAIN The project's good operating environment, if there are more needs, you can easily specify *nginx.conf* with `docker run`. #### 1. Building the image Build a complete image of the runtime environment required by NG-ALAIN based on the Dockerfile. ```bash docker build -t ng-alain . ``` #### 2. Running **Based on compose (Recommended)** ```bash docker-compose up -d ``` The details can be modified with `docker-compose.yml`. **Based on imperative** ```bash docker run -d -p 80:80 --name alain ng-alain ``` Finally, you can visit:http://localhost/ #### 3. About SSL The Dockerfile provided by NG-ALAIN is relatively simple, and the most common use of real projects is support for SSL. Therefore, by default you can place the certificate in the `_nginx/ssl` directory and enable the `_nginx/default.conf` related SSL configuration item. Finally, add the `ports` node of `docker-compose.yml`: ``` - 443:443 ``` ### Container deployment Refer to [Angular Container Deployment](https://zhuanlan.zhihu.com/p/35688938). --- --- order: 40 title: Contributing type: Other --- The following is a set of guidelines for contributing to ng-alain. Please spend several minutes in reading these guidelines before you create an issue or pull request. ## Code of Conduct We have adopted a [Code of Conduct](https://github.com/ng-alain/delon/blob/master/CODE_OF_CONDUCT.md) that we expect project participants to adhere to. Please read the full text so that you can understand what actions will and will not be tolerated. ## Open Development All work on [ng-alain](https://github.com/ng-alain/ng-alain)、[delon](https://github.com/ng-alain/delon) happens directly. Both core team members and external contributors send pull requests which go through the same review process. ## Bugs We are using [GitHub Issues](https://github.com/ng-alain/ng-alain/issues) for bug tracing. The best way to get your bug fixed via [GitHub Issues](https://github.com/ng-alain/ng-alain/issues) and provide a reprduction with this [template](https://stackblitz.com/edit/ng-alain-setup). Before you reporting a bug, please make sure you've searched exists issues, and read our [FAQ](https://ng-alain.com/). ## Proposing a Change If you intend to change the public API or introduce new feature that via [GitHub Issues](https://github.com/ng-alain/ng-alain/issues). ## Your First Pull Request Working on your first Pull Request? You can learn how from this free video series: [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/ng-alain/ng-alain/labels/good%20first%20issues) that contain bugs or small features that have a relatively limited scope. This is a great place to get started. If you decide to fix an issue, please be sure to check the comment thread in case somebody is already working on a fix. If nobody is working on it at the moment, please leave a comment stating that you intend to work on it so other people don’t accidentally duplicate your effort. If somebody claims an issue but doesn’t follow up for more than two weeks, it’s fine to take over it but you should still leave a comment. ## Sending a Pull Request The core team is monitoring for pull requests. We will review your pull request and either merge it, request changes to it, or close it with an explanation. **Before submitting a pull request**, please make sure the following is done: 1. Run `yarn` in the repository root. 2. If you’ve fixed a bug or added code that should be tested, add tests! 3. Ensure the test suite passes (`npm run test`). 4. Make sure your code lints (`npm run lint`). Tip: Lint runs automatically when you `git commit`. 5. Make sure rebase your code to keep the history clean. 6. Make sure your commit message meet the [guidelines](https://github.com/ng-alain/delon/blob/master/CONTRIBUTING.md#-commit-message-guidelines) ## Development Workflow After cloning `ng-alain` or `delon`, run `yarn` to fetch its dependencies. Then, you can run several commands: ### delon 1. `npm run site` runs ng-alain.com website locally 2. `npm run lint` checks the code style 3. `npm run test` runs the complete test suite 5. `npm run release` build packages relases ### ng-alain 1. `npm run site` runs [demo site](https://ng-alain.surge.sh/) website locally 2. `npm run lint` checks the code style 3. `npm test` runs test suite 5. `npm run build` creates build of [demo site](https://ng-alain.surge.sh/) --- --- order: 5 title: Customize Theme type: Documents --- Ant Design allows you to customize some basic design aspects in order to meet the needs of UI diversity from business and brand, including primary color, border radius, border color, etc. ![](https://zos.alipayobjects.com/rmsportal/zTFoszBtDODhXfLAazfSpYbSLSEeytoG.png) ## Less variables We are using [Less](http://lesscss.org/) as the development language for styling. A set of less variables are defined for each design aspect that can be customized to your needs. ### Customize theme with schematics Run `ng add ng-alain`, set up custom theme file, then modified the file `src/styles/theme.less`. ## Official Themes We have some official themes, try them out and give us some feedback! - 🌑 Dark Theme (supported in 9+) - 📦 Compact Theme (supported in 9+) ### Method 1 In the style file `src/styles/theme.less`, change `default` to `dark` or `compact` to override theme variables. ```less // - `default` Default Theme // - `dark` 🌑 Dark Theme (supported in 9+) // - `compact` 📦 Compact Theme (supported in 9+) @import '@delon/theme/theme-default.less'; // ==========The following is the custom theme variable area========== // @primary-color: #f50; ``` ### Method 2 If the project does not use Less, you can include `dark.css` or `compact.css` in the CSS file or add to the `angular.json` config. CSS file: ```css @import "@delon/theme/dark.css"; ``` angular.json ```json { "build": { "options": { "styles": [ "node_modules/@delon/theme/dark.css" ] } } } ``` ## Switch Theming When using @angular/cli to configure themes, you must build applications for each theme. When you want to switch themes without reloading the application (like this website), you can use the following method to compile the theme into a style file, and switch at runtime: Note: Make sure theme variables exist in global styles, not in component scope styles, because component styles that have higher priority will prevent the theme override. 1. Install Dependencies ```bash npm i --save-dev ng-alain-plugin-theme ``` > [ng-alain-plugin-theme](https://github.com/ng-alain/plugin-theme) is to generate `color.less` and theme CSS files for NG-ALAIN. Add `theme` node in `ng-alain.json`: ```json { "$schema": "./node_modules/ng-alain/schema.json", "theme": { "list": [ { "theme": "dark" }, { "key": "dust", "modifyVars": { "@primary-color": "#F5222D" } } ] } } ``` Finally, run the following command: ```bash npx ng-alain-plugin-theme -t=themeCss ``` Two style files will be generated in `src/assets/style.dark.css` and `src/assets/style.dust.css`. 2. Switch Theme at Runtime Dynamically create a `link` tag, dynamically load style files into the application, and remove them otherwise. > You can also use the [theme-btn](https://github.com/ng-alain/delon/tree/master/packages/theme/theme-btn/) component directly. ```ts private readonly doc = inject(DOCUMENT); changeTheme(theme: 'default' | 'dark'): void { const el = this.doc.querySelector('#dark-theme'); if (theme === 'dark') { if (el) return; const style = this.doc.createElement('link'); style.type = 'text/css'; style.rel = 'stylesheet'; style.id = 'dark-theme'; style.href = 'assets/style.dark.css'; this.doc.head.appendChild(style); } else { el?.remove(); } } ``` Note: If you use `@delon/chart` or third-party component, you may need to manually modify the component to support the corresponding theme. ## Component development issues The above theme switching method is based on the fact that all Less style content is independent of `src/styles.less`. Sometimes, it is also defined in the component, like: ```ts @Component({ selector: 'app-dashboard-analysis', templateUrl: './analysis.component.html', styleUrls: ['./analysis.component.less'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class DashboardAnalysisComponent {} ``` ```less // analysis.component.less @import '@delon/theme/index'; :host ::ng-deep { color: @text-color; } ``` Because the styles defined in the component run independently under Angular, it is impossible to switch to the dark theme as a whole according to the introduction of `@import '@delon/theme/theme-compact.less';`, if you want the same in the component To use the dark series, you must: ```diff // analysis.component.less - @import '@delon/theme/index'; + @import '@delon/theme/theme-dark'; ``` Or, redefine for a component theme: ```less // analysis.component.less [data-theme='dark'] { :host ::ng-deep { // Redefining the dark theme } } ``` Or compact theme: ```less [data-theme='compact'] { :host ::ng-deep { // Redefining the compact theme } } ``` ## Related articles - [Component styles](/theme/component-styles) --- --- order: 4 title: Customize Widgets type: Documents --- ## Foreword `@delon/form` try our best to meet the needs of different environments, in addition to the built-in basic widgets (Some require manual registration), you can further expand the requirements in two ways: ## Custom widget in sf Please refer to [Custom Widget](/form/custom). ## Making widgets Making a set of widgets for project can lead to faster development work. ### How to making widget **Create widgets** A widget is a component. You only need to inherit `ControlWidget` to create a widget. For example: ```ts import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ControlWidget, DelonFormModule, SFWidgetProvideConfig } from '@delon/form'; export function withTestWidget(): SFWidgetProvideConfig { return { KEY: 'test', type: TestWidget }; } @Component({ selector: 'sf-tinymce', template: ` `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [DelonFormModule] }) class TestWidget extends ControlWidget implements OnInit { /* KEY value for registering widgets */ static readonly KEY = 'test'; // It is recommended to use `ngOnInit` to obtain the parameters required by the component. config: any; loadingTip: string; ngOnInit(): void { this.loadingTip = this.ui.loadingTip ?? 'Loading……'; this.config = this.ui.config ?? {}; } // reset can better solve the problem of new data required during the form reset process reset(value: string) { } change(value: string) { if (this.ui.change) this.ui.change(value); this.setValue(value); } } ``` **sf-item-wrap** Wrap your custom content in the template with the `sf-item-wrap` component, which encapsulates the form base elements internally. **Change detection** The widget is manually trigger changed detection during the rendering process. In most cases, the `ControlWidget` is well manage of changing detection. but the asynchronous operation may be encountered, you can call the `detectChanges()` method to trigger a change detection of the widget. ### Register Recommended to define a widget called `withXWidth` and return `SFWidgetProvideConfig` type, for example: ```ts export function withTestWidget(): SFWidgetProvideConfig { return { KEY: 'test', type: TestWidget }; } ``` Finally, register via `provideSFConfig`: ```ts provideSFConfig({ widgets: [ withTestWidget() ] }) ``` For more friendly maintenance, recommended to define a project-specific collection in the `shared` directory. If you are interested, please refer to [NG-ALAIN implementation](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/) or refer to [@delon/form/widgets/autocomplete](https://github.com/ng-alain/delon/tree/master/packages/form/widgets/autocomplete). ### Usage Just like other widgets, just specify the `widget` value, for example: ```json "test": { "type": "string", "ui": { "widget": "test", "data": "widget parameters" } } ``` --- --- order: 90 title: Default parameter type: Documents --- ng-alain provides a number of build modules, page templates, which have same option parameters, such as: `spec` for generating test pages, `flat` for flat directories, `inline-template` for inline template, etc. However, parameters can be preset via `angular.json`. ```json // angular.json { "schematics": { "ng-alain:module": { "routing": true }, "ng-alain:list": { "skipTests": false }, "ng-alain:edit": { "skipTests": false, "modal": true }, "ng-alain:view": { "skipTests": false, "modal": true }, "ng-alain:curd": { "skipTests": false }, "@schematics/angular:module": { "routing": true }, "@schematics/angular:component": { "skipTests": false, "flat": false, "inlineStyle": true, "inlineTemplate": false }, "@schematics/angular:directive": { "skipTests": false }, "@schematics/angular:service": { "skipTests": false } } } ``` You can execute the following command to get the parameter description: ```bash ng g ng-alain:list --help ``` --- --- order: 20 title: en-US: FAQ zh-CN: 常见问题 type: Other --- Please check the FAQ below before asking questions. ## Basic ### Expression Changed After It Has Been Checked Error Common mistakes under Angular, [this article](https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4) will help you understand why. ### Can't bind to 'formGroup' since it isn't a known property of 'form' Common mistakes under Angular, the use of Reactive Forms requires the introduction of `ReactiveFormsModule`, refer to [official documentation](https://angular.io/guide/reactive-forms). ### Why is the page not updated after the data is modified? The NG-ZORRO and @delon/* components work in OnPush mode by default. Mutate objects or arrays do not trigger Angular's change detection. Use the immutable method. ### How to use @delon daily build version NG-ALAIN provides a [delon-builds](https://github.com/ng-alain/delon-builds.git) repository as a daily build version. It's not the final stable version, but contains the latest fixed BUG, To use the latest features, you can create `delon.sh` in the root directory: ```bash #!/usr/bin/env bash set -e echo "Download latest @delon version" rm -rf delon-builds git clone --depth 1 https://github.com/ng-alain/delon-builds.git rm -rf node_modules/@delon rm -rf node_modules/ng-alain rsync -am delon-builds/ node_modules/ NG_ALAIN_VERSION=$(node -p "require('./node_modules/ng-alain/package.json').version") rm -rf delon-builds echo "Using ng-alain version: ${NG_ALAIN_VERSION}" ``` When you need to use the daily build version of @delon, you only need to run: ```bash bash delon.sh ``` > If in Windows environment, please use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) to execute Bash scripts. ## Installation ### Why can't I find the ng-zorro-antd/src/*.less style? Two situations: - Using `cnpm` to install dependencies, you will not be able to find style files. This is because `cnpm` is in the form of a soft link path, which causes the `ng-zorro-antd` folder name to change, so it is recommended to use the `yarn` install dependency package instead. - The `ng-zorro-antd` version is too old to cause some components to fail to load into the appropriate style ### How to use other mirror? Install the [nnrm](https://github.com/YunYouJun/nnrm/blob/main/README.zh-CN.md) plugin. ```bash # Install nnrm npm install -g nnrm # Switch Npm to Taobao mirror nnrm use taobao ``` ## Configuration ### How do I deploy the antd icon locally? First, the latest iconfont file can go to [this link](https://ant.design/docs/spec/download) ([mirror](http://ant-design.gitee.io/docs/spec/download)) Download. Finally, redefine the new path in `src/styles/theme.less`: ```less @icon-url: "/assets/iconfont"; ``` > Use an absolute path or CDN address. ### Missing locale data for the locale "zh-cn" For missing language imports, refer to [app.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/app.module.ts#L6-L25). ### How to deploy ng-alain.com documentation site in local Online documents will only retain the data of the last three major version numbers. If the version is low, you can view it through local deployment: ```bash git clone --depth 1 -b full https://github.com/ng-alain/archive-docs.git ng-alain-doc cd ng-alain-doc npm install npm start ``` ### Fix the abnormal display of the lower part of the 360 browser The built-in Chrome core of some 360 browsers is too low, resulting in some CSS3 not supported, you can manually increase `.browserslistrc` For more details, refer to [#2310](https://github.com/ng-alain/ng-alain/issues/2310#issuecomment-1299460266). --- --- order: 99 title: FAQ type: Documents --- ## path Many functions require passing `path` parameter, it uses `/` to represent form object path, following JSON Schema is an example: ```ts schema: SFSchema = { properties: { app: { type: 'string', title: 'APP', }, user: { type: 'object', properties: { name: { type: 'string', }, age: { type: 'number', }, } }, list: { type: 'array', items: { type: 'object', properties: { key: { type: 'string', }, } } } } }; ``` Following `path` are all valid: - `/app` - `/user/name` - `/list/0/key` 0 means array index Starting with `/` indicates to search from root path, otherwise, search from current path. ## Why some customized validations are not working in non-realtime validation? Because non-realtime validation (set `liveValidate:false`) doesn't revalidate every element, although it can be done, customized validation may involve asynchronized validation, cannot guarantee the order of execution, so non-realtime validation actually only validates JSON Schema itself. ## How to use Schema dynamically? There are two common scenarios: **1. Schema is limited by remote data after definition** ```ts @ViewChild('sf', { static: false }) sf: SFComponent; schema: SFSchema = { properties: { app: { type: 'string', title: 'Affiliated Application', ui: 'select', enum: [] } } }; ngOnInit() { this.http.get('/apps').subscribe(res => { this.schema.properties.app.enum = res; this.sf.refreshSchema(); }); } ``` **2. Remote Schema** ```ts schema: SFSchema = { properties: {} }; ngOnInit() { this.http.get('/schema').subscribe(res => this.sf.refreshSchema(res)); } ``` ## When to use `default`? `default` of Schema is used to set initialization, usually, need to provide `formData` when editting forms, but for adding forms, should rely on `default` to provide a better user friendly experience form to users. ## How to refresh a specific Schema? You can get a specific attribute of Schema by calling `getProperty` function, the attribute includes Schema data and Ui data, you can modify these data and re-render the Schema by calling `reset` function, for example: ```ts const statusProperty = this.sf.getProperty('/status')!; statusProperty.schema.enum = ['1', '2', '3']; statusProperty.widget.reset('2'); // Or manually trigger `detectChanges` // statusProperty.widget.detectChanges(); ``` If just only update a element value, then: ```ts this.sf.getProperty('/name')?.setValue('new name'); ``` ## Why can't verify `required` Added [strict](https://ajv.js.org/options.html#strict-mode-options) mode from Ajv 7.x, and the default is `true`, when the most basic `required` is not correct When verifying, the high probability is that the Schema contains information that does not conform to the Json Schema format. This can be verified through the `debug` mode: ```ts schema: SFSchema = { properties: { month: { type:'string', format:'month' } }, required: ['month'], ui: {debug: true} }; ``` Since the `format:'month'` here is not a Json Schema standard, you can get an error in the Console panel: ``` Error: unknown format "month" ignored in schema at path "#/properties/month" ``` To solve this problem, you can only set `strict` to `false` through the global configuration: ```ts // src/app/global-config.module.ts const alainConfig: AlainConfig = { sf: { ajv: {strict: false} } }; ``` ## How to toggle show or hide an element ```ts this.sf.getProperty('/mobile')?.setVisible(status).widget.detectChanges(); ``` --- --- order: 100 title: FAQ type: Documents --- ## How to auto-resize of the container? The G2 uses `window.addEventListener('resize', this.onResize)` to detect a change in parent dom element's size. So you need to manually monitor the change in the size of the container and call `chart.forceFit()` to force resize. [comment]: --- --- order: 10 title: en-US: Get Token zh-CN: 获取Token type: Documents --- Token's acquisition is divided into two categories, one is its own user authentication center, and the other is social login (essentially, it needs to go to its own user authentication center). ## User authentication center Generally speaking, the backend will provide a URL authentication address. We can send the user name and password entered by the user to the user authentication center just like a normal HTTP request. Finally, we will return a user information containing the Token. Therefore, you only need to use this [Token Information Store](/auth/set). ## Social login A complete social login takes about two steps: - Open a third-party authorization box - Get the authentication information after the callback and [Token Information Store](/auth/set) ### Turn on `SocialService` provides a `open()` method to open a login box. By default it is not registered in any module, because `@delon/auth` thinks that such a service is usually only generated during the login process, so there is no need to inject it globally; only need to use the `SocialService` component Inject it, of course you have to be willing to inject it in the root module. ```ts @Component({ providers: [ SocialService ] }) export class ProUserLoginComponent { constructor(private socialService: SocialService) {} } ``` Finally, use the `type` attribute to specify what form to open an authorization box: ```ts this.socialService.login(`//github.com/login/oauth/authorize?xxxxxx`, '/', { type: 'href' }); // Or use window.open to open the authorization window and subscribe to the results this.socialService.login(`//github.com/login/oauth/authorize?xxxxxx`, '/', { type: 'window' }).subscribe(res => { }); ``` ### Callback The callback page refers to the authentication information that is carried after the authorization is successful. In the past, you may write the authentication information directly to the cookie on the backend, and it will automatically write to the browser after the request ends. For the single page such as Angular, especially the front and rear ends. This approach becomes unachievable when deployed separately. So `@delon/auth` is to take the information from the callback page URL address as the source of the acquisition, of course it will be limited by the URL itself (eg length); but I want to be a long enough Token value, you can get Token, then get user information. You need to create a page for the callback, and the only thing the page has to do is call the `callback()` method on `ngOnInit` (for example: [callback.component.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/callback/callback.component.ts#L24)): ```ts // 1, default based on the current URL address this.socialService.callback(); // 2, without `{ useHash: true }` routing this.socialService.callback(`/callback?token=40SOJV-L8oOwwUIs&name=cipchk&uid=1`); // 3, with `{ useHash: true }` route this.socialService.callback(`/?token=40SOJV-L8oOwwUIs&name=cipchk&uid=1#/callback`); // 4, specify the `ITokenModel` object this.socialService.callback({ token: '123456789', name: 'cipchk', id: 10000, time: +new Date }); ``` `callback()` will automatically convert the URL to the effect of `ITokenModel`. > Note: The route for `{ useHash: true }` does not support callbacks in many third-party authorization boxes. If it is your own account system, you can still use the URL form in the third example. --- --- order: 10 title: Getting Started type: Basic --- ## Foreword NG-ALAIN is a production-ready solution for admin interfaces. Built on the design principles developed by [Ant Design](https://ant.design/), this project introduces higher level components; we have developed templates, components, and a corresponding design kit to improve the user and development experience for admin interfaces. **How to read document** This document uses the following conventions: - API - `[]` Input Property - `()` Output Event - `[()]` Two-way binding - `ng-content` Component content placeholders - `#tpl` Refers `` ## Preparation You will need [node](http://nodejs.org/) and [git](https://git-scm.com/). The project is based on [Typescript](https://www.tslang.com/), [Angular](https://angular.io/), [g2](http://g2.alipay.com/), [@delon](https://github.com/ng-alain/delon) and [ng-zorro-antd](https://ng.ant.design/). It would be helpful if you have pre-existing knowledge on those. ## Installation ### CLI (Recommend) Please make sure global Angular Cli is latest version via `ng version` command, please refer to [CLI Command Reference](https://angular.io/cli) for how to upgrade. ```bash npm install -g @angular/cli@19 ng new my-project --style less --routing cd my-project ng add ng-alain npm start # Or use HMR mode by: npm run hmr ``` **Multiple projects** ```bash yarn global add @angular/cli@19 ng new my-workspace --no-create-application --package-manager yarn cd my-workspace ng g application mgr --style less --routing ng add ng-alain yarn mgr:start # Or use HMR mode by: yarn run mgr:hmr ``` > Please refer to [Schematics](/cli) for more details. ### Clone the Git Repository ```bash git clone --depth=1 https://github.com/ng-alain/ng-alain.git my-project cd my-project npm install npm start # Or use HMR mode by: npm run hmr ``` > Note: Installing with the CLI is a clean scaffolding; using clone the git repository include all example pages. ## Scaffolding NG-ALAIN is a standard Angular CLI project that includes common routes for admins and demonstrates our component library. The project layout is as follows: ``` ├── _mock # Mock Data rule ├── src │   ├── app │   │   ├── core # Core module │   │   │   ├── i18n │   │   │   ├── net │   │   │   │   └── default.interceptor.ts # Default HTTP interceptor │   │   │   ├── services │   │   │   │   └── startup.service.ts # Initialize project configuration │   │   │   └── index.ts # Core module file │   │   ├── layout # Core layout │   │   ├── routes │   │   │   ├── ** # Business directory │   │   │   └── routes.ts # Service routes registration │   │   ├── shared # Shared module │   │   │   ├── shared-delon.module.ts # @Delon/* import of secondary shared modules │   │   │   ├── shared-zorro.module.ts # NG-ZORRO import of secondary shared modules │   │   │   └── shared-imports.ts # Shared module file │   │   ├── app.ts # Root component │   │   └── app.config.ts # @delon & ng-zorro global config │   ├── assets # Local static resource │   ├── environments # Environment variable configuration │   ├── styles # Style directory └── └── style.less # Style guide entry ``` ## Development ```bash npm start ``` This will automatically open [http://localhost:4200](http://localhost:4200). If you see the following page then you have succeeded. ![](./assets/screenshot/desktop.png | width=700) ## Environment Support - **Limited by Angular, no longer supports IE** - Modern browsers, [Browser support](https://angular.io/guide/browser-support) - Server-side Rendering - [Electron](https://electron.atom.io/) | [Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | [Electron](http://godban.github.io/browsers-support-badges/)
Electron | | --------- | --------- | --------- | --------- | --------- | --------- | | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions ## Contributing Please read our [CONTRIBUTING.md](/docs/contributing) first. If you'd like to help us improve NG-ZORRO, just create a [Pull Request](https://github.com/ng-alain/ng-alain/pulls). Feel free to report bugs and issues [here](https://github.com/ng-alain/ng-alain/issues). > If you're new to posting issues, we ask that you read [*How To Ask Questions The Smart Way*](http://www.catb.org/~esr/faqs/smart-questions.html)(This guide does not provide actual support services for this project!) and [How to Ask a Question in Open Source Community](https://github.com/seajs/seajs/issues/545) and [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) prior to posting. Well written bug reports help us help you! ## Need Help? For questions on how to use ng-alain, please post questions to [![Stack Overflow](https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.svg | width=140)](https://stackoverflow.com/questions/tagged/ng-alain) using the `ng-alain` tag. As always, we encourage experienced users to help those who are not familiar with `ng-alain`! ## DONATE If you would like to [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/cipchk) to NG-ALAIN. --- --- order: 1 title: Getting Started type: Documents --- ## Foreword @delon/auth is further processing of the authentication process, with a focus on three issues: + How to get the behavior of authentication information, such as: account, social login (Github, Facebook, etc.) + How to access authentication information and monitor changes in authentication information + When to use authentication information, for example: JWT @delon/auth does not care about the user interface, it only needs to convert the Token information to the `ITokenService` type when the login is successful. It will be stored in `localStorage` (by default). When you manipulate an HTTP request, it automatically adds Token information to `header` (or elsewhere). So, @delon/auth is not limited to ng-alain scaffolding, can be used with any Angular project. > @delon/auth just solution authentication process. You can use [@delon/acl](/acl) for permission control. ### Process - Get Token - Store Token - Send Token to the backend using the HTTP interceptor ## Definition ### Token @delon/auth thinks that the encrypted string that needs to be sent when requesting is called the Token value, whether it is JWT `Authorization` parameter or OAuth2 `access_token` value, which is also the value carried by each HTTP request. Therefore, the `ITokenModel` interface is used to represent authentication information and has only one `token` attribute. > Note: The token value must be a string type. ### Authentication Style There are currently two styles: Simple Web Token (using `SimpleTokenModel`) and Json Web Token (using `JWTTokenModel`) with parsing `payload` capabilities. The `ITokenModel` interface can be customized if you have special requirements. ## Usage Install `@delon/auth`: ```bash npm i -S @delon/auth ``` Configure the `provideAuth` environment in `app.config.ts`: ```typescript providers: [ // Indicates using JWT style and using `localStorage` to store Token provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authJWTInterceptor, defaultInterceptor])), provideAuth(withLocalStorage()), ] ``` ## AlainAuthConfig | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[store_key]` | `string` | `_token` | `localStorage` storage KEY value | ✅ | | `[token_invalid_redirect]` | `boolean` | `true` | Jump to login page if invalid, includs: invalid token, token expired (Only: JWT) | ✅ | | `[token_exp_offset]` | `number` | `10` | JWT token expiration time offset value (unit: second) | ✅ | | `[token_send_key]` | `string` | Token | Send token parameter name | ✅ | | `[token_send_template]` | `string` | `${token}` | Send a token template with a `${property name}` placeholder | ✅ | | `[token_send_place]` | `header,body,url` | `header` | Send token parameter position | ✅ | | `[login_url]` | `string` | `/login` | Login page routing address | ✅ | | `[ignores]` | `RegExp[]` | `[/\/assets\//]` | Ignore the list of URL addresses. In addition, you can also control whether to ignore through [ALLOW_ANONYMOUS](/auth/qa/en). | ✅ | | `[refreshTime]` | `number` | `3000` | Refresh time (unit: ms) | ✅ | | `[refreshOffset]` | `number` | `6000` | Offset value (unit: ms), it is recommended to set according to the multiple of `refreshTime` | ✅ | > You can override them via [Global Configuration](/docs/global-config). ## FAQ ### Solve the Token pollution problem of multiple NG-ALAIN projects in the same domain You can modify the `store_key` for distinction in each project through [Global Configuration](/docs/global-config). --- --- order: 1 title: Getting Started type: Documents --- ACL (Access Control List) is a very simple role-based permission control method, you can use in any Angular projects, a online [DEMO](//ng-alain.github.io/ng-alain/#/logics/acl) ## About ACLService The `ACLService` service class contains a set of methods based on role permissions. For a better coding experience ng-alain has multiple components or modules that depend on it, such as `st`, `MenuService` etc.. So, when you encounter the `acl` attribute, it means the parameter value of the [can](#ACLCanType) method. ## Usage Install `@delon/acl`: ```bash npm i -S @delon/acl ``` If you use Standalone, there is no need to import the `DelonACLModule` module, otherwise: ```typescript import { DelonACLModule } from '@delon/acl'; @NgModule({ imports: [ DelonACLModule ] }) export class AppModule { } ``` ## API ### Parameters | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[guard_url]` | `string` | Router URL when guard fail | `/403` | ✅ | | `[preCan]` | `(roleOrAbility: ACLCanType) => ACLType` | `can` before execution callback | - | ✅ | > You can override them via [Global Configuration](/docs/global-config). ### ACLService | Name | Description | |------|-------------| | `[change]` | Listen for ACL change notifications | | `[data]` | Get all ACL data | | `setFull(val: boolean)` | Whether to super administrator | | `set(value: ACLType)` | Set current user role or permission (automatic override) | | `setRole(roles: string[])` | Set current user role (automatic override) | | `setAbility(abilities: (number | string)[])` | Set current user permission (automatic override) | | `add(value: ACLType)` | Add role or permission to the current user | | `attachRole(roles: string[])` | Attach a role to the current user | | `attachAbility(abilities: (number | string)[])` | Attach a permission to the current user | | `removeRole(roles: string[])` | Remove the role for the current user | | `removeAbility(abilities: (number | string)[])` | Remove the permission for the current user | | `can(roleOrAbility: ACLCanType)` | Whether the current user has a role | | `canAbility(ability: ACLCanType)` | Whether the current user has a permission | ### ACLCanType ```ts type ACLCanType = number | number[] | string | string[] | ACLType ``` ### ACLType | Name | Type | Summary | Default | |------|------|---------|---------| | `[role]` | `string[]` | List of role | - | | `[ability]` | `number[], string[]` | List of permission | - | | `[mode]` | `allOf, oneOf` | `allOf` Must be valid against all of the given permission.
`oneOf` Must be valid against exactly one of the given permission. | `oneOf` | | `[except]` | `boolean` | Whether it's except, when the result is `true`, it means unauthorized | `false` | --- --- order: 1 title: Getting Started type: Documents --- ## Foreword `@delon/mock` is a simulation data generator to help the front-end to develop and prototype separate from the back-end progress and reduce some monotony particularly while writing automated tests. **Features** - All of the Angular projects - Unobtrusive - Simple usage - Support [@faker-js/faker](https://github.com/faker-js/faker) ## Usage Install `@delon/mock` from `yarn`. ```bash npm i --save-dev @delon/mock ``` Please refer to [global-config.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/global-config.module.ts#L26-L30) import the [Mock Rule data](/mock/rule). ### MockOptions > You can override them via [Global Configuration](/docs/global-config). | Property | Type | Default | Description | Global Config | |----------|-------------|------|---------|---------------| | `[data]` | `any` | - | Mock data rule | ✅ | | `[delay]` | `number` | `300` | Request delay, unit is milliseconds | ✅ | | `[force]` | `boolean` | `false` | Whether to force all requests to Mock, `true` means to return a 404 error directly when the requested URL does not exist, `false` means to send a real HTTP request when the request is missed | ✅ | | `[log]` | `boolean` | `true` | Whether to print Mock request information, make up for the browser without Network information; it will output [👽Mock] when hit | ✅ | | `[copy]` | `boolean` | `true` | Whether to return copy data | ✅ | ### Why is it only valid for development environment? Mock is not real data, and most scenarios are for development local or test environments; therefore, Mock modules and rule data should not be included in the production environment. Of course, you can also put the `provideMockConfig` of `environment.ts` under `environment.prod.ts` so that the production environment also uses this rule, just like https://ng-alain.github.io/ng- Like alain/, some mock requests are needed to ensure the environment runs. ```ts import { provideMockConfig } from '@delon/mock'; import * as MOCKDATA from '../../_mock'; export const environment = { providers: [provideMockConfig({ data: MOCKDATA })], } as Environment; ``` --- --- order: 1 title: Getting Started type: Documents --- `@delon/util` is a collection of tool functions. --- --- order: 1 title: Getting Started type: Documents --- @delon/form is a dynamic build form based on the [JSON Schema](http://json-schema.org/) standard. ## Features - Compliance with the JSON Schema standard - Based on the ng-zorro-antd library - Built on the design principles developed by Ant Design - Twenty different widgets - Customizable widgets - No third-party dependencies, so applicable to all antd projects ## How to read document This document uses the following conventions: - With `schema.` prefix is JSON Schema properties - With `ui.` prefix is UI properties - Some widget data sources are divided into **static** and **realtime** - **Static** refers to the `schema.enum` value, which is JSON Schema standard, and limited to the array format `any[]` - **Real-time** refers to the `ui.asyncData` value, which is not JSON Schema standard, the format `(input?: any) => Observable` ## Usage Install `@delon/form` from `yarn`. ```bash npm i -S @delon/form ``` Import `DelonFormModule` in to your root `AppModule`. ```typescript import { DelonFormModule } from '@delon/form'; @NgModule({ imports: [ DelonFormModule.forRoot() ] }) export class AppModule { } ``` Although the default `@delon/form` validator is [ajv](https://ajv.js.org/), you can override `SchemaValidatorFactory` to use other validator libraries. **Global Configuration** Please refer to [Global Configuration](/docs/global-config), the members are as follows: | Property | Description | Type | Default | |----------|-------------|------|---------| | `[ajv]` | [ajv](https://github.com/ajv-validator/ajv/blob/master/docs/api.md#options) options | `Ajv.Options` | - | | `[ingoreKeywords]` | Whether to ignore data type validator ([all types](https://github.com/ng-alain/delon/blob/master/packages/form/src/errors.ts#L4)) | `string[]` | `[ 'type', 'enum' ]` | | `[liveValidate]` | Whether to live validate | `boolean` | `true` | | `[autocomplete]` | autocomplete value of this form | `on,off` | `null` | | `[firstVisual]` | Whether to show visual error immediately | `boolean` | `false` | | `[onlyVisual]` | Whether only show visual error not include text, and cancel the error text spacing | `boolean` | `false` | | `[errors]` | Customize error messages | `{ [ key: string ]: string }` | `ERRORSDEFAULT` | | `[ui]` | Default global ui property | `SFUISchemaItem` | - | | `[size]` | Size of the all angular element | `default,large,small` | - | | `[button]` | Submit button of the form | `SFButton` | `{submit:'提交',submit_type:'primary',reset:'重置',reset_type:'default'}` | | `[uiDateStringFormat]` | Date widget default format | `string` | `yyyy-MM-dd HH:mm:ss` | | `[uiDateNumberFormat]` | Date widget default format | `string` | `T` | | `[uiTimeStringFormat]` | Time widget default format | `string` | `HH:mm:ss` | | `[uiTimeNumberFormat]` | Time widget default format | `string` | `T` | | `[uiEmailSuffixes]` | Specify the default Email suffix for `format: 'email'` | `string[]` | `['qq.com', '163.com', 'gmail.com', '126.com', 'aliyun.com']` | | `[delay]` | Whether to delay rendering, should be manually call `refreshSchema()` | `boolean` | `false` | For example, Build a email and name form: ```ts @Component({ selector: 'app-home', template: ` ` }) export class HomeComponent { schema: SFSchema = { properties: { email: { type: 'string', title: 'Email', format: 'email', maxLength: 20 }, name: { type: 'string', title: 'Name', minLength: 3 } } }; submit(value: any) { } } ``` ## API ### sf | Property | Description | Type | Default | |----------|-------------|------|---------| | `[layout]` | layout of the form | `horizontal,vertical,inline` | `horizontal` | | `[schema]` | **Required** JSON Schema | `SFSchema` | - | | `[ui]` | UI Schema | `SFUISchema` | - | | `[formData]` | Default form values | `any` | - | | `[mode]` | Form type mode | `default,search,edit` | `default` | | `[button]` | Submit button of the form | `SFButton, 'none'` | `{}` | | `[firstVisual]` | Whether to show visual error immediately | `boolean` | `true` | | `[liveValidate]` | Whether to live validate | `boolean` | `true` | | `[autocomplete]` | autocomplete value of this form | `on,off` | `null` | | `[disabled]` | Whether to disabled status | `boolean` | `false` | | `[loading]` | Whether to load status,when `true` reset button is disabled status, submit button is loading status | `boolean` | `false` | | `[noColon]` | Whether to not display `:` after label text. | `boolean` | `false` | | `[compact]` | Whether compact style | `boolean` | `false` | | `[expandable]` | Whether to enable expand/collapse | `boolean` | `false` | | `[expanded]` | Expand/Collapse state, support two-way binding | `boolean` | `false` | | `[cleanValue]` | Whether to clean up data for undefined Schema | `boolean` | `false` | | `[delay]` | Whether to delay rendering, should be manually call `refreshSchema()` | `boolean` | `false` | | `(formChange)` | Callback when data changes | `EventEmitter<{}>` | - | | `(formValueChange)` | Callback when value changes | `EventEmitter` | - | | `(formSubmit)` | Callback when submitting the form | `EventEmitter<{}>` | - | | `(formReset)` | Callback when resetting the form | `EventEmitter<{}>` | - | | `(formError)` | Callback when form check | `EventEmitter` | - | ### SFButton | Property | Description | Type | Default | |----------|-------------|------|---------| | `[submit]` | Submit text of button | `string` | `提交` | | `[submit_type]` | Submit type of button | `string` | `primary` | | `[submit_icon]` | Submit icon of button | `SFButtonIcon` | - | | `[reset]` | Reset text of button | `string` | `重置` | | `[reset_type]` | Reset type of button | `string` | `default` | | `[reset_icon]` | Reset icon of button | `SFButtonIcon` | - | | `[search]` | Search text of button | `string` | `搜索` | | `[edit]` | Edit text of button | `string` | `保存` | | `[render]` | Button layout | `SFRenderButton` | - | ### SFValueChange | Property | Description | Type | Default | |----------|-------------|------|---------| | `[value]` | Always return complete data | `SFValue` | - | | `[path]` | Current triggered path | `string, null` | `null` | | `[pathValue]` | Current path value | `SFValue` | - | ### Methods | Method Name | Description | Return Value | |-------------|-------------|--------------| | `valid` | Form is valid | `boolean` | | `value` | The current value of the form | `any` | | `refreshSchema` | Refresh JSON Schema | `void` | | `reset` | Resets the form | `void` | | `validator` | Manually verify a form | `void` | | `getProperty` | Get a form property via path | `FormProperty` | | `getValue` | Get value via path | `any` | | `setValue` | Set value via path, should be throw error when invalid path | `this` | | `setDisabled` | Set `disabled` status via path, should be throw error when invalid path | `this` | | `setRequired` | Set `required` status via path, should be throw error when invalid path | `this` | | `updateFeedback` | Set feedback status via path | `this` | > **Note:** All paths are separated by `/`, for example: `/user/name`, `/arr/0/name`. ### Button **Notice** - Manually add button when value is `null` or `undefined`, but keeping container element. - Manually add button when value is `none`, but removed container element. - When using fixed `label` width. Default is center when `render.class` is not spacifid. **Custom Button** **NOTICE:** Must be setting `button` value is `null`. ```html ``` ## FAQ ### What is mode `mode` is just a quick action, **which has the highest priority**, rules: - `default` default mode, do nothing - `search` search mode, automatically set `layout: inline`、`firstVisual: false`、`liveValidate: false`、`button.submit: 'Search'` - `edit` edit mode, automatically set `layout: horizontal`、`firstVisual: false`、`liveValidate: true`、`button.submit: 'Save'` ### Schema Internationalization `sf` doesn't support any Schema internationalization, this is because Schema itself is a group of JSON values, you can just provide different versions of languages to support internationalization. --- --- order: 1 title: Getting Started type: Documents --- Chart provides the well-designed abstract chart components based on the [G2](https://antv.alipay.com/zh-cn/g2/3.x/index.html). These components provide the ability to use with complex mixed view or just use along for common business usage. ## Usage ### G2 class library loading By default, the class library CDN address has been specified in [Global Configuration](/docs/global-config): ```ts // global-config.module.ts const alainConfig: AlainConfig = { chart: { // The following is the default configuration. If the project cannot be accessed from the Internet, you can directly use the `./assets***` path for the dependent package according to the `angular.json` configuration libs: [ 'https://gw.alipayobjects.com/os/lib/antv/g2/4.1.4/dist/g2.min.js', 'https://gw.alipayobjects.com/os/lib/antv/data-set/0.11.7/dist/data-set.js', ], }, }; export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ provideAlainConfig(alainConfig) ] }; } } ``` Of course, you can also directly import the CDN address in `index.html`, for example: ```html ``` You can also configure the `assets` (About [assets](https://angular.io/guide/workspace-config#assets-configuration) Document) option in `angular.json` to obtain the G2 library from `node_modules`, for example: ```json "assets": [ { "glob": "**/*", "input": "./node_modules/@antv/g2/dist", "output": "/@antv/g2/" }, { "glob": "**/*", "input": "./node_modules/@antv/data-set/dist", "output": "/@antv/data-set/" } ] ``` Finally modify the `libs` parameter of the global configuration: ```ts // global-config.module.ts const alainConfig: AlainConfig = { chart: { libs: [ './assets/@antv/g2/g2.min.js', './assets/@antv/data-set/data-set.js', ], }, }; ``` ### Import module ```ts // shared.module.ts import { G2BarModule } from '@delon/chart/bar'; @NgModule({ imports: [ G2BarModule ], exports: [ G2BarModule ] }) ``` ## Custom G2 components Use the [g2-chart](/chart/custom) component to better implement custom charts. ## Configure chart theme Configure the chart theme for all G2, but only provide interfaces. For the configuration chart theme parameters, please refer to [G2 website](https://g2.antv.vision/zh/docs/manual/tutorial/theme). ```ts // global-config.module.ts const alainConfig: AlainConfig = { chart: { theme: 'dark' }, }; export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ provideAlainConfig(alainConfig) ] }; } } ``` --- --- order: 1 title: Getting Started type: Documents --- ## Foreword Using ng-alain scaffolding should as use as possible the `ng` command set provided by the Angular CLI to build, upgrade, etc., ng-alain also implemented some cool things: - Build empty scaffolding with [ng add](/cli/add) - Use [ng g](/cli/generate) to build modules, business pages - Pluggable [plugin](/cli/plugin) ## Installation We don't recommend directly cloning the git repository, but instead using `ng add` to build the ng-alain project, there are a few simple steps: 1. Create an empty angular project ```bash # Make sure you are using the latest version of Angular cli ng new demo --style less ``` 2, add ng-alain scaffolding ```bash ng add ng-alain --defaultLanguage=en ``` > If you any querstion, please refer to [FAQ](/docs/faq) 3, running ```bash ng serve ``` ## How to upgrade Suggest Star or Watch [source code](https://github.com/ng-alain/ng-alain) repository to help you better understand change log. please refer to [upgrade](/docs/upgrade). --- --- order: 1 title: Getting Started type: Documents --- `@delon/theme` is the only must be imported to ng-alain scaffold. It contains a lot of [style parameters](/theme/global) and several generalities [services](/theme/menu), [pipes](/theme/date). ## Style ng-alain defaults to using less as the style language, we recommend that you learn about the features of [less](http://lesscss.org/) before using it or sometimes when you have some questions. If you want to get a basic knowledge of CSS or look for properties usage, you can refer to the [MDN doc] (https://developer.mozilla.org/en-US/docs/Web/CSS/Reference). ## Layout Scaffolding include two layouts: [default layout](/theme/layout-default), [blank layout](/theme/layout-blank), scaffolding does not contain these style files, it's in the `@delon/theme` library. ## Scaffold Style You can use the toolset provided by ng-alain to adjust spacing, color, size, borders, etc. It's a set of like bootstrap style tools. Or customize your styles with [theme.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles/theme.less), which will work in global applications, in the style development process, there are two prominent problems: - Global Pollution - CSS selectors are globally valid. Selectors with the same name in different files will be built together, and the former will be overrided by the latter. - Complex Selector - in order to avoid the above problem, we have to be careful when writing styles. The increase in flags for range restriction will lead to a growing class name, besides that, naming style confusion in multi person development and an increasing number of selectors on an element is hard to avoid. We should use component `styles` property to create component styles. For how use Angular styles, please refer to [About Angular Style Packaging](https://zhuanlan.zhihu.com/p/31235358). ## Style file category In a project, style files can be divided into different categories depending on their function. ### theme.less Global style file, where you can make some common settings. ### Tools Please refer to [Tools](/theme/tools)。 ### Page level Specific page-related style, such as [monitor.component.less](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/dashboard/monitor/monitor.component.less), the content is only related to the content of this page. Under normal circumstances, if it is not particularly complex page, with the previous global style and tools style, there should be little to write. ## How to override parameters We are using [Less](http://lesscss.org/) as the development language for styling. A set of less variables are defined for each design aspect that can be customized to your needs. The changes parameters put into the [theme.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles/theme.less) LESS file, all parameters include: - [Global Parameters](/theme/global) - [Default Layout](/theme/layout-default) - [Blank Layout](/theme/layout-blank) Please report an issue if the existing list of variables is not enough for you. --- --- order: 90 title: en-US: Global Configuration zh-CN: 全局配置项 type: Dev --- We add support of **global configuration** to many components. You can define the default behavior of the component through global configuration, thus reducing the code that needs to be written in the template, and support changing global config at runtime. ## How to Use? If you want to provide default configurations to some components, please use `provideAlain` function. object providing implements interface `AlainProvideOptions` For example: ```typescript // global-config.module.ts import { AlainConfig, AlainProvideLang } from '@delon/util/config'; import { ICONS } from '../style-icons'; import { ICONS_AUTO } from '../style-icons-auto'; const defaultLang: AlainProvideLang = { abbr: 'zh-CN', ng: ngLang, zorro: zorroLang, date: dateLang, delon: delonLang }; const alainConfig: AlainConfig = { st: { ps: 3 }, }; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, icons: [...ICONS_AUTO, ...ICONS] }) ] }; ``` These global configuration would be injected into a service named `AlainConfigService` and gets stored. ## About NG-ZORRO Please refer to NG-ZORRO Website [Documentation](https://ng.ant.design/docs/global-config/en). --- --- type: Theme order: 1 title: Global Parameters --- ## Public | Name | Default | Description | | --- | --- | --- | | `@layout-gutter` | `8px` | Antd layout spacing (unchageable) | | `@font-size-base` | `14px` | Antd font size (unchageable) | | `@primary-color` | Blue | antd primary color | | `@mobile-min` | `768px` | PC of width | | `@mobile-max` | `767px` | Mobile of width | | `@text-xs` | `@font-size-base - 2` | Text of xs size | | `@text-sm` | `@font-size-base + 0` | Text of sm size | | `@text-md` | `@font-size-base + 2` | Text of md size | | `@text-lg` | `@font-size-base + 4` | Text of lg size | | `@text-xl` | `@font-size-base + 8` | Text of xl size | | `@text-xxl` | `@font-size-base + 12` | Text of xxl size | | `@icon-sm` | `@font-size-base * 2` | Icon of sm size | | `@icon-md` | `@font-size-base * 4` | Icon of md size | | `@icon-lg` | `@font-size-base * 6` | Icon of lg size | | `@icon-xl` | `@font-size-base * 8` | Icon of xl size | | `@icon-xxl` | `@font-size-base * 10` | Icon of xxl size | | `@h1-font-size` | `32px` | h1 font size | | `@h2-font-size` | `24px` | h2 font size | | `@h3-font-size` | `20px` | h3 font size | | `@h4-font-size` | `16px` | h4 font size | | `@h5-font-size` | `14px` | h5 font size | | `@h6-font-size` | `12px` | h6 font size | | `@enable-all-colors` | `false` | Turn on background, text color
eg: `.bg-teal`、`.text-teal` | | `@modal-sm` | `300px` | Small modal | | `@modal-md` | `500px` | Medium modal | | `@modal-lg` | `900px` | Large modal | | `@modal-lg` | `1200px` | Extra large modal | | `@drawer-sm` | `300px` | Small drawer | | `@drawer-md` | `500px` | Medium drawer | | `@drawer-lg` | `900px` | Large drawer | | `@drawer-lg` | `1200px` | Extra large drawer | | `@drawer-sm-height` | `200px` | Small drawer for height | | `@drawer-md-height` | `400px` | Medium drawer for height | | `@drawer-lg-height` | `600px` | Large drawer for height | | `@drawer-xl-height` | `800px` | Extra large drawer for height | | `@code-border-color` | `#eee` | `` border color | | `@code-bg` | `#f7f7f7` | `` background color | | `@widths` | `xs @layout-gutter * 10`
`sm @layout-gutter * 20`
`md @layout-gutter * 30`
`lg @layout-gutter * 40`
`xl @layout-gutter * 50`
`xxl @layout-gutter * 50` | width | | `@border-radius-md` | `4px` | Medium border rounded corner | | `@border-radius-lg` | `6px` | Large border rounded corner | | `@masonry-column-gap` | `@layout-gutter * 2` | CSS waterfall flow column and column spacing | | `@scrollbar-enabled` | `true` | Enable landscaping scrollbars | | `@scrollbar-width` | `6px` | Scroll bar width | | `@scrollbar-height` | `6px` | Scroll bar height | | `@scrollbar-track-color` | `rgba(0, 0, 0, 0.3)` | Scrollbar track color | | `@scrollbar-thumb-color` | `transparent` | Scrollbar thumb color | | `@scrollbar-table-enabled` | `false` | Enable landscaping scrollbars of nz-table | | `@rtl-enabled` | `false` | Wheter support RTL | | `@enabled-util-align` | `true` | Whether to enable tools align | | `@enabled-util-border` | `true` | Whether to enable tools border | | `@enabled-util-code` | `true` | Whether to enable tools code | | `@enabled-util-color` | `true` | Whether to enable tools color | | `@enabled-util-display` | `true` | Whether to enable tools display | | `@enabled-util-float` | `true` | Whether to enable tools float | | `@enabled-util-icon` | `true` | Whether to enable tools icon | | `@enabled-util-img` | `true` | Whether to enable tools img | | `@enabled-util-position` | `true` | Whether to enable tools position | | `@enabled-util-overflow` | `true` | Whether to enable tools overflow | | `@enabled-util-responsive` | `true` | Whether to enable tools responsive | | `@enabled-util-spacing` | `true` | Whether to enable tools spacing | | `@enabled-util-text` | `true` | Whether to enable tools text | | `@enabled-util-width` | `true` | Whether to enable tools width | | `@enabled-util-scrollbar` | `true` | Whether to enable tools scrollbar | | `@enabled-util-other` | `true` | Whether to enable tools other | ## Ng patch ### General | Name | Default | Description | | --- | --- | --- | | `@preserve-white-spaces-enabled` | `true` | Fixed between buttons spacing when enabled [preserveWhitespaces](https://angular.io/api/core/Component#preserveWhitespaces) is true | | `@preserve-sf-and-st-spaces` | `16px` | Spacing between `sf` and `st` | | `@preserve-buttons-spaces` | Spacing between `button` and `button`(incluldes: `button`,`button-group`,`popconfirm`) | | `@router-animation-enabled` | `false` | Whether to enable animation when route changing | | `@router-animation-duration` | `antFadeIn` | Route switching animation name | | `@router-animation-duration` | `1s` | Animation duration | ## Zorro patch ### General | Name | Default | Description | | --- | --- | --- | | `@forced-turn-off-nz-modal-animation-enabled` | `false` |Forced to turn off `nz-modal` animation | ### Form | Name | Default | Description | | --- | --- | --- | | `@form-state-visual-feedback-enabled` | `false` | Turn on visual feedback of form invalid elements | | `@search-form-bg` | `#fbfbfb` | Background color of simple search form | | `@search-form-radius` | `4px` | Border rounded corner of simple search form | ### Table By `nz-table`。 | Name | Default | Description | | --- | --- | --- | | `@nz-table-img-radius` | `4px` | Image rounded in the table | | `@nz-table-img-margin-right` | `4px` | Image margin-right in the table | | `@nz-table-img-max-width` | `32px` | Image maximum width in the table | | `@nz-table-img-max-height` | `32px` | Image maximum height in the table | | `@nz-table-even-background` | `none` | Even background color in the table | | `@nz-table-rep-max-width` | `@mobile-max` | Triggering table response when mobile screen | | `@nz-table-rep-header-background` | `@border-color-split` | Table responsive: title background color | | `@nz-table-rep-even-background` | `#f9f9f9` | Table responsive: even background color | | `@nz-table-rep-padding-vertical` | `8px` | Table responsive: Cell vertical spacing | | `@nz-table-rep-padding-horizontal` | `8px` | Table responsive: Cell horizontal spacing | | `@nz-table-rep-column-name-width` | `100px` | Table responsive: Column name maximum width | | `@nz-table-rep-column-name-text-align` | `right` | Table responsive: Column name text alignment | | `@nz-table-rep-column-name-padding-right` | `right` | Table responsive: Column name right spacing | | `@nz-table-rep-column-name-color` | `rgba(0, 0, 0, 0.5)` | Table responsive: Column name color | ## Widgets | Name | Default | Description | | --- | --- | --- | | `@hafl-enabled` | `true` | Whether hafl image | | `@abs-enabled` | `true` | Whether abs element| | `@masonry-enabled` | `true` | Whether css masonry | | `@setting-drawer-enabled` | `true` | Whether setting drawer css | | `@search__form-enabled` | `true` | Pro style search form, [DEMO](https://ng-alain.surge.sh/) | --- --- order: 3 title: Granular permissions type: Documents --- ## Foreword Many times you need to control the permissions of a button. `@delon/acl` provides an `acl` directive that allows you to control the permissions of a button, table, list, etc. ## Principle `[acl]` adds a `acl__hide` style to the target element by default, and hides the unauthorized element with `display: none`, which is a simple and efficient way. The corresponding `*aclIf` is a structured directive similar to `ngIf` which does not render the element when it is not authorized. **Note:** In order to keep it simple, it does not support `acl-ability` permission point configuration. ## DEMO ### Role Displayed when the button must have a user role. ```html ``` Displayed when the button must have a user or manage role. ```html ``` Displayed when the button must have a user and manage role. ```html ``` Displayed when the input muse have a user role, displayed text when it's not authorized. ```html {{user}} ``` Use `except` reverse control to displayed when it's not authorized. ```html ``` ### Permission Displayed when the button must have a 10 value permisseion. ```html ``` In order for the acl instruction to be a role or a permission, so the value of the parameter value is `string` which mean the role, `number` which mean the permission. Use the `acl-ability` parameter if the permission is a string. ```html ``` - `oneOf` Must be valid against exactly one of the given permission (default). - `allOf` Must be valid against all of the given permission Displayed when the button must have a `10` and `USER-EDIT` permission. ```html ``` **String permission** The check permission is via the `can` method, and through [Global Configuration](/docs/global-config) `acl.preCan` method, which can be used to implement a string to distinguish roles or permissions. ```ts // global-config.module.ts const alainConfig: AlainConfig = { acl: { preCan: (roleOrAbility) => { const str = roleOrAbility.toString(); return str.startsWith('ability.') ? { ability: [ str ] } : null; } } }; ``` Therefore, passing a string with the beginning of `ability.` will be considered a permission point, for example: ```html ``` ## API ### *aclIf Property | Description | Type | Default ----------|----------------|----------|------- `[aclIf]` | `can` method parameter | `ACLCanType` | - `[aclIfThen]` | Display template when authorized | `TemplateRef | null` | - `[aclIfElse]` | Display template when not authorized | `TemplateRef | null` | - `[except]` | Permissions denied to display | `boolean` | `false` --- --- order: 3 title: Guard type: Documents --- ## Foreword Routing guard prevent unauthorized users visit the page. `@delon/acl` implements the generic guard functions `aclCanMatch`, `aclCanActivate`, `aclCanActivateChild`, which allows for complex operations through simple configuration in route registration, and supports the `Observable` type. Use the fixed attribute `guard` to specify the `ACLCanType` parameter value, for example: ```ts const routes: Routes = [ { path: 'auth', canActivate: [ aclCanActivate ], data: { guard: 'user1' as ACLGuardType } }, { path: 'auth', canActivate: [ aclCanActivate ], data: { guard: { role: [ 'user1' ], ability: [ 10, 'USER-EDIT' ], mode: 'allOf' } as ACLGuardType, guard_url: '/no-permisseion' } }, { path: 'obs', canActivate: [ aclCanActivate ], data: { guard: ((_srv, _injector) => { return of('user'); }) as ACLGuardFunctionType, guard_url: '/no-permisseion' } } ] ``` > The value of `guard` must match the value of [ACLCanType](/acl/api#ACLCanType). ## DEMO ```ts import { of } from 'rxjs'; import { aclCanActivate, aclCanActivateChild, aclCanMatch } from '@delon/acl'; const routes: Routes = [ { path: 'guard', component: GuardComponent, children: [ { path: 'auth', component: GuardAuthComponent, canActivate: [ aclCanActivate ], data: { guard: 'user1' } }, { path: 'admin', component: GuardAdminComponent, canActivate: [ aclCanActivate ], data: { guard: 'admin' } } ], canActivateChild: [ aclCanActivateChild ], data: { guard: { role: [ 'user1' ], ability: [ 10, 'USER-EDIT' ], mode: 'allOf' } } }, { path: 'pro', loadChildren: './pro/pro.module#ProModule', canMatch: [ aclCanMatch ], data: { guard: 1 } }, { path: 'pro', loadChildren: './pro/pro.module#ProModule', canMatch: [ aclCanMatch ], data: { guard: of(false).pipe(map(v => 'admin')) } } ]; ``` --- --- order: 0 title: How to start type: Dev i18n: need-update --- ## First, pre-order preparation As you begin your business development on NG-ALAIN, i recommend that you first review the following documents, which is very helpful for everyone on the team. + [Ant Design Guide article](//ant.design/docs/spec/introduce) + [Ant Design for Angular](//ng.ant.design/) + [Antv chart - G2](//www.yuque.com/antv/g2-docs-en?language=en-us) + [NG-ZORRO Community recommendation](https://ng.ant.design/docs/recommendation/en) NG-ALAIN's base component library comes from NG-ZORRO, so you can get a very detailed [API](//ng.ant.design/) documentation for its use on the official website. For ng components provided by -alain are obtained through the [component page](/components). ## Second, the startup process NG-ALAIN is a scaffold that can be used directly in production environments. The prerequisite for understanding these details is that you have a certain knowledge of Angular. The following documents may be helpful to you before you start: - NG-ALAIN Getting started video ([YouTube](https://www.youtube.com/watch?v=lPnNKPuULVw&list=PLhWkvn5F8uyJRimbVZ944unzRrHeujngw)、[Tencent video](http://v.qq.com/vplus/2c1dd5c6db4feeeea25e9827b38c171e/foldervideos/870001501oy1ijf)、[Station B](https://space.bilibili.com/12207877/#/channel/detail?cid=50229)) - [Know the column](https://zhuanlan.zhihu.com/ng-alain) When running an app via `ng serve`, a complete Angular startup process would look like this: 1. Trigger `APP_INITIALIZER` (the scaffolding implementation is implemented in `StartupService.load`) to get the application information. 2. Trigger service routing (src/app/routes/routes-routing.module.ts` for scaffolding) 3. Rendering components ### 1) APP_INITIALIZER Angular provides a DI (dependency injection) token `APP_INITIALIZER` that allows the application to perform some data that will affect the rendering results when it starts, such as: language data, menu data, user information data, dictionary data, etc., and must return an `Observable` Asynchronous, asynchronous means that you can do a lot of interesting things, such as data coming from a remote location. `APP_INITIALIZER` will only be executed once, you only need to register it in the `ApplicationConfig` module. The NG-ALAIN scaffolding provides a sample code on how to load basic data before starting rendering after starting Angular [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/ master/src/app/core/startup/startup.service.ts). 1. Provide unified registration `provideStartup` function, which only needs to be registered in `app.config.ts` 2. Provide the `load()` function and ensure that **regardless of whether the request is successful or not** must return an `Observable` for Angular to render normally, otherwise Angular will not be able to start. > Note: NG-ALAIN provides authorization services. If the requested data interface cannot be authorized, you can add `ALLOW_ANONYMOUS` to mark it. **Application Information** Including: application name, description, year, information can be directly injected into the `SettingsService` ([API](/theme/settings)) and directly in the HTML template. ```ts this.settingService.setApp(res.app); ``` **User Info** Including: name, avatar, email address, etc., information can be directly injected into the `SettingsService` ([API](/theme/settings)) and directly in the HTML template. ```ts this.settingService.setUser(res.user); ``` **Layout information** Including: name, avatar, email, address, etc., information can be directly injected into the `SettingsService` ([API](/theme/settings)) and directly in the HTML template. ```ts // Whether to fix the top menu this.settingService.setLayout(`fixed`, false); // Whether to collapse the right menu this.settingService.setLayout(`collapsed`, false); ``` **Menu data** NG-ALAIN takes menu from the remote and can also inject `MenuService` ([API](/theme/menu)) to change the menu data. Of course, it is more reasonable to perform menu assignment before Angular starts. Menu data **Make sure** ensure [Menu](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/menu/interface.ts) format, menu data throughout Applications, for example: page header auto navigation [page-header](/components/page-header), page title text [TitleService](/theme/title ) Wait. ```ts this.menuService.add(res.menu); ``` **Page title** If the page title always wants to add the application name as a suffix, you can re-adjust the `suffix` attribute value by injecting `TitleService`([API](/theme/title)). ```ts // Set the suffix of the page title this.titleService.suffix = res.app.name; ``` **ACL** ```ts this.aclService.setFull(true); ``` It is recommended to load the ACL access control permission data before starting. For more details, please refer to [Access Control List](/acl). **Globalization** It is recommended to load the internationalization package first before starting, which will ensure that the page is rendered as the target language after the project is started. See [Internationalization](/docs/i18n) for more details. ### 2) Business routing Scaffolding top-level routing begins with [routes.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/routes.ts) Its structure is as follows: ```ts const routes: Routes = [ { path: '', component: LayoutBasicComponent, children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: 'dashboard', loadChildren: () => import('./dashboard/routes').then(m => m.routes) }, // business submodule // { path: 'trade', loadChildren: () => import('./trade/routes').then(m => m.routes) }, ] }, // Blank layout { path: 'blank', component: LayoutBlankComponent, children: [ ] }, { path: '', loadChildren: () => import('./passport/routes').then(m => m.routes) }, { path: 'exception', loadChildren: () => import('./exception/routes').then(m => m.routes) }, // All missed routes will jump to the `exception/404` page { path: '**', redirectTo: 'dashboard' } ]; ``` > The above mentioned `LayoutBasicComponent` basic layout is used in the business module, user authorization uses `LayoutPassportComponent` user authorization layout and `LayoutBlankComponent` blank layout. The above three layouts can be used in [layout](https://github.com/ ng-alain/ng-alain/tree/master/src/app/layout) directory. > NG-ALAIN also provides some [commercial themes](https://e.ng-alain.com/) to chooses. For example, when a user accesses the `/dashboard` route, they will first go through `LayoutBasicComponent` -> `DashboardComponent`, and eventually form a huge component tree to represent a specific page. NG-ALAIN scaffolding helps you complete most of the work, and a newbie only needs to care about how to implement the `DashboardComponent` business component. ### Routing permission control The routing URL may be affected by the browser's own historical memory, so that users may access the unprivileged route. If you want a better experience, you need to configure the `canActivate` option on the route. When the user has no permission, it will utomatically jump to the relevant page. see the [ACL Routing Guard](/acl/guard) section for details. ### Intercept network requests Network requests are a very frequent task. If you want to use network request actions elegantly within business components, it is essential to centrally handle server-side URL prefixes, exception handling, token refresh and other operations. NG-ALAIN scaffolding Provide a [net](https://github.com/ng-alain/ng-alain/tree/master/src/app/core/net) file. It uses the token `HttpInterceptorFn` to act as an interceptor. For details, please refer to [default.interceptor.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor. ts) file. ## IDE A developer must first sharpen his tools if he is to do his work well, NG-ALAIN recommended to use the [Visual Studio Code](https://code.visualstudio.com/) IDE, because ng-alain adds some extra features to VSCode to better help you. Development. > Or use the [NG-ALAIN Extension Pack](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-extension-pack) suite directly. ### Code fragment - [NG-ALAIN Snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) ### Class style smart reminder ng-alain has a lot of built-in toolkit styles ([API](/theme/tools)), and the following plugins can be installed directly into the HTML template. - [NG-ALAIN Snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) --- --- order: 30 title: en-US: I18n zh-CN: 国际化 type: Advance --- Angular internationalization provides a solution for extracting language files, but for NG-ALAIN, this is not the best way; this is mainly limited by @Delon/* The component library needs to provide a set of dynamic translation Service, therefore, NG-ALAIN has built-in a simple internationalized service `ALAIN_I18N_TOKEN` interface. ## How to configure Scaffolding is composed of two important parts: `ng-zorro-antd` and `@delon/*`. These two libraries have their own international configuration. When internationalizing, they need to be the same for these libraries. Language configuration. ### Angular Angular configuration is mainly for currency, date format, etc., such as Chinese version: ```ts import { registerLocaleData } from '@angular/common'; import zh from '@angular/common/locales/zh'; registerLocaleData(zh); ``` ### ng-zorro-antd `ng-zorro-antd` internationalization defaults to the Chinese version, for example the default English version: ```ts import { en_US, provideNzI18n } from 'ng-zorro-antd/i18n'; export const appConfig: ApplicationConfig = { providers: [provideNzI18n(en_US)] }; ``` Of course, you can also use runtime changes: ```ts import { en_US, NzI18nService } from 'ng-zorro-antd/i18n'; ... constructor(private nzI18nService:NzI18nService) { } switchLanguage() { this.nzI18nService.setLocale(en_US); } ``` ### @delon @delon internationalization defaults to Chinese version, for example the default is English version: ```ts import { DELON_LOCALE, en_US } from '@delon/theme'; @NgModule({ ... providers : [ { provide: DELON_LOCALE, useValue: en_US } ] }) export class AppModule { } ``` Of course, you can also use runtime changes: ```ts import { en_US, DelonLocaleService } from '@delon/theme'; ... private readonly i18n = inject(DelonLocaleService); switchLanguage() { this.delonLocaleService.setLocale(en_US); } ``` ## ALAIN_I18N_TOKEN `@delon/*` class library has many data interface properties with the _i18n_ typeface (for example: `page-header`, `st` column description, `Menu` menu data, etc.) when you want the data for these components. When the interface can dynamically switch automatically according to the Key value in the current language, you also need to define a self-implementation service interface for `ALAIN_I18N_TOKEN` (for example: [I18NService](https://github.com/ng-alain/ng-alain/blob) /master/src/app/core/i18n/i18n.service.ts)) and register under the `app.config.ts` file. ```ts import { I18NService } from '@core'; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] }; ``` ### i18n pipe In order not to be named by the third-party pipes, the scaffolding contains a `i18n` pipe, which is equivalent to calling the `fanAIN` method of `ALAIN_I18N_TOKEN` directly. `| i18n` will not listen to language change notifications, so there will be better performance. When you explicitly re-render the Angular project after switching languages, `| i18n` will be more suitable. ## How to add When creating scaffolding [from command line](/cli/add) `ng add ng-alain`, it is allowed to specify `--i18n` to indicate whether the internationalized sample code is included. ## How to delete The sample code covers the following: - `src/app/core/i18n` directory - Replace the pipe of i18n that may appear in the default layout using `| i18n` ## Default language Regardless of whether internationalization is required or not, since the default languages ​​of class libraries such as `Angular`, `ng-zorro-antd`, `@delon/*` are different, it is also necessary to ensure that the default language of these libraries is **the same type**. A simple example approach to understand the current language situation for each type of library: ```ts import { Component } from '@angular/core'; @Component({ selector: 'app-i18n-test', template: `

angular

Date: {{now | date}}

ng-zorro-antd

@delon

1
`, }) export class I18nTestComponent { now = new Date(); } ``` ### Example In order to make language uniformity, NG-ALAIN provides a simple unified configuration in the `AppModule` root module. #### Chinese Version ```ts import { default as ngLang } from '@angular/common/locales/zh'; import { provideNzI18n, zh_CN as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, zh_CN as delonLang } from '@delon/theme'; import { zhCN as dateLang } from 'date-fns/locale'; import { I18NService } from '@core'; const defaultLang: AlainProvideLang = { abbr: 'zh', ng: ngLang, zorro: zorroLang, date: dateLang, delon: delonLang, }; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] }; ``` #### English version ```ts import { default as ngLang } from '@angular/common/locales/en'; import { provideNzI18n, en_US as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, en_US as delonLang } from '@delon/theme'; import { en_US as dateLang } from 'date-fns/locale'; const LANG = { abbr: 'en', ng: ngLang, zorro: zorroLang, delon: delonLang, }; const defaultLang: AlainProvideLang = { abbr: 'en', ng: ngLang, zorro: zorroLang, date: dateLang, delon: delonLang, }; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] }; ``` ### Command Line Use the [defaultLanguage](/cli/plugin/zh#defaultLanguage) plugin to quickly switch between the default locales. ## Internationalized routing If you want to toggle internationalization by routed URLs, e.g. by accessing `/zh` and `/en` to change the language, just use the `alainI18nCanActivate` guard in the root route: ```ts const routes: Route[] = [ { path: '', component: LayoutComponent, canActivateChild: [alainI18nCanActivate], children: [ { path: '', redirectTo: 'en', pathMatch: 'full' }, { path: ':i18n', component: HomeComponent } ] } ]; ```` > Where `:i18n` is a fixed value of the parameter, which can be adjusted by the [Global Configuration](/docs/global-config) `paramNameOfUrlGuard`. --- --- order: 2 title: Internationalization type: Documents --- Provide a uniform localization support for `@delon/*` class library built-in text of components. ## Usage ### Providers Provides the token of `DELON_LOCALE` for configuring antd locale text globally. ```ts import { DELON_LOCALE, en_US } from '@delon/theme'; @NgModule({ ... providers : [ { provide: DELON_LOCALE, useValue: en_US } ] }) export class AppModule { } ``` ### Service Provides the service of `DelonLocaleService` to dynamic change the locale text. ```ts import { en_US, DelonLocaleService } from '@delon/theme'; ... constructor(private delonLocaleService: DelonLocaleService) { } switchLanguage() { this.delonLocaleService.setLocale(en_US); } ``` Note: `en_US` is the package name, follow below. ## Supported languages | Language | Filename | |----------|----------| | English | en_US | | Chinese (Simplified) | zh_CN | | Chinese (Traditional) | zh_HK | | Chinese (Traditional) | zh_TW | | Turkish | tr_TR | | Polish | pl_PL | | Greek | el_GR | | Korean | ko_KR | | Croatian | hr_HR | | Japanese | ja_JP | | Slovenian | sl_SI | | French | fr_FR | | Spanish | es_ES | | Italian | it_IT | | Vietnamese | vi_VN | | Arabic | ar_SA | | Indonesian | id_ID | | Khmer | km_KH | | Malay | ms_MY | | Thai | th_TH | ## Add a new language If you can't find your language, you are welcome to create a locale package based on [en_US](https://github.com/ng-alain/delon/tree/master/packages/theme/src/locale/languages/en-US.ts) (You can also refer to [#308](https://github.com/ng-alain/delon/pull/308) to contribute language package to us) and send us a pull request. --- --- order: 5 title: Layout type: Documents --- @delon/form layout is based on Ant Design [Grid](https://ng.ant.design/components/grid/en) system, and layout parameters are determined by SFSchema [Reder Type](/form/schema/en#Render-Type). There are three types of form layout: inline, vertical and horizontal(default), it is determined by [layout](/form/getting-started/en#API). ## Type ### Inline Form elements are arranged inline horizontally, usually, it is used for simple search box. The width of form element is determined by the width of component itself, you can adjust width by setting `width` parameter, for example, width of widget `select` would be very small if no `width` has been set. > You can also set [mode](/form/getting-started/en#What-is-mode) to `search`, which is a fast way to set widget to search mode. ### Vertical Label and form elements are arranged vertically. ### Horizontal Label and form elements are arranged horizontally, usually, it is used on edit page. Horizontal is more complicated than vertical, because responsive would be involved, the number of grid of each form element is determined by [grid](/form/schema/en#Responsive-Property-SFGridSchema). > You can also set [mode](/form/getting-started/en#What-is-mode) to `edit`, which is a fast way to set widget to edit mode. **Non-responsive** You only need to maintain `span` attribute when it is non-responsive. **Responsive** Responsive is based on `xs`、`sm`、`md`、`lg`、`xl`、`xxl` to determine how many grids for different size of screens, notes: - There are `24` grids each row - Set value to `12` if there are two form elements on the same row - `{ sm: 24, md: 12 }` will put two form elements on the same row when screen size is `≥992px`, and each form element on one row when screen size is `<992px` ## Irregular Layout Of course, it is impossible to always have fixed number of form elements, it is possible that one form element occupies a whole row, because of grid system, it will cause another issue: **labels cannot be aligned**, sf provides a solution, fix the width of all labels by `spanLabelFixed` attribute, for example: ```json { "properties": { "email": { "type": "string", "title": "Email", "format": "email" }, "name": { "type": "string", "title": "Name", "minLength": 5 }, "remark": { "type": "string", "title": "Description", "ui": { "widget": "textarea", "autosize": true, "grid": { "span": 24 } } } }, "ui": { "spanLabelFixed": 100, "grid": { "span": 12 } } } ``` ## Button Button rendering has same layout and parameters with form element, you can adjust rendering style by setting [SFButton](/form/getting-started/en#SFButton) attributes. **Notes** - It means adding button manually but reserve container when the value is `null` or `undefined` - It means adding button manually but do not reserve container when the value is `none` - When using `spanLabelFixed` to set fixed width of label, the position is centered by default if no `render.class` is set **Customization** You must set value of `button` to `null` when you want to customize buttons. ```html ``` ## Expand & Collapse When the form has too many fields, you can mark less important fields as collapsible and use the expand/collapse button to toggle visibility. **Usage** Mark fields with `ui.collapse = true`: ```json { "properties": { "name": { "type": "string", "title": "Name" }, "email": { "type": "string", "title": "Email" }, "nickname": { "type": "string", "title": "Nickname", "ui": { "collapse": true } }, "bio": { "type": "string", "title": "Bio", "ui": { "collapse": true } } } } ``` Enable `expandable` on the `sf` component: ```html ``` The expand/collapse button will automatically appear alongside the submit/reset buttons. It is only visible when the form has at least one `collapse` field. Button text supports i18n with default values `Expand` / `Collapse`. You can also control the state externally via `[(expanded)]` two-way binding: ```html ``` **Note:** `expandable` and `collapse` are UI-level controls, independent of the field's `required` logic. --- --- order: 1 title: LLMs.txt tag: New group: AI --- This guide explains how to enable AI tools to better understand @delon/*. ## What is LLMs.txt? We support [LLMs.txt](https://llmstxt.org/) files for making the @delon/* documentation available to large language models (LLMs). This feature helps AI tools better understand our component library, its APIs, and usage patterns. ## Available Resources ### LLMs.txt Aggregated Files We provide several aggregated files to help AI tools access our documentation: | File | Description | | ---------------------------------------------------------- | ----------------------------------------------------------------------------------- | | [llms.txt](https://ng-alain.com/llms.txt) | Navigation file with links to all documentation and components | | [llms-full.txt](https://ng-alain.com/llms-full.txt) | Complete component documentation (English) with implementation details and examples | | [llms-full-cn.txt](https://ng-alain.com/llms-full-cn.txt) | Complete component documentation (Chinese) | ### Single Component Documentation Access individual component documentation with `.md` suffix: - `https://ng-alain.com/llms/components/auto-focus.en.md` (English) - `https://ng-alain.com/llms/components/auto-focus.cn.md` (Chinese) ## Usage with AI Tools | Tool | Description | Prompt | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | **Cursor** | Use `@Docs` feature to include LLMs.txt, or add prompt to `.cursor/rules`. [Documentation](https://docs.cursor.com/context/@-symbols/@-docs) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Windsurf** | Add prompt to `.windsurf/rules` or use cascade memories. [Documentation](https://docs.windsurf.com/windsurf/cascade/memories) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Claude Code** | Add to CLAUDE.md or use `/memory` to persist. [Documentation](https://docs.anthropic.com/en/docs/claude-code) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **GitHub Copilot** | Add to `.github/copilot-instructions.md`. [Documentation](https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Codex** | Add to `.codex/settings.json` or AGENTS.md. [Documentation](https://github.com/openai/codex) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Gemini CLI** | Use `--context` parameter or add to `.gemini/config.json`. [Documentation](https://ai.google.dev/gemini-api/docs?hl=en) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Trae** | Add to project's knowledge sources in settings. [Documentation](https://trae.ai/docs) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Qoder** | Add to `.qoder/config.yml` or use `@docs` in conversation. [Documentation](https://docs.qoder.com/) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | | **Neovate Code** | Run `neovate` and describe task with prompt. [Documentation](https://github.com/neovateai/neovate-code) | `Read https://ng-alain.com/llms-full.txt and understand @delon/* components. Use this knowledge when writing code with @delon/*.` | --- --- order: 30 title: en-US: Module Guidelines zh-CN: 模块注册指导原则 type: Other --- The `AppModule`, `CoreModule`, and `SharedModule` modules have not been used very clearly, and it is easy to use them. The goal of the Angular module is to make components, instructions, services, and pipeline function blocks more cohesive, and each functional area forms a separate set of business domains or utilities. ## 1) Classification description ------------ ### AppModule The root module is used to guide Angular startup. It is very suitable for importing some modules that need to be used everywhere in the application. Such as: theme system, user master authentication module, permission module, global HTTP interceptors, international services, etc.. ### CoreModule The core module will only be imported once. It is equivalent to `AppModule`, but we should treat it as a **pure service class module**. For example: message, data access, etc. ### SharedModule We call it a shared module. It should not have `providers` because `ShareModule` will be imported in all business modules. Which will cause the service to be overwritten. NG-ZORRO, @delon/abc, @delon/chart, etc. have changed from all import to on-demand import since version 11. For this reason, NG-ALAIN has refined two files `shared-delon.module.ts` and `shared -zorro.module.ts` merges some modules frequently used throughout the project into a module called `SharedModule`, which is why it is necessary to import it in the business module for the first time. Although this method can reduce unnecessary import code, it will also cause compilation speed. Therefore, it is not recommended to put all components into `SharedModule`, and try to put the modules that need to be used more than three times before putting them here; Otherwise, you must import it yourself in the business module. ## 2) Recommendation ------------ ### AppModule **Should** import module: + Angular Module: `BrowserModule`, `BrowserAnimationsModule`, `HttpClientModule` + `AlainThemeModule` Theme system + `AlainAuthModule` User authentication module + `AlainACLModule` Privilege module + Internationalization module **Should** include services: + Angular globalization + HTTP interceptor + Angular start service + `ng-zorro-antd` Basic component service + `@delon/abc` Business component service **Role:** Throughout the definition of the entire application. ------------ ### CoreModule **Should** only leave the `providers` attribute. **Role:** Some common services. such as: user messages, HTTP data access. ------------ ### ShareModule **Should** contain definitions: + Apply generic custom business components **Should** import module: + Angular generic module:`CommonModule`、`FormsModule`、`RouterModule`、`ReactiveFormsModule` + `ng-zorro-antd` Basic component module + `@delon/abc` Business component module + Third-party generic dependency component module **Should** Export all included modules. **Should not** have `providers` attribute. **Function:** Some common custom, third-party component definitions, reducing the import of business modules. ------------ ### Business module The business module should include a business definition module and a routing module. **Should** import module: + `SharedModule` + Corresponding routing module **Should not**: + Export any component + Try not to use the `providers` attribute **Route module** **Should** include only the `import`, `exports` modules of the route. --- --- order: 20 title: en-US: New Component zh-CN: 新增业务组件 type: Dev --- For some functional modules that may be referenced in multiple places, it is recommended to refine the management into unified management of business components. These components generally have the following characteristics: - Only responsible for a relatively independent, stable function; - no separate routing configuration; - May be purely static, controlled only by parameters passed by the parent component (usually a page). Let's take a simple static component as an example. Suppose your app often needs to display images. These images are fixed in width, have a gray background and a certain padding, and have text descriptions, like the following: ![](https://gw.alipayobjects.com/zos/rmsportal/vcRltFiKfHBHFrUcsTtW.png | width=400) You can do this with a component that has a default style and can receive the parameters passed by the parent component for display. ## Create a new file Create a new folder named `components` under `src/app/shared`. Create folder called `image-wrapper` and component file. If required add ts files `index.ts` and style files `index.less`. Provide `README.md` for component API descriptions in this folder. > When using components, the default is to look for the export object in `index.ts`. If your component is more complex, you can split it into multiple files, and finally unify the export in `index.ts`, like this: > ```ts > // main.component.ts > export class MainComponent {} > > // sub.component.ts > export class SubComponent {} > > // index.ts > export MainComponent from './main.component'; > export SubComponent from './sub.component'; > ``` Your code is probably like this: ```ts // index.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'image-wrapper', template: `
@if (desc) {
{{ desc }}
}
`, styleUrls: [ './index.less' ] }) export class ImageWrapperComponent { @Input() style: { [key: string]: string }; @Input() src: string; @Input() desc: string; } ``` ```less // index.less :host { width: 400px; margin: 0 auto; padding: 0 20px 8px; text-align: center; background: #f2f4f5; ::ng-deep { .img { max-width: calc(100% - 32px); margin: 2.4em 1em; vertical-align: middle; box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35); } } } ``` The components are built here, to learn more about the development of [component styles](/theme/component-styles). ## Register Once the component is created, you need to import the component into `SharedModule` so that all submodules can use it. ```ts // shared.module.ts import { ImageWrapperComponent } from './image-wrapper'; const COMPONENTS = [ ImageWrapperComponent ]; ``` ## Use Where you want to use this component, just follow the component-defined API input parameters and use it directly: ```html ``` --- --- order: 10 title: en-US: New Page zh-CN: 新增页面 type: Dev --- Angular renders a page in a component tree, the actual development is to organize the code in a module tree to make it better to resuse code. For **module granularity** depends on the requirements, ng-alain is positioned in the middle of the front-end. Therefore, it is recommended to organize your code structure from a business perspective. NG-ALAIN provides a very rich set of Schematics templates to quickly create templates and pages that match NG-ALAIN features, as well as a variety of pluggable [plugins](/cli/plugin). > Additionally: NG-ALAIN is a standard Angular CLI project, you can still use the default command line. ## First, the module To create a page, you need to create a module first. If you need a system to set the relevant module, execute the command: ```bash ng g ng-alain:module sys ``` The CLI will automatically create `sys.module.ts` and `sys-routing.module.ts` files under `src/app/routes/sys`, the former is the system setup module component definition file; the latter is the system setup module routing Configuration file. ```ts // sys.module.ts import { NgModule, Type } from '@angular/core'; import { SharedModule } from '@shared'; import { SysRoutingModule } from './sys-routing.module'; const COMPONENTS: Type[] = []; @NgModule({ imports: [SharedModule, SysRoutingModule], declarations: COMPONENTS, }) export class SysModule {} ``` The function of the module is to import the modules we need. All NG-ZORRO, @delon/abc, @delon/chart, etc. are loaded on demand. The external components are imported wherever the current business page needs. In order to reduce these import actions, NG -ALAIN has refined two files `shared-delon.module.ts` and `shared-zorro.module.ts` to merge some modules frequently used throughout the project into a module called `SharedModule`, which is why it is necessary Import it in the business module for the first time. Note: It is not recommended to put all the components in `SharedModule`, as far as possible, put the modules that need to be used two or three times or more. And the routing configuration module: ```ts // sys-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) export class SysRoutingModule {} ``` By now, you can safely start developing business pages like menu management, logging, system configuration, etc. in the `sys` directory. ## Second, the page Use the `ng generate` (abbreviated as: `ng g`) command to create a log list page in the `sys` directory: ```bash ng g ng-alain:list log -m=sys ``` > See [Command Line Tools](/cli) for more information. Finally, you can access the [Log](//localhost:4200/#/sys/log) page. Of course, the log may be a very rich piece of information, you can add a view page opened in a modal box to display more details. ```bash ng g ng-alain:view view -m=sys -t=log ``` `-t=log` indicates that you want to put the created file under `sys/log/view`. --- --- order: 10 title: ng add subtitle: Create scaffold type: Documents --- ## Overview ```bash ng add ng-alain # If you want to create an English version, then: ng add ng-alain --defaultLanguage=en ``` ## Options | Name | Default | Description | | ------------------- | ------- | ----------------------------------------------------------------------- | | `--form` | `true` | Whether include dynamic form | | `--mock` | `true` | Whether include mock | | `--defaultLanguage` | `zh` | Default language, [Supported language list](/cli/plugin/en#Supported-language-list) | | `--codeStyle` | `false` | Whether include code style | | `--i18n` | `false` | Whether include i18n | For example, generate a project with i18n: ```bash ng add ng-alain --i18n --defaultLanguage=en ``` See more [plugins](/cli/plugin). --- --- order: 20 title: ng g subtitle: Business page type: Documents --- ## Foreword `ng generate` (shorthand: `ng g`) is used to generate business pages. The default Angular Cli template is different from the ng-alain specification. For example, we want to include a `SharedModule` when generating a module. ng-alain adds cool operations based on this. ## Command format ```bash ng g ng-alain:[command name] [name] [options] ``` Examples: ```bash # Generate a trade module ng g ng-alain:module trade # Generate a TradeListComponent List component under the trade module ng g ng-alain:list list -m=trade # Generate a TradeListComponent List component and TradeService class under the trade module ng g ng-alain:list list -m=trade --service=root # Generate a ListComponent List component under the trade module ng g ng-alain:list list -m=trade --withoutModulePrefixInComponentName=true # Generate a TradeEditComponent Edit component under the trade module ng g ng-alain:edit edit -m=trade ``` > ng-alain has own file structure specification, which can cause exceptions when you break this file structure. | Option | Description | | ---- | --- | | `-m` or `--module` | Allows specification of the declaring module. | | `-t` or `--target` | Specifies relative path, could be set like `bus/list` | | `--modal` | Specifies using modal mode | | `--withoutPrefix` | Without prefix to selectors | | `--withoutModulePrefixInComponentName` | Without prefix to component name | | `--service` | Specifies how to generate service classes, can be set: `ignore`, `root`, `none` | ## Module Generate a `trade` module: ```bash ng g ng-alain:module trade ``` Will be generated `trade.module.ts`, `trade-routing.module.ts` in the `routes/trade` directory. The module imports `SharedModule` and some import and export specifications, and you can't destroy these variable names (like this: `COMPONENTS`, `routes`). By default, all the codes are stored under `app/routes`, which can be pointed to other folder through `ng-alain.json`, for example: ```json { "$schema": "./node_modules/ng-alain/schema.json", "projects": { // Indicates that ng-alain projects are stored under `app/pages` "ng-alain": { "routesRoot": "app/pages" } } } ``` ## Business page - `empty` Blank page - `list` List page - `edit` Edit page - `view` View page - `curd` List, edit, view pages Generate a `list` list page in the `trade` directory: ```bash ng g ng-alain:list list -m=trade ``` **Note:** `-m` must be specified because ng-alain thinks the page should be in a specific module, not a ghost. ### Cool In general, a module might includes the same type of business page, and its file structure might like this: ``` sys log view view.component.ts edit edit.component.ts log.component.ts sys.module.ts ``` So when you want to generate a view page that should be under the `log` directory (could be set like `log/list`): ```bash ng g ng-alain:view view -m=sys -t=log ``` **Override the default template pages** If the default pages generated by commands `list`, `edit`, `view`, and `empty` are not expected by the business, can be override. For example, override the `list` command default template, create the directory name `_list` under the root directory `_cli-tpl`, and the directory structure must be equivalent to the [original list directory](https://github.com/ng-alain/delon/tree/master/packages/schematics/list/files). ### edit & view page For `edit`, `view`, the default is modal render, you can use the page render: ```bash ng g ng-alain:edit [page name] --modal=false ``` > If you receive `No provider for NzModalRef!` error, because of modal component must be opened with `nzModalService`, and does not need to register into route. ## Custom template page In addition to the default, you can also customize the project-level business page. For example, to create a custom edit page template, you only need to create the following directory structure in the project's root directory (You can get it via [Github](https://github.com/ng-alain/ng-alain/tree/master/_cli-tpl)): ``` └── _cli-tpl │ └── edit // Your template name │ └── __path__ // (name fixed value) │ └── __name@dasherize@if-flat__ // (name fixed value) │ ├── __name@dasherize__.component.ts.template // Component class file (name fixed value) │ ├── __name@dasherize__.component.html.template // Component html file (name fixed value) │ └── __name@dasherize__.component.spec.ts.template // Component spec file (name fixed value) └── src ``` After that, just run: ```bash ng g ng-alain:tpl [your template name] [name] -m=trade ``` ### How to write a template file In the directory structure of the custom page, the file name begins with the `__` prefix is a variable placeholder, Cli passes some parameters and methods: | Type | Name | Default | Description | | -------- | --------------- | --------- | -------------------------------------------- | | Variable | project | - | Project name | | Variable | name | - | Name, equivalent command line `` | | Variable | path | - | Target path | | Variable | flat | `false` | Whether file is flat | | Variable | inlineTemplate | `false` | Whether inline template(Fixed value `false`) | | Variable | selector | - | Component `selector` | | Variable | componentName | - | Component name | | Variable | changeDetection | `Default` | Component `changeDetection` value | | Variable | modal | - | Whether to use Modal to render | | Method | decamelize | - | Converts a camelized string into all lower case separated by underscores | | Method | dasherize | - | Replaces underscores, spaces, or camelCase with dashes | | Method | camelize | - | Returns the lowerCamelCase form of a string | | Method | classify | - | Returns the UpperCamelCase form of a string | | Method | underscore | - | More general than decamelize. Returns the lower_case_and_underscored form of a string. | | Method | capitalize | - | Returns the Capitalized form of a string | These variables or methods can be used in templates, for example: `<%=componentName%>` for component names, `<% %>` for JavaScript code. You can refer to: - [delon](https://github.com/ng-alain/delon/blob/master/packages/schematics/edit/files/__path__/__name%40dasherize%40if-flat__/__name%40dasherize__.component.html) - [material2](https://github.com/angular/material2/blob/master/src/lib/schematics/dashboard/files/__path__/__name%40dasherize%40if-flat__/__name%40dasherize__.component.html) ### Custom Data The `tpl` command allows you to process the data further before generating the file, The command will check the `_cli-tpl/_fix.js` file during execution and call the `fix` method, which must return a `Promise` object, for example: > **Note: ** CLI is a Node JS program, so the syntax is based on Node JS. ```js function fix(options) { return new Promise((resolve) => { resolve(); }); } module.exports = { fix }; ``` The `fix` method has only an `options` parameter, which contains the CLI used to generate all parameter data, even if it is undefined, for example: ```bash ng g ng-alain:tpl list -m=setting --import-type=UserDto ``` `import-type` is not a defined parameter of the command itself, but `options` will convert these undefined parameters to an `extraArgs` object, so the `options` you receive will be: ```json { "tplName": "test", "modal": true, ... "extraArgs": { "import-type": "UserDto" } } ``` The `options` object is passed to the template engine, so you can attach some processed data to `options` and use them in the template file, for example: ```json { "tplName": "test", "modal": true, ... "extraArgs": { "import-type": "UserDto", "newData": "asdf" } } ``` You can apply `newData` to the template, for example `__name@dasherize__.component.html`: ```html <%= extraArgs.newData %> ``` The result is: ```html asdf ``` --- --- order: 80 title: en-US: Performance zh-CN: 优化 type: Advance --- ## Envelope size optimization Divided into JavaScript script files and CSS files. The following only describes the script part. For the CSS file, please refer to [Optimize Theme System](/theme/performance). **Note:** It is recommended to always prioritize **business, optimize to the post-** criteria, and have some understanding of NG-ALAIN before starting to optimize. The optimization scheme in this chapter will change with the change of the version. Please pay attention to the details. ### Structure description In general, there are two files that are larger after the build: `scripts.js` and `main.js`, and our optimization is mainly for these two items. **scripts.js** It comes from a collection of `scripts` nodes of `angular.json`, so the size of this file depends on the size of the third-party component referenced by the `scripts` node. In general, you should put it under `scripts` when you are using non-Angular third-party components. **main.js** Using `ng build` by default will package all `@angular/*`, `ng-zorro-antd`, `@delon/*` and some third-party Angular components. Unless you use the `--vendor-chunk` parameter to separate these classes library. ### Optimization object We know that the resource file packaged by Angular Cli will contain the hashing value of the file. Which is like the unique identifier of the file. If the module is not modified, no new hashing value will be generated. This ensures that after the user downloads the resource for the first time. No matter how we build the user again, there is no need to download it again. > In fact, Angular Cli defaults to `--vendor-chunk`. The main factor is that these `@angular/*` are very fast relative to iteration. According to NG-ALAIN's [module registration guidelines](/docs/module), generate two `shared-delon.module.ts` and` shared-zorro.module.ts` produced are the summary of the import of secondary modules shared by @Delon and NG-ZORRO. `@delon/abc`,`@delon/chart`, `ng-zorro-antd` The three main libraries all support secondary imports. Only selecting the modules required by the project will effectively solve the problem of package size. ### Conclusion After NG-ZORRO provides better optimization support, we will release [#684](https://github.com/ng-alain/ng-alain/pull/684) and hope to have a better package size. Expected results. --- --- type: Documents order: 10 title: Performance --- ng-alain also includes a set of like bootstrap style tools, And built on the design principles developed by [Ant Design](https://ant.design/). If you are familiar with Bootstrap, it will be very friendly, because all naming as close as it. Also, Install [ng-alain snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) plugin in VSCode for intellisense these class names. ## Usage We have hundreds of Less variables (including ng-zorro-antd, ng-alain), some of which contain the `-enabled` suffix, which means that these libraries are optional. You can to [theme.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles/theme.less) set to `false`, which can reduce css file size. ```less // I don't need masonry style @masonry-enabled: false ``` ## Parameters | Name | Default | Description | | --- | --- | --- | | `@scrollbar-enabled` | `true` | Enable landscaping scrollbars | | `@preserve-white-spaces-enabled` | `true` | Fixed between buttons spacing when enabled [preserveWhitespaces](https://angular.io/api/core/Component#preserveWhitespaces) is true | | `@form-state-visual-feedback-enabled` | `false` | Turn on visual feedback of form invalid elements | | `@hafl-enabled` | `true` | Whether hafl image | | `@abs-enabled` | `true` | Whether abs element| | `@masonry-enabled` | `true` | Whether css masonry | | `@setting-drawer-enabled` | `true` | Whether setting drawer css | | `@search-form-enabled` | `true` | Simple style search form, [DEMO](https://ng-alain.surge.sh/) | | `@search__form-enabled` | `true` | Pro style search form, [DEMO](https://ng-alain.surge.sh/) | --- --- order: 30 title: Pluggable plugin type: Documents --- ## Foreword Plugins are the add and remove of optional features, such as when you don't need code style (although I don't think so), only need to: ```bash ng g ng-alain:plugin codeStyle -t=remove ``` Or add code style: ```bash ng g ng-alain:plugin codeStyle ``` ## Command format ```bash ng g ng-alain:plugin [plugin name] -t=[add | remove] ``` The `[plugin name]` is plugin name, `-t` supports two values `add` (default) and `remove`. ## List of plugins ### codeStyle Code style rules: - Verify typescript with [angular-eslint](https://github.com/angular-eslint/angular-eslint) - Verify less with [stylelint](https://github.com/stylelint/stylelint) - Use [prettier](https://github.com/prettier/prettier) code formatting - Use [husky](https://github.com/typicode/husky) to code verify and code format when git add ```bash # add ng g ng-alain:plugin codeStyle # remove ng g ng-alain:plugin codeStyle -t=remove ``` ### docker Support for Docker deployment. ```bash # add ng g ng-alain:plugin docker # remove ng g ng-alain:plugin docker -t=remove ``` ### defaultLanguage Change the current default language, refer to: [I18n - Default Language](/docs/i18n#默认语言) ```bash # change to [en] ng g ng-alain:plugin defaultLanguage --defaultLanguage=en # change to [zh] ng g ng-alain:plugin defaultLanguage --defaultLanguage=zh # change to [zh-tw] ng g ng-alain:plugin defaultLanguage --defaultLanguage=zh-tw ``` #### Supported language list | Name | Language pack name | [Angular](https://github.com/angular/angular/tree/master/packages/common/locales) pack | [Zorro](http://ng.ant.design/docs/i18n/zh#%E6%94%AF%E6%8C%81%E8%AF%AD%E8%A8%80) pack | [Delon](/theme/locale) pack | |------|--------------------|----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|--------------------------| | Simplified Chinese | zh-Hans,zh-cn,zh-Hans-CN,zh | zh-Hans,zh-cn,zh-Hans-CN,zh | zh_CN | zh_CN | | Traditional Chinese | zh-Hant,zh-tw,zh-Hant-TW | zh-Hant,zh-tw,zh-Hant-TW | zh_TW | zh_TW | | English (American) | en | en | en_US | en_US | | Turkish | tr | tr | tr_TR | tr_TR | | Polish | pl | pl | pl_PL | pl_PL | | Greek | el | el | el_GR | el_GR | | Korean | ko | ko | ko_KR | ko_KR | | Croatian | hr | hr | hr_HR | hr_HR | | Slovenian | sl | sl | sl_SI | sl_SI | | French | fr | fr | fr_FR | fr_FR | | Spanish | es | es | es_ES | es_ES | | Italian | it | it | it_IT | it_IT | | Vietnamese | vi | vi | vi_VI | vi_VI | | Arabic | ar | ar | ar_EG | ar_SA | ### sts [ng-alain-sts](https://github.com/ng-alain/sts) Plugins, Build Swagger APIs to list, edit pages, You can finish some interesting things. ```bash # add ng g ng-alain:plugin sts # remove ng g ng-alain:plugin sts -t=remove ``` ### icon From the project to analyze and generate static load Icon, The plugin will automatically generate two files in the `src` directory: - `src/style-icons.ts` Custom Icon (e.g: remote menu icon) - `src/style-icons-auto.ts` command automatically generates files > Automatically exclude [ng-zorro-antd](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/icon/nz-icon.service.ts#L6) and [@delon](https://github.com/ng-alain/delon/blob/master/packages/theme/src/theme.module.ts#L33) already loaded icons. ```bash ng g ng-alain:plugin icon ``` Also, you need to manually import in `startup.service.ts`: ```ts import { ICONS_AUTO } from '../../../style-icons-auto'; import { ICONS } from '../../../style-icons'; @Injectable() export class StartupService { constructor(iconSrv: NzIconService) { iconSrv.addIcon(...ICONS_AUTO, ...ICONS); } } ``` **Valid Grammar** ```html ``` ### rtl Support RTL plug-in, the text direction is set to "from right to left". ```bash ng g ng-alain:plugin rtl ``` > The plug-in does not support hot swap, if you need to remove it, please handle it manually. --- --- order: 10 title: QA type: Documents --- ## How to ignore a request When calling the request, add `ALLOW_ANONYMOUS`. ```ts this.http.post(`login`, { name: 'cipchk', pwd: '123456' }, null, { context: new HttpContext().set(ALLOW_ANONYMOUS, true) }); ``` ## How to capture intercepted information when there is no Token? ```ts // Use subscription Error this.http.get('/user').subscribe( res => console.log('success', res), err => console.error('error', err) ); // Or use catchError this.http.get('/user').pipe( catchError(err => { console.error('error', err); return of({}); }) ).subscribe(); ``` --- --- order: 25 title: en-US: Routing guard zh-CN: 路由守卫 type: Documents --- ## Written in front When a route does not initiate a request, it means that the Token validity cannot be verified in the interceptor, and the routing guard can solve the problem, for example, in your root path: ```ts [ { path: 'home', component: MockComponent, canActivate: [authJWTCanActivate], }, { path: 'my', canActivateChild: [authJWTCanActivateChild], children: [ { path: 'profile', component: MockComponent } ], }, { path: 'login', component: MockComponent, }, ] ``` ## How to choose? Similarly, the different authentication styles are: - `authSimpleCanActivate`, `authSimpleCanActivateChild`, `authSimpleCanMatch` based on Simple Web Token authentication style - `authJWTCanActivate`, `authJWTCanActivateChild`, `authJWTCanMatch` based on Json Web Token authentication style --- --- order: 2 title: Rule Data type: Documents --- ## Foreword The Mock rule data is an `Object` object, Key is request declaration, and Value is response data, for example: ```ts export const USERS = { 'GET /users': { users: [1, 2], total: 2 }, } ``` When send requesting `/users` via `HttpClient`, it will directly response `{ users: [1, 2], total: 2 }`, and will not send any HTTP requests in `Network` panel. ## Key Use `' '`space to separate the request method and URL, the request method can be ignored, the default is `GET`; the URL supports routing parameters and regular expressions. E.g: ```ts export const USERS = { 'GET /users': null, // GET: can be ingored '/users/1': null, // POST 'POST /users/1': null, // Routing parameters '/users/:id': null, // Regular expressions need to be wrapped with `()` '/data/(.*)': null }; ``` ## Value Supports three types: `Object`, `Array`, `(req: MockRequest) => any | Observable | Promise`. ```ts import { MockStatusError } from '@delon/mock'; export const USERS = { // Array '/users': [ { uid: 1 }, { uid: 2 } ], // Object '/users': { uid: 1 }, // Function '/qs': (req: MockRequest) => req.queryString.pi, // Support HttpResponse '/http': (req: MockRequest) => new HttpResponse({ body: 'Body', headers: new HttpHeaders({ 'token': '1' }) }), // Send Status Error '/404': () => { throw new MockStatusError(404); }, // Support Observable '/obs': () => of(1), // Support Promise '/promise': async () => { await delay(10); return 1; } }; ``` ### MockRequest Name | Type | Description ------------|--------------------|------------------------------------------------------------------ `[params]` | `any` | Routing parameter, `/:id` then `params.id` `[queryString]` | `any` | URL parameter, `/users?pi=1&ps=10` then `queryString.pi`, `queryString.ps` `[headers]` | `any` | Headers `[body]` | `any` | Body `[original]` | `HttpRequest` | `HttpRequest` ### MockStatusError When you want to respond to a `404` http status. ## Demo ```ts import { MockStatusError } from '@delon/mock'; export const USERS = { // Support object or array values 'GET /users': { users: [1, 2], total: 2 }, // GET: can be ingored '/users/1': { users: [1, 2], total: 2 }, // POST 'POST /users/1': { uid: 1 }, // Get request parameters: queryString、headers、body '/qs': (req: MockRequest) => req.queryString.pi, // Routing parameters '/users/:id': (req: MockRequest) => req.params, // /users/100, output: { id: 100 } // Send Status Error '/404': () => { throw new MockStatusError(404); }, // Regular expressions need to be wrapped with `()` '/data/(.*)': (req: MockRequest) => req, // Support Observable '/obs': () => of(1), // Support Promise '/promise': async () => { await delay(10); return 1; } }; ``` ## Storage rule In general, Mock is required during development, so recommended to create `_mock` directory in the project root directory and create `index.ts` file to export all data rules. See [ng-alain/_mock]( Https://github.com/ng-alain/ng-alain/tree/master/_mock). --- --- order: 2 title: Schema type: Documents --- ## Prologue [JSON Schema](http://json-schema.org/) is a specification to define JSON data structure, it doesn't include detailed explanation about how to convert the specification to specific forms, `@delon/form` is a dynamic form library developed based on our own understanding of JSON Schema and current data input components of `ng-zorro-antd`. JSON Schema **must always** have a type `type="object"` as **root node**,therefore a simplest Schema structure at least is: ```ts schema = { type: 'object', // optional, set to `object` by default properties: {} } ``` Ahead of dscribing Schema, it is necessary to make a systematic description about the relationship between form elements and Schema. As we know, form is a set of HTML elements, every element maps to one Schema property, a property has it's own data type, format, visual information, etc., but this is not enough to describe the rich APIs provided by `ng-zorro-antd`. In order to better use these APIs, `@delon/form` not only implemented most standard JSON Schema, but also added an additional property `ui`, which is used to describe how to render the property. ### Non Pollution Of course, you can set `` to add additional UI rendering if you have strict requirement about standard, or JSON Schema data is generated from backend. For example: ```ts schema = { properties: { url: { type: 'string', title: 'Web Site' } } } ``` A URL property, the pure JSON Schema structure cann't describe about adding prefix `https://`, but `nz-input` supports very rich prefix and postfix texts, so we can customize it in `ui` to add prefix `https://`: ```ts ui = { $url: { addOnBefore: 'https://' } } ``` `ui` itself is a JSON structure, in order to distinguish with relationship of JSON Schema property, **must** add prefix `$` to all properties; must replace array elements with `$items`. When KEY is `*`, it is valid for all properties. ### Relationship between Form and Data Structure We think a complete form should include some of following elements: ![](./assets/img/form-input.png) Description from left to right: | Structure Source | Parameter | Description | Type | Default Value | |------------------|-----------|-------------|------|---------------| | Schema | `[required]` | If required | `string[]` | - | | Schema | `[title]` | Title | `string` | - | | ui | `[optional]` | Optional information | `string` | - | | ui | `[optionalHelp]` | Optional help information | `string, SFOptionalHelp` | - | | ui | `[placeholder]` | Placeholder | `string` | - | | Schema | `[description]` | Description for the property | `string` | - | | - | `[error]` | Error information | `string` | - | ### Some Specifications - Following camelCase to name `key` - You can ignore description marked as **Not recommended** if you are very familiar with JSON Schema. ## JSON Schema(SFSchema) JSON Schema has complete specification descrbes for each property, `@delon/form` is currently based on specification [draft-07](http://json-schema.org/), following is the detailed explanation of specification: ### Common Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[type]` | Data type, support JavaScript basic types | `number,string,boolean,object,array` | `object` | | `[enum]` | Enum, static data source | `SFSchemaEnumType[]` | - | ### Value Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[minimum]` | Minimum value | `number` | - | | `[exclusiveMinimum]` | If excluding `minimum` value | `boolean` | - | | `[maximum]` | Maximum value | `number` | - | | `[exclusiveMaximum]` | If excluding `maximum` value | `boolean` | - | | `[multipleOf]` | Multiple | `number` | - | **About exclusiveMinimum and exclusiveMaximum** The implementation mechanism of `sf` causes that it couldn't handle error capturing for `type` perpectly, therefore `sf` ignores all `type` (see [config.ts](https://github.com/ng-alain/delon/blob/master/packages/form/src/config.ts#L12)) errors by default, these two kinds of errors are considered as `type` error, which will trigger invalid check. (find more details from [#676](https://github.com/ng-alain/ng-alain/issues/676#issuecomment-420208459)) ### String Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[maxLength]` | Maximum length of string | `number` | - | | `[minLength]` | Minimum length of string | `number` | - | | `[pattern]` | Regular expression | `string` | - | ### Array Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[items]` | Array element description, only support array object. Can use other components if array of basic type is needed | `SFSchema` | - | | `[minItems]` | Minimum number of element in array | `number` | - | | `[maxItems]` | Maximum number of element in array | `number` | - | | `[uniqueItems]` | Element is unique in array | `boolean` | - | | `[additionalItems]` | additional validation rules for array | `SFSchema` | - | ### Object Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[maxProperties]` | Maximum number of property, must be a nonnegative integer | `number` | - | | `[minProperties]` | Maximum number of property, must be a nonnegative integer | `number` | - | | `[required]` | If required | `string[]` | - | | `[properties]` | Propery definition | `{ [key: string]: SFSchema }` | - | ### Condition Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[if]` | Condition validation | `SFSchema` | - | | `[then]` | Condition validation | `SFSchema` | - | | `[else]` | Condition validation | `SFSchema` | - | Validation of condition check is very strong and rich, but considering it breaks UI and adds complexity to component build, `@delon/form` only implements `required`, and uses it to determine if need validation, for example, a login page, it can show different login mode based on different login methods: ```ts schema: SFSchema = { properties: { type: { type: 'string', enum: [ 'mobile', 'name' ], default: 'mobile' }, name: { type: 'string' }, pwd: { type: 'string' }, mobile: { type: 'string' }, code: { type: 'string' } }, required: [ 'type' ], if: { properties: { type: { enum: [ 'mobile' ] } } }, then: { required: [ 'mobile', 'code' ] }, else: { required: [ 'name', 'pwd' ] } }; ``` For above configuraion, eventual behavior is showing `mobile` and `code` in UI when login method is `mobile`, otherwise, showing `name` and `pwd`. Actually, condition type is eventually parsed to `ui.visibleIf`, Convert it to the following: ```ts { properties: { login_type: { type: "string", title: "登录方式", enum: [ { label: "手机", value: "mobile" }, { label: "账密", value: "account" } ], default: "mobile", ui: { widget: "radio", styleType: "button" } }, mobile: { type: "string", ui: { visibleIf: { login_type: val => val === "mobile" } } }, code: { type: "number", ui: { visibleIf: { login_type: val => val === "mobile" } } }, name: { type: "string", ui: { visibleIf: { login_type: val => val === "account" } } }, pwd: { type: "string", ui: { type: "password", visibleIf: { login_type: val => val === "account" } } } }, required: ["login_type"] }; ``` ### Logic Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[allOf]` | **Not recommended**, can be replaced by `required` | `SFSchema[]` | - | | `[anyOf]` | **Not recommended**, can be replaced by `required` and `minProperties` | `SFSchema[]` | - | | `[oneOf]` | **Not recommended**, value must be one of | `SFSchema[]` | - | > **Not recommended**, mainly because there is no UI handle for logic type, it's similar to condition type, will affect UI rendering. ### Format and Visual Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[title]` | Title | `string` | - | | `[description]` | Description | `string` | - | | `[default]` | Default value | `any` | - | | `[readOnly]` | If read only, equals to `nzDisabled` | `boolean` | - | | `[format]` | Data format, [Doc](http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3) | `string` | - | ### Other | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[definitions]` | Internal definition | `SFSchemaDefinition` | - | | `[$ref]` | Reference definition | `string` | - | | `[$comment]` | Comment for developer, no real meaning, won't be validated | `string` | - | ### Non Standard | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[ui]` | UI configuration, has more priority than `ui` property of `sf` component | `SFUISchemaItem` | - | ## UI(SFUISchemaItem) UI Schema structure is composed by commonality and widgets, following is descriptioin of commonality part, please refer to widget API for widget part. > In order to keep integrity of API, Schema of widget may includes commonality information. ### SFUISchema Equals to ``, a group of UI structure corresponds to JSON Schema structure, type is: `[ key: string ]: SFUISchemaItem`。 ### Basic Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[debug]` | Debug mode | `boolean` | - | | `[order]` | Order of property | `string[]` | - | | `[asyncData]` | Asynchronized static data source | `(input?: any) => Observable` | - | | `[hidden]` | Whether to hide | `boolean` | `false` | | `[visibleIf]` | Is visible with conditions | `{ [key: string]: any[] | ((value: any, property: FormProperty) => boolean) }` | - | | `[visibleIfLogical]` | The logical used when specifying multiple `visibleIf` | `or, and` | `or` | | `[acl]` | ACL permission (Use `can()` verify) | `ACLCanType` | - | **visibleIf** Is visible with conditions, for example: - `visibleIf: { shown: [ true ] }`: show current property when `shown: true` - `visibleIf: { shown: [ '$ANY$' ] }`: show current property when `shown` is any value - `visibleIf: { shown: (value: any, property: FormProperty) => value > 0 }`: complex expression ### Validation Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[liveValidate]` | If realtime validation | `boolean` | `true` | | `[firstVisual]` | If show visual error immediately | `boolean` | `false` | | `[onlyVisual]` | If only show visiual error not error text | `boolean` | `false` | | `[ingoreKeywords]` | Ignore validation for some data types | `string[]` | | | `[errors]` | Customized error text | `{ [ key: string ]: string | ((obj: ErrorData) => string) }` | - | | `[showRequired]` | Whether to display the required logo * | `boolean` | - | | `[validator]` | Custom verification, the final result will be merged with Ajv verification results | `(value: any, formProperty: FormProperty, form: PropertyGroup) => ErrorData[]` | - | ### Array Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[items]` | UI of specific sub element | `SFUISchema` | - | | `[addTitle]` | Add Title | `string` | `Add` | | `[addType]` | Add button style, equals to `nzType` | `string` | `dashed` | | `[removable]` | If show remove button | `boolean` | - | | `[removeTitle]` | Text of remove button | `string` | `Remove` | ### Form Element Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[type]` | `type` of `input` | `string` | `text` | | `[placeholder]` | placeholder | `string` | - | | `[autofocus]` | If auto focus during loading | `boolean` | - | ### Render Type | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[widget]` | Widget | `string` | - | | `[i18n]` | Refers to the i18n key of `schema.title` | `string` | - | | `[descriptionI18n]` | Refers to the i18n key of `schema.description` | `string` | - | | `[class]` | Customized class, equals to `[ngClass]` | `string,string[]` | - | | `[width]` | Width, unit: `px` | `number` | - | | `[size]` | Size of element | `default,large,small` | - | | `[grid]` | Property for responsive | `SFGridSchema` | - | | `[optional]` | Optional | `string` | - | | `[optionalHelp]` | Optional help | `string, SFOptionalHelp` | - | | `[collapse]` | Mark this field as collapsible when form is collapsed | `boolean` | - | ### Responsive Property SFGridSchema `grid` equals to complete [Grid](https://ng.ant.design/components/grid/en), can determine how to render the form by `grid` | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[gutter]` | Gutter | `number` | - | | `[span]` | Number of column for each element, `0` means `display: none` | `number` | - | | `[xs]` | `<768px` responsive grid, can be number of columns or including object of other properties | `number, SFGridSizeSchema` | - | | `[sm]` | `≥768px` responsive grid, can be number of columns or including object of other properties | `number, SFGridSizeSchema` | - | | `[md]` | `≥992px` responsive grid, can be number of columns or including object of other properties | `number, SFGridSizeSchema` | - | | `[lg]` | `≥1200px` responsive grid, can be number of columns or including object of other properties | `number, SFGridSizeSchema` | - | | `[xl]` | `≥1600px` responsive grid, can be number of columns or including object of other properties | `number, SFGridSizeSchema` | - | | `[xxl]` | Reserved field, support after version `0.7.0` | `number, SFGridSizeSchema` | - | ### Horizontal Layout Type > The sum of label and control **must** be `24` | Parameter | Description | Type | Default Value | |-----------|-------------|------|---------------| | `[spanLabel]` | Number of column for `label` | `number` | 5 | | `[spanControl]` | Number of column for form element | `number` | 19 | | `[offsetControl]` | Number of column for left side of `control` | `number` | - | | `[spanLabelFixed]` | Fixed width for `label` | `number` | - | --- --- order: 20 title: zh-CN: 发送Token en-US: Send Token type: Documents --- ## Authentication style It is better to add the corresponding authentication information to each request through the HTTP interceptor. `@delonn/auth` implements two separate HTTP interceptors based on two different authentication styles. ### authSimpleInterceptor The parameter name and its sending location can be specified via `DelonAuthConfig`, for example: ```ts token_send_key = 'token'; token_send_template = 'Bearer ${token}'; token_send_place = 'header'; ``` Indicates the `{ token: 'Bearer token_string' }` data in the `header` of each request. ### authJWTInterceptor It is a standard JWT sending rule that automatically adds `{ Authorization: 'Bearer token_string' }` to `header`. ### How to choose? `authSimpleInterceptor` is a very liberal style, you can put `token` in the request body, request header, etc. `authJWTInterceptor` is a JWT standard, which needs to ensure that the backend also uses such standards. ## How to load In `app.config.ts` file: ```ts providers: [ provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authSimpleInterceptor, defaultInterceptor])), // Or JWT provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authJWTInterceptor, defaultInterceptor])), ] ``` ## Example [DEMO](//ng-alain.github.io/ng-alain/#/passport/login), login or other login method, and observe the changes in the browser localStorage storage data. --- --- order: 40 title: sta subtitle: Swagger API generator type: Documents --- ## Foreword If the back-end API documentation is described by Swagger, a complete set of API codes can be generated through the following commands: ```bash ng g ng-alain:sta --url=https://petstore3.swagger.io/api/v3/openapi.json ``` ## Command format ```bash ng g ng-alain:sta --name= --url= --filePath= --output= ``` | Name | Default | Description | |------|---------|-------------| | `name` | `sta` | Name for swagger project name | | `url` | - | URL to swagger schema, Choose one of filePath and url. | | `filePath` | - | Path to swagger schema, Choose one of filePath and url. | | `output` | `src/app/${name}` | Path to folder where will been located the created api module | | `responseDataField` | - | The real data field of Response | | `modelTypePrefix` | - | Model name prefix | | `httpClientType` | `delon` | HttpClient request method, 1. `delon` use `_HttpClient` of `@delon/theme`, 2. `angular` use `HttpClient` | | `generateApiOptions` | - | swagger-typescript-api [options](https://github.com/acacode/swagger-typescript-api#-usage) | | `tagsMapping` | - | Swagger tag mapping dictionary | ## Use config file Add `sta.json` to the project root directory: ````json { "$schema": "./node_modules/ng-alain/sta/schema.json", "filePath": "swagger.json", "tagsMapping": { "部门": "Dept" } } ```` Run: ```bash ng g ng-alain:sta ```` ## FAQ ### Path and Service Association By default, the first `tags` of each `path` will be merged into one Service. Please use `[a-zA-Z][-_a-zA-Z]+` to describe `tag` as much as possible. ### Unexpected name By default, it will be processed according to the `operationId` item, otherwise it will be automatically combined according to the `path` and `method`. A few ways to turn on the languages: **.NET CORE** ```cs // Swashbuckle services.AddSwaggerGen(c => { c.CustomOperationIds(e => { var name = e.ActionDescriptor.RouteValues["action"] ?? ""; return name[0].ToString().ToLower() + name.Substring(1); }); } ``` **JAVA** Please refer to [Configuring the output of operationId in a Swagger 2.0 spec](https://springfox.github.io/springfox/docs/snapshot/#configuring-the-output-of-operationid-in-a-swagger-2-0-spec). ### Global Response When all `path`s have a fixed output format, such as success and exceptions have a unified format, they all return: ```json { "status": 200, "error": "Error Message", "result": {} } ``` If an interceptor is used to handle exception messages, when subscribing only needs to always get the data of the `result` field, it can be solved by specifying `--responseDataField="result"`. --- --- order: 15 title: zh-CN: 存储Token en-US: Store Token type: Documents --- ## How to use The `ITokenService` interface (the default implementation of `TokenService`) has only four methods and the `login_url` attribute: - `set(data: ITokenModel): boolean` Set authentication information and trigger `change` - `get(): ITokenModel` Get authentication information - `clear()` clears the authentication information and triggers the `change` parameter to be `null` - `change(): Observable` Subscribe to authentication information change callback - `login_url` Get the login address, equivalent to the value configured by `forRoot()` Therefore, when the backend returns the corresponding authentication information during the login process, as long as the `ITokenModel` interface object is met, the `set` method can be called to store the authentication to `IStore` (the default implementation `LocalStorageStore`). ```ts constructor(@Inject(DA_SERVICE_TOKEN) service: ITokenService) { service.set({ token: `asdf` }); service.get().token; // output: asdf } ``` ## Storage type The default is to use `withLocalStorage` persistent storage. You can change other storage methods in `app.config.ts`. ```ts providers: [ provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authJWTInterceptor, defaultInterceptor])), provideAuth(withLocalStorage()), ] ``` Contains three storage types: ### withLocalStorage `localStorage` storage, **not lost after closing the browser**. ### withSessionStorage `sessionStorage` storage, **lost after closing the browser**. ### withMemoryStorage Memory storage, **lost after closing the browser tab**. ### withCookie `cookie` storage. --- --- order: 90 title: en-US: Style Guide zh-CN: 编码规范建议 type: Advance --- The project built by Angular CLI is already very good in terms of its directory structure. Official website also has an [Angular Style Guide](https://angular.io/guide/styleguide) ([Chinese version](https://angular.cn/guide/styleguide)) Style guide. It is recommended to read it several times. In addition, NG-ALAIN also has a part of the coding style, the following instructions may be useful for reading the code. ## Consistent code style NG-ALAIN uses [ESLint](https://eslint.org/) to **guarantee code quality** and [Prettier](https://prettier.io/) to **optimize the code style**. It is recommended to install several plugins for more friendly development in vscode: - [ng-zorro snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-zorro-vscode) - [ng-alain snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) Of course, NG-ALAIN has prepared a complete set of extension packs for everyone, just install [NG-ALAIN Extension Pack](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-extension-pack). ## Git - pre commit Hook The `ng lint` command provided by Angular, can be very effective in helping us find bugs and readability earlier. Isn't it cool if we can guarantee that the lint of the file in staged is automatically done before each commit in the team development process? NG-ALAIN is configured to do lint each time you commit to staged and you can't commit if you find an error. By default, the `*.ts`, `*.less` commit process forces the formatting of the code. You can change the rules by modifying the `husky` node of `package.json`. > If **hint: The'.husky/pre-commit' hook was ignored because it's not set as executable.** is generated when executing `git commit -m "commit"`, it may be due to permission issues. Try to execute in the project root directory: ```bash chmod ug+x .husky/* chmod ug+x .git/hooks/* ``` ## Style guide ### API documentation Applications are always inevitable for the development of business components. We can not guarantee that you can remember these after a certain time. So, make sure to include the `README.md` document in each business component and describe the API, DEMO and other information. For example: ```markdown ## When do you use it? Instruction ## DEMO Instruction ## API Parameter | Description | Type | Default ----|------|-----|------ src | The map's address | `string` | - ``` ### Module registration Please parameter [module registration guidelines](/docs/module). ## Auxiliary item NG-ALAIN is configured with some options for the CLI to better code. ### CLI Vscode is the best choice for writing Angular. You can type: `ng g c list` in any directory of the project to generate the corresponding files for the component. NG-ALAIN is configured by default without generating style files & unit tests. So you will see that only `.ts`, `.html` are generated. This is because NG-ALAIN provides a very rich style API, and custom styles are not just needed on most pages. At the same time, unit tests. Of course, you can easily adjust the default configuration in `angular.json`. ### VSCode snippets VSCode is the best choice for writing Angular. Naturally NG-ALAIN also created the corresponding snippets extension: [ng-zorro-vscode](//marketplace.visualstudio.com/items?itemName=cipchk.ng-zorro-vscode) and [ng-alain-vscode](//marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode). --- --- type: Theme order: 2 title: Style Tools --- ng-alain builds a set of style tools for size, spacing, color, and more based on Ant Design. > Install [ng-alain snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) plugin in VSCode for intellisense these class names. ## Spacing ng-alain believes that these styles tool only using in the content area. Built on the design principles developed by Ant Design, A spacing width is based on `8px` as a reference unit, and derived from three rule sizes. | Name | Formula | Size | Description | | ---- | --- | --- | --- | | `xs` | $gutter / 2 | `4px` | Smaller | | `sm` | $gutter | `8px` | Small | | `md` | $gutter * 2 | `16px` | Medium | | `lg` | $gutter * 3 | `24px` | Large | | `xl` | $gutter * 4 | `32px` | Extra Large | | `xxl` | $gutter * 6 | `48px` | Oversized | According these rules, derived from `margin`,`padding`, and naming rules as follows: - Type: `padding`, `margin` - Direction(Optional): `top`, `right`, `bottom`, `left`, `x`(equal to `left`, `right`), `y`(equal to `top`, `bottom`) **Clean** ```regex [p|m][t|r|b|l|x|y]?0 ``` **Name Rule** ```regex [p|m][t|r|b|l|x|y]?-[sm|md|lg] ``` Demo: ```css .p-sm { padding: 8px !important; } .pt-sm { padding-top: 8px !important; } .m-sm { margin: 16px !important; } .mt-md { margin-top: 16px !important; } .p0 { padding: 0 !important; } ``` ## Color Ant Design does not have a button color, but is based on visual effects. The default is daybreak blue, for example: button type `primary` of `nz-button`. ng-alain still does not break this rule, building a set of color classes for text and background based on the [color](//ant.design/docs/spec/colors) section. | Name | Primary Color | Description | | ---- | --- | --- | | `red` |
#f5222d
| Dust Red: Fighting, unrestrained | | `volcano` |
#fa541c
| Volcano: Eye-catching | | `orange` |
#fa8c16
| Sunset Orange: Warm and cheerful | | `gold` |
#faad14
| Calendula Gold: Vital and active | | `yellow` |
#fadb14
| Sunrise Yellow: Birth, sunshine | | `lime` |
#a0d911
| Lime: Natural, vital | | `green` |
#f5222d
| Polar Green: Health, innovation | | `cyan` |
#13c2c2
| Cyan: Hope, strong | | `blue` |
#1890ff
| Daybreak Blue: Inclusive, technology, Pratt & Whitney | | `geekblue` |
#2f54eb
| Geek Blue: Explore and delve into | | `purple` |
#722ed1
| Golden Purple: Elegant, romantic | | `magenta` |
#eb2f96
| Magenta: Smooth, neutral | Ant Design's base color palette totals 120 colors, including 12 primary colors and their derivative colors. These colors can basically include the need for color in background applications design. **Category** | Name | Color Size | | ---- | --- | | `light` | 5 | | `normal` | 6 | | `dark` | 7 | **Name Rule** ```regex [text|bg]-[red|volcano|orange|gold|yellow|lime|green|cyan|blue|geekblue|purple|magenta|grey](-[light|dark])?(-h)? ``` > `normal` itself is the basic color, so it can be ignored > `grey` is very often used, so add extra `grey-light`, `grey-darker` alias to indicate depth DEMO: ```less // Text color .text-red-light { color: #fabeb9 !important; } .text-red { color: #f04134 !important; } .text-red-dark { color: #a31837 !important; } // background-color color .bg-red-light { background-color: #fabeb9 !important; } .bg-red { background-color: #f04134 !important; } .bg-red-dark { background-color: #a31837 !important; } // hover color .bg-red-light-h { &:hover { background-color: #fabeb9 !important; } } .bg-red-h { &:hover { background-color: #f04134 !important; } } .bg-red-dark-h { &:hover { background-color: #a31837 !important; } } ``` ### Aliase | Aliase | Color Name | | ---- | --- | | `primary` | `@blue-6`
#1890ff
| | `success` | `@green-6`
#52c41a
| | `error` | `@red-5`
#ff4d4f
| | `warning` | `@gold-6`
#faad14
| | `info` | `@blue-6`
#1890ff
| | `processing` | `@blue-6`
#1890ff
| | `highlight` | `@red-5`
#ff4d4f
| | `normal` | `#d9d9d9`
#d9d9d9
| DEMO: ```less // Text color .text-error-light { color: #fabeb9 !important; } .text-error { color: #f04134 !important; } .text-error-dark { color: #a31837 !important; } // background-color color .bg-error-light { background-color: #fabeb9 !important; } .bg-error { background-color: #f04134 !important; } .bg-error-dark { background-color: #a31837 !important; } // hover color .bg-error-light-h { &:hover { background-color: #fabeb9 !important; } } .bg-error-h { &:hover { background-color: #f04134 !important; } } .bg-error-dark-h { &:hover { background-color: #a31837 !important; } } ``` **Full Colors** You can use `@enable-all-colors: true` to turn on all 120 color rules. ```less .text-red-1 { color: #fff1f0 !important; } .text-red-6 { color: #f04134 !important; } .text-red-10 { color: #5c0011 !important; } .bg-red-6 { color: #f04134 !important; } .bg-red-6-h { color: #f04134 !important; } ``` ## Clearfix Clear floats `.clearfix`. ## Display | Aliase | CSS | | ---- | --- | | `d-none` | `display: none` | | `d-block` | `display: block` | | `d-inline-block` | `display: inline-block` | | `d-flex` | `display: flex` | | `d-inline-flex` | `display: inline-flex` | | `justify-content-start` | `justify-content: flex-start` | | `justify-content-end` | `justify-content: flex-end` | | `justify-content-center` | `justify-content: center` | | `justify-content-between` | `justify-content: space-between` | | `justify-content-around` | `justify-content: space-around` | | `align-items-start` | `align-items: flex-start` | | `align-items-end` | `align-items: flex-end` | | `align-items-center` | `align-items: center` | | `align-items-baseline` | `align-items: baseline` | | `align-items-stretch` | `align-items: stretch` | | `align-content-start` | `align-content: flex-start` | | `align-content-end` | `align-content: flex-end` | | `align-content-center` | `align-content: center` | | `align-content-between` | `align-content: space-between` | | `align-content-around` | `align-content: space-around` | | `align-content-stretch` | `align-content: stretch` | | `align-self-auto` | `align-self: auto` | | `align-self-start` | `align-self: flex-start` | | `align-self-end` | `align-self: flex-end` | | `align-self-center` | `align-self: center` | | `align-self-baseline` | `align-self: baseline` | | `align-self-stretch` | `align-self: stretch` | | `flex-center` | `display: flex; align-items: center;` | | `flex-center-between` | `display: flex; align-items: center; align-content: space-between;` | ## Position | Name | Description | | ---- | --- | | `overflow-auto` | `overflow: auto` | | `overflow-hidden` | `overflow: hidden` | | `fixed-top` | Fixed top | | `fixed-bottom` | Fixed bottom | ## Text ### Sizing Ant Design is based on `14px`. | Name | Description | | ---- | --- | | `text-xs` | `12px` | | `text-sm` | `14px` | | `text-md` | `16px` | | `text-lg` | `18px` | | `text-xl` | `22px` | ### Alignment | Name | Description | | ---- | --- | | `text-left` | Text left | | `text-center` | Text center | | `text-right` | Text right | ### Overflow > The container must be `display: inline-block` or `display: block`. | Name | Description | | ---- | --- | | `text-nowrap` | Outputs a single line | | `text-truncate` | Truncate string with ellipsis | ### Transformation | Name | Description | | ---- | --- | | `text-lowercase` | Lowercase of text | | `text-uppercase` | Uppercase of text | | `text-capitalize` | Capitalize of text | | `text-deleted` | Deleted line | ### Weight and italics | Name | Description | | ---- | --- | | `font-weight-normal` | `font-weight: normal` | | `font-weight-bold` | `font-weight: 700` | | `font-italic` | `font-style: italic` | ### Other | Name | Description | | ---- | --- | | `text-hover` | `*:hover { color: @primary-color; }` | | `disabled` | Disabled | ## Borders ### Border | Name | Description | | ---- | --- | | `border` | `border: 1px solid #f5f5f5 !important;` | | `border-0` | `border: 0 !important;` | | `border-top-0` | `border-top: 0 !important;` | | `border-right-0` | `border-right: 0 !important;` | | `border-bottom-0` | `border-bottom: 0 !important;` | | `border-left-0` | `border-left: 0 !important;` | ### Color Supports all color & aliase of [color section](/theme/tools#color), such as `border-red`, `border-success`. ### Rounded | Name | Description | | ---- | --- | | `rounded-0` | `border-radius: 0;` | | `rounded-circle` | `border-radius: 50%;` | | `rounded-sm` | `border-radius: 2px;` | | `rounded-md` | `border-radius: 4px;` | | `rounded-lg` | `border-radius: 6px;` | ## Width | Name | Description | | ---- | --- | | `width-sm` | `160px` | | `width-md` | `240px` | | `width-lg` | `320px` | | `width-[0-10]` | `0%-100%` | ## Responsive Like Bootstrap responsive rules, all apply `hidden-xs` classes are hidden when screen less than `480px`. | Name | Screen | | ---- | --- | | `hidden-xs` | <480px | | `hidden-sm` | <768px | | `hidden-md` | <992px | | `hidden-lg` | <1200px | | `hidden-pc` | <768px | | `hidden-mobile` | >768px | ## Rotate ``` .rotate-[360 / 15] ``` DEMO: ```css // Rotate 15 degrees .rotate-15 // Rotate 90 degrees .rotate-90 // Rotate 360 degrees .rotate-360 ``` ## Other | Name | Description | | ---- | --- | | `block-center` | `margin: 0 auto` | | `point` | `cursor: pointer` | | `no-data` | No result | | `no-resize` | Setting does not allow adjustment elements | | `bg-center` | Background image is vertically centered | | `scrollbar` | Custom scrollbar for a div | | `color-weak` | Weak mode | ## Widgets ### Masonry Online [DEMO](https://ng-alain.surge.sh/#/style/gridmasonry)。 | Name | Description | | ---- | --- | | `row-masonry` | Rows | | `row-masonry-{xs|sm|md|lg|xl}-{1-10}` | Rows, Responsive style | | `col-masonry` | Columns | ## ng-zorro ### nz-card | Name | Description | | ---- | --- | | `ant-card__body-nopadding` | Force body without spacing | ### nz-modal | Name | Description | | ---- | --- | | `modal-{sm|md|lg|xl}` | Set size of modal, `wrapClassName: 'modal-lg'` | | `modal-body-nopadding` | Without padding in body element | | `modal-header` | Use html template to custom modal, [DEMO](https://ng-alain.surge.sh/#/extras/poi) | | `modal-footer` | Use html template to custom modal, [DEMO](https://ng-alain.surge.sh/#/extras/poi) | ### nz-table | Name | Description | | ---- | --- | | `ant-table-rep__title` | Title | | `ant-table-rep__hide-header-footer` | Show title or bottom when mobile screen | [comment]: ### nz-tag | Name | Description | | ---- | --- | | `ant-tag__plus` | Add button style | --- --- order: 2 title: TokenService type: Documents --- `ITokenService` contains some service class for Token operations, such as get current Token information: ```ts constructor(@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService) { console.log(tokenService.get().token); // When JWT console.log(tokenService.get(JWTTokenModel).token); } ``` ## Expired refresh After subscribing to `refresh`, the token will be automatically triggered when it expires. When the backend supports Token refresh, the token can be refreshed before expiration to extend the user authorization period. > **Recommendation** `refresh` only subscribe once in the application. ## API ### Property | Name | Type | Description | |------|------|-------------| | `[login_url]` | `string` | Get the login address of `DelonAuthConfig` configuration | | `[referrer]` | `AuthReferrer` | Get routing before authorization failure | | `[refresh]` | `Observable` | Subscription refresh, automatically triggered when expired; **Note** It will be triggered multiple times, please make business process | ### Method | Name | Return Type | Description | |------|-------------|-------------| | `change()` | `Observable` | Token changed callback | | `set(data: ITokenModel)` | `boolean` | Set Token | | `get(type?: any)` | `ITokenModel` | Get Token | | `clear(options?: { onlyToken: boolean })` | `void` | Clear Token | --- --- order: 1000 type: Basic title: Upgrade to version 21.0 --- > This guide applies to the current version ng-alain >= `20`; > If you encounter problems during the upgrade process, feel free to comment here. > If you find any errors in this guide, please point out > Or you have encountered a new problem and solved it, welcome to comment here. ## Before upgrade 1. Make sure `Node.js` >= `22.21.1`. 2. Create a new branch, or use other methods to back up the current project. 3. Delete the `package-lock.json` or `yarn.lock` file. ### 1.Upgrade dependencies - Upgrade Angular to 21.x version, Run `ng update @angular/core@21 @angular/cli@21 angular-eslint@21 ng-zorro-antd@21 ng-alain@21`. - _Run `ng update @angular/cdk@21`, if you have used `@angular/cdk`._ > NG-ALAIN scaffolding upgrade all change files, please refer to: [#2593](https://github.com/ng-alain/ng-alain/pull/2593/files). ### 2. ng-zorro-antd BREAKING CHANGES Please note that NG-ZORRO has BREAKING CHANGES; please refer to https://github.com/NG-ZORRO/ng-zorro-antd/releases/tag/21.0.0 for details. ### 3. Reference - Code style configuration change [#2594](https://github.com/ng-alain/ng-alain/pull/2594/files) --- --- order: 60 title: Use a third-party lib type: Dev --- In addition to the NG-ALAIN base component and the @delon business component, sometimes we need to reference other external class libraries, the following is how to use the rich text component [ngx-tinymce](https://github.com/cipchk/ngx-tinymce): ## Angular Component ### Installing dependencies ```bash npm i -S ngx-tinymce ``` ### Import module You may need to use rich editor in all submodules, as it's recommended to import and export them in the `SharedModule` module. ```ts // #region third libs import { NgxTinymceModule } from 'ngx-tinymce'; const THIRDMODULES = [ NgxTinymceModule ]; // #endregion ``` > The `region: third libs` region is a coding convention for NG-ALAIN, register all third-party components into the `THIRDMODULES` variable, for more coding conventions, refer to [Style Guide](/docs/style-guide). For some third-party components, may be required global configuration. It's recommended to register in the root module, for example: ```ts import { NgxTinymceModule } from 'ngx-tinymce'; @NgModule({ imports: [ BrowserModule, NgxTinymceModule.forRoot({ baseURL: '//cdn.bootcss.com/tinymce/4.7.13/' }) ] }) export class AppModule { } ``` Next you can use `ngx-tinymce` in any submodule: ```html ``` ## Non-Angular Component Referencing a non-Angular component is actually loading a JavaScript class library file, such as the QR code library [qrious](https://github.com/neocotic/qrious/): ### Installing dependencies ```bash npm i -S qrious ``` ### Import Scripts Add `qrious.min.js` to the `scripts` node in `angular.json`: ```json "scripts": [ "node_modules/qrious/dist/qrious.min.js" ] ``` If the third-party library requires additional styles, you also need to add a path to `styles`. > Note: You need to re-run `ng s`. **Lazy loading script** The above import script method will package the code directly into `scripts.js`, which will cause the `scripts.js` to become larger. NG-ALAIN provides another delayed loading CDN library script solution for low-usage services. (Example: [zip](https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js) compression), you can use [LazyService](/util/lazy) to delay loading remote CDN scripts. ### How to use Angular is using TypeScript language, and all types must be clearly defined to be used. For details, please refer to [Angular How to use third-party libraries](https://zhuanlan.zhihu.com/p/35796451). A sample code for the call: ```ts declare var QRious: any; @Component() export class DEMOComponent { constructor() { const qr = new QRious(); } } ``` --- --- order: 3 title: Validation Error type: Documents --- ## Prologue A set of error messages may be created during JSON Schema validation, every error has a fixed `keyword`, you can configure `errors` in [global config](/docs/global-config) to override default error messages, you can also handle internationalization error messages in global config. Following is an example when it is failed to pass required field validation: ```json [{ "keyword": "required", "dataPath": ".client", "schemaPath": "#/required", "params": {"missingProperty":"client"}, "message":"Required Field" }] ``` `message` is used to render error information on page. > **Note:**Validation is triggered when it is first time rendering, but there is no any visual presentation, you can set `` to show error effect when it is first time rendering. ## Custom Error Information Support to [globally configure](/docs/global-config) `errors` (commonly used for internationalization) or configure `ui.errors` (for a specific property) to handle error information. **ui.errors** ```ts schema: SFSchema = { properties: { email: { type: 'string', title: 'Email', format: 'email', maxLength: 20, ui: { errors: { 'required': 'Required Field' } } } } }; ``` ### keyword No matter which way to build error information, `keyword` must be used to distinguish error type. You can find the full list of [ERRORSDEFAULT](https://github.com/ng-alain/delon/blob/master/packages/form/src/errors.ts#L4) here. ## Custom Validation JSON Schema cannot satisfy business requirements all the time, e.g. sometimes, need to distinguish different validation rules based on other properties: ### Property Validation ```ts schema: SFSchema = { properties: { type: { type: 'string', title: 'Type', enum: [ { value: 'mobile', label: 'Mobile' }, { value: 'email', label: 'email' }, ], default: 'mobile', }, mobile: { type: 'string', title: 'Mobile', ui: { visibleIf: { type: ['mobile'] }, showRequired: true, validator: val => (!val ? [{ keyword: 'required', message: 'Required mobile' }] : []), }, }, email: { type: 'string', title: 'Email', ui: { visibleIf: { type: ['email'] }, showRequired: true, validator: val => (!val ? [{ keyword: 'required', message: 'Required email' }] : []), }, }, pwd: { type: 'string', title: 'Password', ui: { type: 'password', }, }, }, required: ['type', 'pwd'], }; ``` ### Asynchronous Validation Following example show how to validate if a username exists asynchronously: ```ts schema: SFSchema = { properties: { name: { type: 'string', ui: { showRequired: true, validator: (value: any) => this.http.get(`/user/check/${value}`).pipe( map(res => res ? [ { keyword: 'required', message: 'Username exists'} ] : []) ) } } } }; ``` **Note:** Because a new instance is generated in every time of validation, some control operations are not able to do, e.g. `debounceTime`. ### setErrors Use the `setErrors` method to adjust error messages. ```ts this.sf.getProperty('/name')?.setErrors({ keyword: 'required' }); this.sf.getProperty('/name')?.setErrors({ message: 'Please input your username!' }); // Clean current error messages this.sf.getProperty('/name')?.setErrors(); ``` ## Visual Can configure [global config](/docs/global-config) or `ui.onlyVisual` to control if only show error visual but not error message. ## Debug JSON Schema has strong requirement on format, for example, date must follow the format described in [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6): ```ts { properties: { time: { type: 'string', ui: { widget: 'date', mode: 'range' }, title: 'Date', format: 'yyyy-MM-dd HH:mm:ss' } }, ui: { debug: true } } ``` `format` is an invalid format of date, detailed validation error information will be shown in console when `debug: true` is set. ``` Error: unknown format "yyyy-MM-dd HH:mm:ss" is used in schema at path "#/properties/time" ``` --- --- order: 40 title: Work with Server type: Dev --- NG-ALAIN is a single-page application based on the Angular technology stack. We provide development models for front-end code and native analog data. Work in the form of the Restful API with the server application of any technology stack. The basics of interacting with the server are briefly described below. ## Front-end request process In NG-ALAIN, a complete front-end UI interaction to the server-side processing flow looks like this: 1. Start Angular for the first time to execute `APP_INITIALIZER`; - Usually some APP general data is loaded before startup, such as currently authorized user data, menu data, dictionary data, configuration data, etc. 2. UI component interaction; 3. Send the request using the encapsulated [_HttpClient](/theme/http); 4. Trigger the user authentication interceptor [@delon/auth](/auth/getting-started) and add the `token` parameter uniformly; - If there is no `token` or an expired interrupt subsequent request, jump directly to the login page; 5. Trigger the default interceptor to process the prefix and other information; 6. Get the server back; 7. Trigger the default interceptor to handle request exceptions, business exceptions, etc. 8. Update the data and refresh the UI. ### Interceptor By default, two interceptors are registered in the root module.[SimpleInterceptor](https://github.com/ng-alain/delon/blob/master/packages/auth/token/simple/simple.interceptor.ts) with [DefaultInterceptor](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts)And the execution order is executed in the registration order. **SimpleInterceptor** [User Authentication](/auth) has built-in interceptors for automatically adding `token` parameters to requests. There is also a [JWTInterceptor](https://github.com/ng-alain/delon/blob/master/packages/auth/token/jwt/jwt.interceptor.ts) interceptor, which is a standard JWT specification. If the backend uses standard JWT, it can be directly replaced with a JWTInterceptor interceptor. **DefaultInterceptor** [DefaultInterceptor](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts) The interceptor simply provides an interceptor. By default, it contains sample code that handles server request prefixes, handles request exceptions, and business exceptions. You can make adjustments based on your own needs. **Important point** We can put the `next.handle(req)` as the demarcation point in the interceptor `intercept` method. The first part is the request and the `pipe` part is the post. This will make it clearer which part is to be done before the request and which part will be executed after the request. For more information on interceptors, please refer to the official website. ## Development environment Under normal circumstances, the development environment and the production environment are not the same back-end request source. You can actually configure it under the [environment](https://github.com/ng-alain/ng-alain/tree/master/src/environments) directory. [environment.ts](https://github.com/ng-alain/ng-alain/blob/master/src/environments/environment.ts) and [environment.prod.ts](https://github.com/ng-alain/ng-alain/blob/master/src/environments/environment.prod.ts) Change the request source for different environments. > environment is actually a JSON object, you can organize different forms to meet the problem of multiple request sources. ## Mock Sometimes when you want to develop the front-end first, you can use [@delon/mock](/mock) to simulate the request data. The actual principle is to use the interceptor to directly return the data to the matching URL instead of sending an HTTP request. By default, it is only valid for the test environment. Of course, you usually need to make sure that the data of the Mock interface is consistent with the backend. You can create the corresponding Mock interface in the `_mock` directory: ```ts export const USERS = { 'GET /users': { users: [1, 2], total: 2 } } ``` So for the test environment, when the `/users` request is encountered, the `{users: [1, 2], total: 2 }` data is returned directly. See [here](/mock) for more Mock syntax and usage. **Note:** When you don't need a Mock interface for a request, be sure to comment out or remove it. ## Cors Most applications will be front-end is separate from the back-end, which leads to CORS factors when a request is made to the back end, such as: ```ts http.get(`http://192.168.1.100/api/app`).subscribe(); ``` > Note: If the request does not start with `http`, each request will add `environment.SERVER_URL` as the leading edge of the request URL. The following error is returned directly: ``` Access to XMLHttpRequest at 'http://192.168.1.100/api/app' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. ``` There are usually two ways to solve cross-domain problems. One is to allow the back-end development environment to directly support CORS requests (not recommended, but simplest), the second is to use Angular Cli to provide [Proxy Support](https://webpack.js.org/configuration/dev-server/#devserver-proxy), the development proxy server will forward the domain and port of the request sent by Angular to the backend Server, CORS is a security restriction of the browser. There is no problem with CORS before the proxy server and the backend server. This is why many people try to make it clear that they can be requested in Postman but not in Angular. Assuming that all backend requests are prefixed with `/api`, you can configure all this prefix in `proxy.conf.js` to forward to the new backend, for example: ```js module.exports = { '/api': { target: 'http://192.168.1.100/api', secure: false } } ``` - `/api` Proxy path, domain is not supported - `target` Proxy target address - `secure` If the proxy target address is `https`, it should be set to `true`, otherwise it is `false` - `pathRewrite` Rewrite the address, such as `pathRewrite: {'^/api':'/'}` to change the prefix `/api` to `/` - `changeOrigin` Change the `host` of the host header to the target URL. Some backends will judge whether it is valid according to its value. You may need to set `true` - `logLevel` Set to `debug` to display the message forwarded by the agent on the terminal Abort more detail please refer to [Proxying to a backend server](https://angular.io/guide/build#proxying-to-a-backend-server), and for configuration description please refer to [http-proxy-middleware options](https://github.com/chimurai/http-proxy-middleware#options). ## Common problem **The request may be rejected or returned directly to `401`?** Scaffolding uses the `SimpleInterceptor` interceptor of `@delon/auth` by default, which causes an error to be returned directly if a token cannot be obtained during the request. [User Authentication](/auth) This process is a must for the middle office. **Unable to display request log** Starting from Angular 13, the debugging log of remote requests will no longer be displayed in the terminal. If you need to display the log, you can fix from refer to [How to fix logging for proxy in angular](https://medium.com/@gagandeep.sidhu88/how-to-fix-logging-for-proxy-in-angular-834cf46d437d). --- # Components --- order: 1 title: _date subtitle: Date type: Pipe --- Based on date-fns date formatting, see more details [date-fns](https://date-fns.org/v1.29.0/docs/format) (China mirror: [moment format](http://Momentjs.cn/docs/#/displaying/format/)) date-fns supports different kinds of time formats, such as: + 2018-08-24 18:08:20 + 2018-08-24 + 20180824 + 1503571962333 ```html {{data.registered | _date: 'yyyy年MM月dd日'}} ``` Output: ``` 2017年08月24日 ``` --- --- order: 1 title: _HttpClient type: Service --- [\_HttpClient](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/http/http.client.ts) service is based on Angular `HttpClient`. ## Features - More friendly call methods - Maintain `loading` attribute - Handling null values - Unified time format is timestamp - Support decorator @GET, @POST etc ## DEMO Network requests are generally used with Object as arguments, such as a `get` request: ```ts HttpClient.get(url, { params: { pi: 1 } }); ``` For `_HttpClient`: ```ts _HttpClient.get(url, { pi: 1 }); ``` ## AlainThemeConfig Common configuration, such as unifying null and time processing for `_HttpClient`. ```ts import { AlainThemeConfig } from '@delon/theme'; export function fnAlainThemeConfig(): AlainThemeConfig { return Object.assign(new AlainThemeConfig(), { http: { nullValueHandling: 'ignore', }, }); } @NgModule({}) export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ { provide: AlainThemeConfig, useFactory: fnAlainThemeConfig }, ], }; } } ``` ### API | Property | Description | Type | Default | |----------|-------------|------|---------| | `nullValueHandling` | Null processing | `include,ignore` | `include` | | `dateValueHandling` | Time processing | `timestamp,ignore` | `timestamp` | ## Decorators The target service must inherit `BaseApi` abstract class. ### Usage ```ts @BaseUrl('/user') @BaseHeaders({ bh: 'a' }) class RestService extends BaseApi { @GET() query(@Query('pi') pi: number, @Query('ps') ps: number): Observable { return; } @GET(':id') get(@Path('id') id: number): Observable { return; } @GET() get(@Payload data: {}): Observable { return; } // Use `::id` to indicate escaping, and should be will be ignored when `id` value is `undefined`, like this:   // When `id` is `10` => 10:type   // When `id` is `undefined` => :id:type @GET(':id::type') get(@Path('id') id: number): Observable { return; } @POST(':id') save(@Path('id') id: number, @Body data: Object): Observable { return; } @POST() save(@Payload data: {}): Observable { return; } @FORM() save(@Payload data: {}): Observable { return; } // If authorization is invalid, will be thrown directly `401` error and will not be sent. @GET('', { acl: 'admin' }) ACL(): Observable { return; } } ``` ### Class decorators - `@BaseUrl(url: string)` - `@BaseHeaders(headers: HttpHeaders | { [header: string]: string | string[] })` ### Method decorators - `@GET(url: string = '', options?: HttpOptions)` - `@POST(url: string = '', options?: HttpOptions)` - `@DELETE(url: string = '', options?: HttpOptions)` - `@PUT(url: string = '', options?: HttpOptions)` - `@HEAD(url: string = '', options?: HttpOptions)` - `@PATCH(url: string = '', options?: HttpOptions)` - `@JSONP(url: string = '', options?: HttpOptions)` - `@OPTIONS(url: string = '', options?: HttpOptions)` #### HttpOptions | Property | Description | Type | Default | |----------|-------------|------|---------| | `acl` | ACL config, depends on `@delon/acl` | `any` | - | | `observe` | Specify response content | `body,events,response` | - | | `responseType` | Specify content format | `arraybuffer,blob,json,text` | - | | `reportProgress` | Whether monitor progress events | `boolean` | - | | `withCredentials` | Set withCredentials | `boolean` | - | ### Parameter decorators - `@Path(key?: string)` URL path parameters - `@Query(key?: string)` QueryString of URL - `@Body` Body of URL - `@Headers(key?: string)` Headers of URL - `@Payload` Request Payload - Supported body (like`POST`, `PUT`) as a body data, equivalent to `@Body` - Not supported body (like `GET`, `DELETE` etc) as a `QueryString` ### CUSTOM_ERROR Whether to customize the handling of exception messages. ```ts this.http.post(`login`, { name: 'cipchk', pwd: '123456' }, { context: new HttpContext() .set(ALLOW_ANONYMOUS, true) .set(CUSTOM_ERROR, true) }).subscribe({ next: console.log, error: console.log }); ``` ### IGNORE_BASE_URL Whether to ignore API prefixes. ```ts // When environment.api.baseUrl set '/api' this.http.get(`/path`) // Request Url: /api/path this.http.get(`/path`, { context: new HttpContext().set(IGNORE_BASE_URL, true) }) // Request Url: /path ``` ### RAW_BODY Whether to return raw response body. --- --- title: acl subtitle: ACL type: Examples --- Combined with `@delon/acl` permissions, a Schema can be used to build forms for different roles or permission points. --- --- title: array subtitle: Array&Tree,flat,groupBy,uniq type: Tools --- ## ArrayService Used for conversion and access between arrays and trees. It is generally used with `nz-tree`. > You can override [ArrayService] to set the mapping name through [Global Configuration](/docs/global-config). ### treeToArr Convert tree structure to array structure. | Property | Description | Type | Default | |----------|-------------|------|---------| | `deepMapName` | Deep map name | `string` | `deep` | | `parentMapName` | Parent item map name of flat array | `string` | `parent` | | `childrenMapName` | Source data children map name | `string` | `children` | | `clearChildren` | Whether remove `children` item | `boolean` | `true` | | `cb` | Callback event | `(item: any, parent: any, deep: number) => void` | - | ### arrToTree Convert array structure to tree structure. > If `parent_id` is a string, the root value **Make Sure** is an empty string. | Property | Description | Type | Default | |----------|-------------|------|---------| | `idMapName` | Id map name | `string` | `id` | | `parentIdMapName` | Parent id map name | `string` | `parent_id` | | `rootParentIdValue` | Root parent id value, the most suitable root parent id value will be automatically calculated by default | `any` | - | | `childrenMapName` | Children map name | `string` | `children` | | `cb` | Callback event | `(item: any) => void` | - | ### arrToTreeNode Convert array structure to `nz-tree` data structure. | Property | Description | Type | Default | |----------|-------------|------|---------| | `idMapName` | Id map name | `string` | `id` | | `parentIdMapName` | Parent id map name | `string` | `parent_id` | | `titleMapName` | Title map name | `string` | `title` | | `isLeafMapName` | isLeaf map name, if value does not exist, include `children` value to determine whether it's a leaf node | `string` | `isLeaf` | | `checkedMapname` | Checked map name | `string` | `checked` | | `selectedMapname` | Selected map name | `string` | `selected` | | `expandedMapname` | Expanded map name (Except leaf nodes) | `string` | `expanded` | | `disabledMapname` | Disabled map name | `string` | `disabled` | | `cb` | Callback event | `(item: any, parent: any, deep: number) => void` | - | ### visitTree Recursive access tree. | Property | Description | Type | Default | |----------|-------------|------|---------| | `childrenMapName` | Children map name | `string` | `children` | ### findTree Return the value of the first tree value in the tree where predicate is true, and `undefined` otherwise. | Property | Description | Type | Default | |----------|-------------|------|---------| | `childrenMapName` | Children map name | `string` | `children` | ### getKeysByTreeNode Get all the selected `key` values. | Property | Description | Type | Default | |----------|-------------|------|---------| | `includeHalfChecked` | Whether include half-checked | `boolean` | `true` | | `keyMapName` | Whether re-specify `key` name | `string` | - | | `cb` | Callback event, return `key` value | `(item: NzTreeNode, parent: NzTreeNode, deep: number) => any` | - | ### flat Recursively flattens array. ```ts srv.flat([1, [2, 3, [4, 5, [6]]]]) => [1,2,3,4,5,6] srv.flat([1, [2, 3, [4, 5, [6]]]], 1) => [1,2,3,[4, 5, [6]]] ``` ### groupBy Group the array. ```ts srv.groupBy([6.1, 4.2, 6.3], Math.floor) => {"4":[4.2],"6":[6.1,6.3]} srv.groupBy(['one', 'two', 'three'], v => v.length) => {"3":["one","two"],"5":["three"]} ``` ### uniq Creates a duplicate-free version of an array. ```ts uniq([1, 2, 2, 3, 1]) => [1,2,3] uniq([{ a: 1 }, { a: 1 }, { a: 2 }], 'a') => [{"a":1},{"a":2}] uniq([{ a: 1 }, { a: 1 }, { a: 2 }], i => (i.a === 1 ? 'a' : 'b')) => [{"a":1},{"a":2}] ``` --- --- title: array subtitle: Array type: Widgets order: 2 --- Create array object, it's only valid when `schema.type="array"`. ## Layout Array layout is divided into array itself and array element layout, `arraySpan` determines the number of grid of each array element. UI embed in Schema: ```ts const schema: SFSchema = { properties: { list: { type: 'array', items: { type: 'object', properties: { a: { type: 'string' }, b: { type: 'number', ui: { spanLabel: 10 } } } }, ui: { spanLabel: 5, grid: { arraySpan: 12 } } } } }; ``` **Note:** All attributes in `items` are inherited from `list.ui`, eventually, `items.a` has `5` units, `items.b` has `10` units. Schema and UI are separated, above UI configuration will be converted to: ```ts const ui = { $list: { $items: { $b: { spanLabel: 10 } }, spanLabel: 5, grid: { arraySpan: 12 } } }; ``` ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[items]` | Description of array element | `SFSchema` | - | | `[minItems]` | Minimum number of array element | `number` | - | | `[maxItems]` | Maximum number of array element | `number` | - | | `[uniqueItems]` | Element in array is unique | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[addTitle]` | Add button title | `string` | `add` | | `[addType]` | Add button type, equals to `nzType` | `string` | `dashed` | | `[removable]` | Whether includes remove button | `boolean` | `true` | | `[removeTitle]` | Remove button title | `string` | `remove` | | `[required]` | Add required style to current item | `boolean` | - | | `[$items]` | UI description of array element | `SFUISchema` | - | | `(add)` | Add callback,`property` indicates form property after add | `(property: FormProperty) => void` | - | | `(remove)` | Remove callback | `(index: number) => void` | - | --- --- type: Other title: auto-focus subtitle: Auto focus description: Automatically focuses after the component is rendered cols: 1 module: import { AutoFocusModule } from '@delon/abc/auto-focus'; --- Allows to focus HTML-element right after its appearance, By default, it will take effect for `input` and `textarea` with `[autofocus="autofocus"]`. ## API ### [auto-focus] | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enabled]` | Whether enabled of auto focus | `boolean` | `true` | | `[delay]` | Delay of the focus (unit: ms) | `number` | `25` | | `(focus)` | Get focus callback | `void` | `-` | --- --- title: autocomplete subtitle: Autocomplete type: Non-built-in widgets --- Input complete automatically. ## How to use Non-built-in modules need to additionally register `withAutoCompleteWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Data Source **Static** Every filter after data got is filtered by `filterOption`, data source is from `asyncData`, `enum`. Email postfix is automatically added when it is `schema.format: 'email'`, by default, it is `['qq.com', '163.com', 'gmail.com', '126.com', 'aliyun.com']`, can adjust the value by setting `enum` or [global config](/docs/global-config/en) `uiEmailSuffixes`。 **Realtime** Every filter after data got is filtered by `filterOption`, data source is from `asyncData`. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Static data source | `SFSchemaEnumType[]` | - | | `[readOnly]` | Read only | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Realtime data | `(input: string) => Observable` | - | | `[size]` | Szie, equals to `nzSize` | `string` | - | | `[placeholder]` | Placeholder | `string` | - | | `[filterOption]` | Whether filter by input, by default, only apply to `label` and filter through `indexOf` case insensitive. When it's a function, accept `inputValue` and `option` parameters, return `true` when `option` match search criteria, otherwise, return `false` | `boolean or (inputValue: string, option: SFSchemaEnum) => boolean` | `true` | | `[type]` | Mode, automatically complete common email postfix, can set new postfix by setting `enum` | `email` | - | | `[debounceTime]` | debounce time, minimum is `50` by default when it's realtime data source, unit: millisecond | `number` | `0` | | `[defaultActiveFirstOption]` | Whether active the first item by default | `boolean` | `true` | | `[backfill]` | Fill selected value into input when keyboard selection options is used | `boolean` | `false` | | `[nzWidth]` | Customize width, unit is px | `number` | Trigger width of element | | `[change]` | Change callback | `(item: NzAutocompleteOptionComponent, orgData: SFSchemaEnum) => void` | - | | `[overlayClassName]` | Class name of the dropdown root element | `string` | - | | `[overlayStyle]` | Style of the dropdown root element | `object` | - | | `[compareWith]` | Same as [SelectControlValueAccessor](https://angular.io/api/forms/SelectControlValueAccessor#caveat-option-selection) | `(o1: any, o2: any) => boolean` | `(o1: any, o2: any) => o1===o2` | --- --- type: Theme order: 110 title: Blank Layout --- Used for any top and side areas, typically for highly customizable pages such as large screen data. The blank layout all parameters are prefixed with `@alain-blank-`. ## Usage Import in `src/styles.less`: ```less @import '@delon/theme/layout-blank/style/index'; ``` ## Parameters | Name | Default | Description | | --- | --- | --- | | `@prefix` | `.alain-blank` | Style name prefix | | `@bg` | `#f5f7fa` | Background color | | `@content-padding-vertical` | `0` | Vertical padding | | `@content-padding-horizontal` | `16px` | Horizontal padding | --- --- title: boolean subtitle: Switch type: Widgets order: 4 --- Switching Selector. ## API ### schema | Property | Description | Type | Default | | ------------ | ---------------------------- | --------- | ------- | | `[readOnly]` | Whether to disable the state | `boolean` | - | ### ui | Property | Description | Type | Default | | --------------------- | ----------------------------------------------- | --------------- | --------- | | `[size]` | Size of the `nz-switch` | `default,small` | `default` | | `[checkedChildren]` | Content to be shown when the state is checked | `string` | - | | `[unCheckedChildren]` | Content to be shown when the state is unchecked | `string` | - | | `[loading]` | Loading state of switch | `boolean` | `false` | --- --- title: browser subtitle: Cookie, Copy, DOM etc type: Tools --- ## CookieService A set of simple Cookie manipulation classes. - `cookie` Original cookie value - `getAll` Get all cookie key-value pairs - `get` Get the value of given cookie `key` - `put` Sets a value for given cookie key [comment]: ## isEmpty Used to verify `` is empty, useful for custom components. ## updateHostClass Update host component style `class`, for example: ```ts updateHostClass( this.el.nativeElement, this.renderer, { [ 'classname' ]: true, [ 'classname' ]: this.type === '1', [ this.cls ]: true, [ `a-${this.cls}` ]: true } ) ``` ## copy Copy text to clipboard. ## ScrollService Scrollbar control allows scrolling to where the specified element. | Method | Property | Description | |--------|----------|-------------| | `getScrollPosition` | `element?: Element` | Retrieves the current scroll position | | `scrollToPosition` | `element: Element | Window, position: [number, number]` | Sets the scroll position | | `scrollToElement` | `element?: Element, topOffset = 0` | Scroll to element | | `scrollToTop` | `topOffset = 0` | Scroll to top | --- --- title: cascader subtitle: Cascader type: Non-built-in widgets --- Usually, it's used in province/city/district, company hierarchy, category of things, etc. ## How to use Non-built-in modules need to additionally register `withCascaderWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Note - Value of `default` or `formData` should always be an array, for example, city cascade may only save leaf node `value`, but you need to manually provide the whole data chain `value` array ## Data Source **Static** One time fetching data, data source is from `asyncData`, `enum`. **Realtime** Every select triggers a HTTP request, data source is from `asyncData`; includes three parameters `(node: NzCascaderOption, index: number, me: CascaderWidget) => PromiseLike`, `me` indicates instance of the current widget. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Static data source | `SFSchemaEnumType[]` | - | | `[readOnly]` | Read only | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Asynchronous static data source | `(node: NzCascaderOption, index: number, me: CascaderWidget) => PromiseLike` | - | | `[size]` | Size, equals to `nzSize` | `string` | - | | `[placeholder]` | Placeholder | `string` | - | | `[showSearch]` | Whether support search | `bool` | `false` | | `[allowClear]` | Whether show clear button | `bool` | `true` | | `[clearValue]` | Default value when cleared | `any` | `undefined` | | `[clearText]` | Title of clear button | `string` | `清除` | | `[showArrow]` | Whether show arrow | `bool` | `true` | | `[showInput]` | Whether show input | `bool` | `true` | | `[menuClassName]` | Custom floating layer class name | `string` | - | | `[menuStyle]` | Custom floating layer style | `string` | - | | `[columnClassName]` | Custom style of data column in popup menu | `string` | - | | `[notFoundContent]` | Content when dropdown list is empty | `string` | - | | `[data]` | Initial data, is used in first column data, sub column is loaded by `children` option, or loaded by `load` asynchronous event | `Array` | - | | `[enableCache]` | Whether cache asynchronous loaded data, should set it to false if data is changed in every asynchronous load | `bool` | `true` | | `[expandTrigger]` | How is sub menu expanded, options: 'click' or 'hover' | `string` | `click` | | `[changeOnSelect]` | When it is set to true, the value is changed when each level of menu option is selected, see details from above demo | `bool` | `false` | | `[changeOn]` | Custom function to determine if it should have a change when a menu option is selected, will have a change when return value is true | `(option: NzCascaderOption, level: number) => boolean` | - | | `[triggerAction]` | Trigger action to show menu | `('click', 'hover')[]` | `['click']` | | `[valueProperty]` | Property of `value` | `string` | `value` | | `[labelProperty]` | Property of `label` | `string` | `label` | | `[multiple]` | Support multiple | `boolean` | `false` | | `(visibleChange)` | Asynchronous load event | `(value: boolean) => void` | - | | `(change)` | Selected value changed event | `(values: any[]) => void` | - | | `(selectionChange)` | Select option changed event | `(values: NzCascaderOption[]) => void` | - | | `(clear)` | Content clear event | `() => void` | - | --- --- type: CURD title: cell subtitle: Cell Data cols: 1 order: 4 module: import { CellModule } from '@delon/abc/cell'; --- Cell formatting is supported for multiple data types, and supports widget mode. ## API ### cell | Property | Description | Type | Default | |----------|-------------|------|---------| | `[value]` | Value of the cell | `unknown` | - | | `[options]` | Option of the cell | `CellOptions` | - | | `[loading]` | Whether loading | `boolean` | `false` | ### CellOptions | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Render type of the cell | - | - | | `[tooltip]` | Text popup tip | `string` | - | | `[renderType]` | Render type of the cell | `primary,success,danger,warning` | - | | `[size]` | Size of the cell | `large,small` | - | | `[unit]` | Unit, can also be specified by `value: {text: 100, unit: 'RMB'}` | `string` | `-` | | `[default]` | Default text | `string | CellDefaultText` | - | | `[mask]` | Format mask, [Document](https://ng-alain.com/util/format/en#formatMask) | `string, FormatMaskOption` | - | | `[widget]` | Widget config | `{key?: string, data?: string}` | - | | `[date]` | Date config, supports `minutes ago` formatting | `{format?: string}` | - | | `[mega]` | Large number format filter, [Document](https://ng-alain.com/util/format/en#mega) | `CurrencyMegaOptions` | - | | `[currency]` | Currency config | `CurrencyFormatOptions` | - | | `[boolean]` | Boolean config | `YNOptions` | - | | `[img]` | Image config, support large image preview | `{ size?: number; big?: boolean }` | - | | `[link]` | Link config | `{ url?: string; target?: string }` | - | | `[html]` | HTML config | `{ safe?: string }` | - | | `[badge]` | Badge config | `{ data?: CellBadge }` | - | | `[tag]` | Tag config | `{ data?: CellTag }` | - | | `[checkbox]` | Checkbox config | `{ label?: string }` | - | | `[radio]` | Radio config | `{ label?: string }` | - | **Type** - `string` String - `number` Number - `mega` Large number format filter, [Document](https://ng-alain.com/util/format/en#mega) - `currency` Currency - `cny` Converted into RMB notation - `boolean` Boolean - `date` Date - `img` Image, support large image preview - `link` Link - `html` HTML - `badge` Badge - `tag` Tag - `checkbox` Checkbox (Support `disabled`) - `radio` Radio (Support `disabled`) - `enum` Enum - `widget` Custom widget ## Custom widget Just implement the `CellWidgetInstance` interface, for example: ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import type { CellTextResult, CellWidgetInstance } from '@delon/abc/cell'; import { NzMessageService } from 'ng-zorro-antd/message'; import { NzTooltipModule } from 'ng-zorro-antd/tooltip'; @Component({ selector: 'cell-widget-test', template: ` `, host: { '(click)': 'show()' }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ NzTooltipModule ] }) export class CellTestWidget implements CellWidgetInstance { private readonly msg = inject(NzMessageService); static readonly KEY = 'test'; readonly data!: CellTextResult; show(): void { this.msg.info(`click`); } } ``` `data` is a fixed parameter, including `value`, `options` configuration items. Finally, register the widget through `provideCellWidgets` under `app.config.ts`, for example: ```ts export const appConfig: ApplicationConfig = { providers: [ provideCellWidgets( { KEY: CellTestWidget.KEY, type: CellTestWidget } ), ] } ``` --- --- title: checkbox subtitle: Checkbox type: Widgets --- Checkbox. ## API ### schema Property | Description | Type | Default ----------------|---------------------------------------------|----------------------|-------- `[readOnly]` | Whether to disable the state | `boolean` | - `[enum]` | Render checkbox group when the value exists | `SFSchemaEnumType[]` | - `[title]` | Text of the single checkbox | `string` | - `[description]` | Help text of the single checkbox | `string` | - ### ui Property | Description | Type | Default -------- | ----------- | ---- | ------- `[asyncData]` | Async data source, render checkbox group when the value exists | `() => Observable` | - `[span]` | Raster number of cells to occupy | `number` | - `[styleType]` | Style of the `nz-checkbox` | `default, button` | `default` `[checkAll]` | Whether to select all | `boolean` | - `[checkAllText]` | Select all button text | `string` | `全选` `[change]` | Changed event, Parameter: single checkbox is `boolean`, otherwise `SFSchemaEnum[]` | `(res: boolean | SFSchemaEnum[]) => void` | - --- --- title: color subtitle: Color type: Non-built-in widgets --- Used when the user needs to customize the color selection. ## How to use Non-built-in modules need to additionally register `withColorWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## API ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[format]` | Format of color | `rgb`|`hex`|`hsb` | `hex` | | `[defaultValue]` | Default value of color | `string`|`NzColor` | `false` | | `[allowClear]` | Allow clearing color selected | `boolean` | `false` | | `[trigger]` | ColorPicker trigger mode | `hover`|`click` | `click` | | `[showText]` | Show color text | `boolean` | `false` | | `[title]` | Setting the title of the color picker | `TemplateRef`|`string` | - | | `(change)` | Callback when value is changed | `EventEmitter<{ color: NzColor; format: string }>` | - | | `(formatChange)` | Callback when `format` is changed | `EventEmitter<'rgb'|'hex'|'hsb'>` | - | | `[block]` | Color Block | `boolean` | `false` | --- --- title: Conditional expression type: Examples --- `sf` supports two types of conditional expressions: - JSON Schema Standard [if-then-else](https://ajv.js.org/json-schema.html#if-then-else) - More flexible `visibleIf` --- --- type: Basic title: count-down subtitle: Count down cols: 3 module: import { CountDownModule } from '@delon/abc/count-down'; --- The countdown component depends on [ngx-countdown](https://github.com/cipchk/ngx-countdown). ## Dependencies ``` npm i -S ngx-countdown ``` ## API ### count-down | Property | Description | Type | Default | |----------|-------------|------|---------| | `[target]` | Target time, `number` means seconds | `number | Date` | - | | `[config]` | [Config](https://github.com/cipchk/ngx-countdown#countdownconfig) paraments | `CountdownConfig` | - | | `(event)` | Event notification | `EventEmitter` | - | --- --- title: currency subtitle: Currency Pipes type: Pipes module: import { CurrencyPipeModule } from '@delon/util/pipes/currency'; --- > You can override to set the `startingUnit`, `megaUnit`, `precision`, `ingoreZeroPrecision` through [Global Configuration](/docs/global-config). ## price Format a number with commas as thousands separators. [comment]: ## mega Large number format filter. [comment]: ## cny Converted into RMB notation. [comment]: --- --- title: custom subtitle: Custom type: Widgets --- Custom the current dynamic form widget. For more complexity, please refer to [customize](/form/customize) widget. ## Usage Make sure to specify **sf-template** a valid path value, for example: ```html ``` ## API | Name | Description | Type | | ------------- | --------------------------------- | ---------------- | | `[$implicit]` | Current widget component instance | `ControlWidget` | | `[schema]` | Schema data of the widget | `SFSchema` | | `[ui]` | UI data of the widget | `SFUISchemaItem` | The widget component instance include `formProperty` property, whose `value` attribute is the only way to communicate with the widget. The widget component instance contains some shortcut properties and methods, please read for more details [ControlWidget](https://github.com/ng-alain/delon/blob/master/packages/form/src/widget.ts). --- --- title: date subtitle: Date type: Widgets --- To select or input a date. ## Notice - Format is divided into two types: **Data format** means form data, **Display format** means display data ([nzFormat](https://ng.ant.design/components/date-picker/en#api)) - All **Data format** units, reference [date-fns format](https://date-fns.org/v1.29.0/docs/format) (China mirror: [moment format](http://Momentjs.cn/docs/#/displaying/format/)) - Specify `schema.format` must follow [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) time format, otherwise considered as a format error, default rules: - `date-time` default is `yyyy-MM-ddTHH:mm:ssZ` - `date`, `full-date` default is `yyyy-MM-dd` - `time`, `full-time` default is `HH:mm:ss` - *Non-standard:* `week` default is `yyyy-ww` - *Non-standard:* `month` default is `yyyy-MM` - When `schema.format` is not specified, the data formatting (Allows you to reassign default values via [Global Configuration](/docs/global-config)) is determined by the `schema.type` type: - `string` default is `yyyy-MM-dd HH:mm:ss` - `number` default is `T` 13-bit Unix Timestamp ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[readOnly]` | Whether to disable the state | `boolean` | - | | `[format]` | Data format type | `string` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[mode]` | Render mode | `date,week,month,year` | `date` | | `[rangeMode]` | Set picker mode of range picker | `date,week,month,year` | `date` | | `[size]` | Size of the `nz-date-picker` | `default,large,small` | - | | `[placeholder]` | Placeholder of date input | `string, string[]` | - | | `[displayFormat]` | Display format([nzFormat](https://ng.ant.design/components/date-picker/en#api)) | `string` | `yyyy-MM-dd HH:mm:ss` | | `[end]` | End `key` value for the date range | `string` | - | | `[allowClear]` | Whether to show clear button | `boolean` | `true` | | `[className]` | Picker className | `string` | - | | `[disabledDate]` | specify the date that cannot be selected | `(current: Date) => boolean` | - | | `[locale]` | localization configuration | `object` | - | | `[popupStyle]` | to customize the style of the popup calendar | `object` | - | | `[dropdownClassName]` | to customize the className of the popup calendar | `string` | - | | `[onOpenChange]` | a callback emitter, can be executed whether the popup calendar is popped up or closed | `(status: boolean) => void` | - | | `[disabledTime]` | to specify the time that cannot be selected | `(current: Date) => { nzDisabledHours, nzDisabledMinutes, nzDisabledSeconds }` | - | | `[renderExtraFooter]` | render extra footer in panel | `string` | - | | `[showTime]` | to provide an additional time selection, the `object` type is [TimePickerOptions](https://ng.ant.design/components/time-picker/en#api) | `object,boolean` | `true` | | `[showToday]` | whether to show "Today" button | `boolean` | `true` | | `[inputReadOnly]` | Set the readonly attribute of the input tag (avoids virtual keyboard on touch devices) | `boolean` | `false` | | `[inline]` | Inline mode of the date picker | `boolean` | `false` | | `[separator]` | separator | `string, TemplateRef` | `'~'` | | `[showWeekNumber]` | whether to show the week number on each row (Only supported by date picker. Week picker always shows week numbers) | `boolean` | `false` | | `[onOk]` | callback when click ok button | `(data: Date | Date[]) => void` | - | | `[change]` | Date change callback | `(data: Date | Date[]) => void` | - | --- --- title: date-time subtitle: Date Time Conversion type: Tools --- ## toDate Convert to `Date` format, support `Date, number, string` types, If the argument is a number, it is treated as a timestamp. * `formatString` If parsing fails try to parse the date by pressing `formatString` * `defaultValue` If parsing fails returned default value, default: `new Date(NaN)` * `timestampSecond` Whether the incoming value is in seconds ## formatDate Format date, supports `Date, number, string` types, If the argument is a number, it is treated as a timestamp. * Please refer to [date-fnd format](https://date-fns.org/v2.30.0/docs/format) for string format * `dateLocale` Recommended to be consistent with NG-ZORRO by using `inject(NZ_DATE_LOCALE)` ## dateTimePickerUtil 一组针对 [DatePicker](https://ng.ant.design/components/date-picker/en) 的工具类。 - `now` Current local time - `date` Current local date (not including time part) - `removeTime` Remove the time part of the date - `format` Format date-time - `getDiffDays` Calculate the number of days between two dates, `0` means the same day - `disabledBeforeDate` Disabled Before date (Default: today), Generally serves `nzDisabledDate` - `disabledAfterDate` Disabled After date (Default: today), Generally serves `nzDisabledDate` - `disabledBeforeTime` Disabled Before time (Default: now), Generally serves `nzDisabledTime` - `disabledAfterTime` Disabled After time (Default: now), Generally serves `nzDisabledTime` ```ts disabledDate = dateTimePickerUtil.disabledBeforeDate(); disabledDateTime = dateTimePickerUtil.disabledBeforeTime({ offsetSeconds: 60 * 5 }); ``` ## getTimeDistance Get the time range, return `[ Date, Date]` for the start and end dates, for example: Get this week time: ```ts getTimeDistance('week') ``` **Parameters** - `type` Shortcut type, with `-` for last time, if specified `number` type for days - `today`, `-today`, `yesterday` Today or yesterday - `week`, `-week` This week or last week - `month`, `-month` This month or last month - `year`, `-year` This year or last year - `time` Specify start time, default is `now` --- --- title: decorator subtitle: Decorator type: Tools --- ## @ZoneOutside The decoration method runs in `runOutsideAngular`. ```ts class MockClass { constructor(public ngZone: NgZone) {} @ZoneOutside() run(): void {} } ``` ## @ZoneRun The decoration method runs in `run`. ```ts class MockClass { constructor(public ngZone: NgZone) {} @ZoneRun() run(): void {} } ``` --- --- type: Theme order: 100 title: Default Layout --- The default layout all parameters are prefixed with `@alain-default-`. ## Usage ### 1. Style import Import in `src/styles.less`: ```less @import '@delon/theme/layout-default/style/index'; ``` ### 2. Using `layout-default` component Creat a new layout in `src/app/layout/basic/basic.ts`: ```ts import { Component } from '@angular/core'; import { SettingsService, User } from '@delon/theme'; import { LayoutDefaultOptions } from '@delon/theme/layout-default'; import { environment } from '@env/environment'; @Component({ selector: 'layout-basic', template: `
  • {{ 'menu.account.center' | i18n }}
  • {{ 'menu.account.settings' | i18n }}
@if (showSettingDrawer) { } `, }) export class LayoutBasic { readonly user = inject(SettingsService).user; protected options: LayoutDefaultOptions = { logoExpanded: `./assets/logo-full.svg`, logoCollapsed: `./assets/logo.svg` }; protected searchToggleStatus = signal(false); protected showSettingDrawer = !environment.production; } ``` The layout can be dynamically managed at runtime through the `LayoutDefaultService` service. In addition, in layout operations, you can subscribe to layout changes through `SettingsService.notify` (for example: sidebar show and hide, etc.). Note that all layout-related changes will pass through this interface, so you need to do `filter` operation. ## API ### layout-default | Property | Description | Type | Default | |----------|-------------|------|---------| | `[options]` | Options of the layout | `LayoutDefaultOptions` | `-` | | `[asideUser]` | Side user of the layout | `TemplateRef` | `-` | | `[asideBottom]` | Bottom information of the layout | `TemplateRef` | `-` | | `[nav]` | Nav | `TemplateRef` | `-` | | `[content]` | Content | `TemplateRef` | `-` | | `[customError]` | Custom exception routing error message, can't show when is `null` | `string, null` | `Could not load ${evt.url} route` | | `[fetchingStrictly]` | Precise check top loading animation state | `boolean` | `false` | | `[fetching]` | Top loading animation state | `boolean` | `false` | ### LayoutDefaultOptions | Property | Description | Type | Default | |----|----|----|-----| | `[logo]` | Custom Logo Area | `TemplateRef` | - | | `[logoExpanded]` | Logo url of expanded status | `string` | `./assets/logo-full.svg` | | `[logoCollapsed]` | Logo url of collapsed status | `string` | `./assets/logo.svg` | | `[logoFixWidth]` | Specify a fixed logo width | `number` | - | | `[logoLink]` | Specify the logo routing address | `string` | `/` | | `[hideAside]` | Hide the sidebar without showing the collapsed icon button | `boolean` | `false` | | `[hideHeader]` | Hide top bar | `boolean` | `false` | | `[showHeaderCollapse]` | Whether to display the menu collapse button on the top bar | `boolean` | `true` | | `[showSiderCollapse]` | Whether to show the menu collapse button at the bottom of the sidebar | `boolean` | `false` | ### layout-default-nav | Property | Description | Type | Default | |----------|-------------|------|---------| | `[disabledAcl]` | Displayed `disabled` state when `acl` check fails. | `boolean` | `false` | | `[autoCloseUnderPad]` | When the route width is less than the Pad width, the sidebar is automatically closed. | `boolean` | `true` | | `[recursivePath]` | Automatic up recursive lookup, menu data source contains `/ware`, then `/ware/1` is also treated as `/ware` | `boolean` | `true` | | `[hideEmptyChildren]` | When all children are hidden, whether to hide the parent as well | `boolean` | `true` | | `[openStrictly]` | Precise check open status, does not auto closed other open item | `boolean` | `false` | | `[maxLevelIcon]` | Icon displays up to which level | `number` | `3` | | `(select)` | Callback when clicking menu (including `disabled`) | `EventEmitter` | - | > The component data comes from `MenuService` (which is structured as [Menu](/theme/menu#Menu)), and the operation of `MenuService` is auto synchronized to the component. ### layout-default-header-item | Property | Description | Type | Default | |----------|-------------|------|---------| | `[hidden]` | Hidden behavior of the header item | `pc, mobile, none` | `nones` | | `[direction]` | Direction of the header item | `left, middle, right` | `right` | ### layout-default-header-item-trigger The trigger style of the head item. ### layout-default-top-menu-item Header business menu item, please refer to [layout.component.ts](https://github.com/ng-alain/delon/blob/master/src/dev/layout.component.ts#L65-L72)([Preview](https://ng-alain.com/dev/home)). ## Layout description In the upper-left-right layout mode, it is applied to the development of the **business page**. Its specification details: + Top area height `64px`(parameter:`@header-hg`) + Side area width `200px`(parameter:`@aside-wd`) + Hide side navigation when the screen is below `1140px` wide + Turn the side navigation to the `fixed` state when the screen is below `1140px` wide + Mainly includes a [layout-default-nav](/theme/layout-default/en?#layout-default-nav) component > Parameters are adjustable as needed by the `src/styles/theme.less` file. **Top area** location:*src/app/layout/base/header* Scaffolding provides some regular top-level components by default, which are stored in the *components* directory. At the same time `@delon/abc` also provides several top components (eg:[notice-icon](/components/notice-icon) Notification menu component. You can build it yourself or develop it yourself based on the components provided. > Scaffolding supports responsive layout. For the top area, you may need to hide some components under the small screen, so you can add `hidden-xs` to the corresponding DOM node to automatically hide when the screen is smaller than `768px`. **Side area** location:*src/app/layout/default/sidebar* Only one user information and main menu are included. The main menu is a [layout-default-nav](/theme/layout-default/en?#layout-default-nav) component. **Internal area** The content area is the business page area, the specification details: + Content distance page standard, side, right scroll bar, bottom, this margin is based on a standard Dashboard Gutter width `24px`. ## Less Parameters | Name | Default | Description | |------|---------|-------------| | `@alain-default-prefix` | `.alain-default` | Style name prefix | | `@alain-default-ease` | `cubic-bezier(.25, 0, .15, 1)` | Animation filter function | | `@alain-default-header-hg` | `64px` | Height of header | | `@alain-default-header-bg` | `@primary-color` | Background-color of header | | `@alain-default-header-padding` | `@layout-gutter * 2` | Horizontal padding of header | | `@alain-default-header-search-enabled` | `true` | Whether top search | | `@alain-default-header-icon-fs` | `18px` | Font size of icon | | `@alain-default-header-logo-max-height` | `36px` | Max height of logo | | `@alain-default-aside-wd` | `200px` | Width of aside | | `@alain-default-aside-bg` | `#fff` | Background-color of aside | | `@alain-default-aside-scrollbar-width` | `0` | Scrollbar width of aside | | `@alain-default-aside-scrollbar-height` | `0` | Scrollbar height of aside | | `@alain-default-aside-scrollbar-track-color` | `transparent` | Scrollbar track color of aside | | `@alain-default-aside-scrollbar-thumb-color` | `transparent` | Scrollbar thumb color of aside | | `@alain-default-aside-nav-fs` | `14px` | Font size of nav name | | `@alain-default-aside-nav-icon-width` | `14px` | Width of nav icon | | `@alain-default-aside-nav-img-wh` | `14px` | Width & height of nav image | | `@alain-default-aside-nav-padding-top-bottom` | `@layout-gutter` | Vertical padding of nav | | `@alain-default-aside-nav-padding-left-right` | `@layout-gutter * 2` | Horizontal padding of nav | | `@alain-default-aside-nav-text-color` | `rgba(0, 0, 0, 0.65)` | Nav text color | | `@alain-default-aside-nav-text-hover-color` | `#108ee9` | Nav text hover color | | `@alain-default-aside-nav-group-text-color` | `rgba(0, 0, 0, 0.43)` | Group text color | | `@alain-default-aside-nav-selected-text-color` | `#108ee9` | Nav selected text color | | `@alain-default-aside-nav-selected-bg` | `#fcfcfc` | Nav selected background color | | `@alain-default-aside-nav-divider-bg` | `#efe3e5` | Nav divider background color | | `@alain-default-aside-collapsed-wd` | `@layout-gutter * 8` | Width of aside collapsed | | `@alain-default-aside-collapsed-nav-fs` | `24px` | Font size of aside collapsed | | `@alain-default-aside-collapsed-nav-img-wh` | `24px` | Width & height nav image of aside collapsed | | `@alain-default-content-heading-bg` | `#fafbfc` | Heading background color of content area | | `@alain-default-content-heading-border` | `#efe3e5` | Heading bottom border color of content area | | `@alain-default-content-padding` | `@layout-gutter * 3` | Padding of content area | | `@alain-default-content-bg` | `#f5f7fa` | Background color of content area | | `@alain-default-widget-app-icons-enabled` | `true` | Whether the app-icon widget styles | | `@alain-default-aside-user-enabled` | `true` | Whether the user styles of aside | ## FAQ ### Why are there two shortcut menus? The shortcut menu generation rules are uniformly searched under the `0` index,and get in the following order: 1. [Recommended] children have `shortcutRoot: true` which is the highest priority 2. Otherwise, find the link with the word [dashboard], if it exists, create a shortcut entry below the menu. 3. Otherwise placed at the 0 node position Therefore, it's recommended to keep a valid `shortcutRoot: true` data under the `0` index of the menu data. ### FAQ **Hide main menu item** You can set `hide: true` in the menu. **Hide auto-generated navigation hide breadcrumbs** You can set `hideInBreadcrumb: true` in the menu. **About level** Although unlimited levels are supported, please keep no more than four levels (including groups) for user experience. **How to update a menu item** The menu will be re-rendered via calling `MenuService.setItem(key, newValue)`, please refer to the definition of [Menu](/theme/menu#Menu). **How to control menu expand** Use `LayoutDefaultService.toggleCollapsed()` for manual control at runtime. --- --- type: Basic title: down-file subtitle: Download file cols: 1 module: import { DownFileModule } from '@delon/abc/down-file'; --- A file download based on `blob`. ## API ### [down-file] | Property | Description | Type | Default | |----------|-------------|------|---------| | `[http-data]` | Parameter of request | `{}` | - | | `[http-body]` | Body of request | `{}` | - | | `[http-method]` | Method of request | `'POST','GET','HEAD','PUT','PATCH','DELETE'` | `'GET'` | | `[http-url]` | Url of request | `string` | - | | `[file-name]` | Specify a file name | `string, (rep: HttpResponse) => string` | - | | `[pre]` | Callback of download | `(ev: MouseEvent) => Promise` | - | | `(success)` | Success callback | `EventEmitter>` | - | | `(error)` | Error callback | `EventEmitter` | - | ## FAQ ### File name order 1. `file-name` 2. `filename*`, `filename` of `content-disposition` 3. `filename`, `x-filename` of `headers` ### Supported Browsers Use `new Blob()` to verify [Browser Compatible](https://github.com/eligrey/FileSaver.js/#supported-browsers), The `down-file__not-support` style is added to the target element when not compatible. > The default incompatible processing behavior is hidden. You can set the Less variable to `@down-file-not-support-behavior: 'disabled'` to can't be click. --- --- order: 3 title: DrawerHelper subtitle: Drawer Helper type: Service --- Based on the `NzDrawerService` package, it solves some known issues: - More friendly handling callbacks - Support responsive ## create ```ts this.drawerHelper.create('Edit', FormEditComponent, { i }).subscribe(res => this.load()); // Ok callback // 1. considered successful this.NzDrawerRef.close(data); this.NzDrawerRef.close(true); // Close this.NzDrawerRef.close(); this.NzDrawerRef.close(false); // Close all open drawers this.DrawerHelper.closeAll(); ``` There are includes `create` & `static` methods to open the normal & static drawer. **Custom component HTML template** ```html Your body content ``` If you don't bottom toolbar, you need specify `footer: false`. ## API | Property | Description | Type | Default | | --- | --- | --- | --- | | `size` | Specify drawer size, responsive only supports non-numeric values | `sm,md,lg,xl,number` | `md` | | `footer` | Whether toolbar | `boolean` | `true` | | `footerHeight` | Toolbar height | `number` | `55` | | `exact` | Exact match return value, default is `true`, If the return value is not null (`null` or `undefined`) is considered successful, otherwise it is considered error. | `boolean` | `true` | | `drawerOptions` | Drawer raw parameters [NzDrawerOptions](https://ng.ant.design/components/drawer/en#nzdraweroptions) 参数 | `NzDrawerOptions` | - | ### Method - `closeAll` Close all open drawers --- --- type: Layout title: ellipsis subtitle: Ellipsis cols: 1 module: import { EllipsisModule } from '@delon/abc/ellipsis'; --- When the text is too long, the Ellipsis automatically shortens it according to its length or the maximum number of lines. ## API ### ellipsis | Property | Description | Type | Default | |----------|-------------|------|---------| | `[tooltip]` | tooltip for showing the full text content when hovering over | `boolean` | `false` | | `[length]` | maximum number of characters in the text before being truncated | `number` | - | | `[lines]` | maximum number of rows in the text before being truncated | `number` | - | | `[fullWidthRecognition]` | whether consider full-width character length as 2 when calculate string length | `boolean` | `false` | | `[tail]` | specify overflow tail | `string` | `'...'` | --- --- type: Form title: error-collect subtitle: Error Collect cols: 2 module: import { ErrorCollectModule } from '@delon/abc/error-collect'; --- A simple form exception messages collector that jump to element location via click icon; it must be a standard `form`. ## API ### error-collect | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[freq]` | Monitor frequency, unit is milliseconds | `number` | `250` | ✅ | | `[offsetTop]` | Top offset, unit is `px` | `number` | `145` | ✅ | --- --- type: Layout title: exception subtitle: Exception cols: 1 module: import { ExceptionModule } from '@delon/abc/exception'; --- Exceptions page is used to provide feedback on specific abnormal state. Usually, it contains an explanation of the error status, and provides users with suggestions or operations, to prevent users from feeling lost and confused. ## API ### exception | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[type]` | type of exception, the corresponding default `title`, `desc`, `img` will be given if set, which can be overridden by explicit setting of `title`, `desc`, `img` | `'403','404','500'` | - | ✅ | | `[title]` | title | `string` | - | - | | `[desc]` | supplementary description | `string` | - | - | | `[img]` | the url of background image | `string` | - | - | | `[backRouterLink]` | Back of router link | `string, any[]` | `/` | - | | `ng-content` | suggested operations, a default 'Home' link will show if not set | `TemplateRef` | - | - | --- --- title: filter subtitle: Filter type: Pipes module: import { FilterPipeModule } from '@delon/util/pipes/filter'; --- ## filter Filter array. [comment]: --- --- type: Layout title: footer-toolbar subtitle: FooterToolbar cols: 1 module: import { FooterToolbarModule } from '@delon/abc/footer-toolbar'; --- A toolbar fixed at the bottom. ## Usage It is fixed at the bottom of the content area and does not move along with the scroll bar, which is usually used for data collection and submission for long pages. ## API ### footer-toolbar | Property | Description | Type | Default | |----------|-------------|------|---------| | `ng-content` | toolbar content, align to the right | `-` | - | | `[errorCollect]` | Whether `error-collect`, make sure to wrap inside the `
` element | `boolean` | `false` | | `[extra]` | extra information, align to the left | `string,TemplateRef` | - | --- --- title: form subtitle: Reactive Forms type: Tools --- ## MatchControl Match two control values, for examples: ```ts this.form = new FormGroup({ pwd: new FormControl(''), repwd: new FormControl(''), }, { validators: MatchControl('pwd', 'repwd'), }); ``` ## _Validators A set of validators for reactive forms, including: - `_Validators.num` Wheter is number - `_Validators.int` Wheter is integer - `_Validators.decimal` Wheter is decimal - `_Validators.idCard` Wheter is People's Republic of China identity card - `_Validators.mobile` Wheter is mobile (China) - `_Validators.url` Wheter is url address - `_Validators.ip4` Wheter is IP address (Support v4, v6) - `_Validators.color` Wheter is color - `_Validators.chinese` Wheter is chinese Every method has a corresponding form validation: ```ts this.valForm = fb.group({ // is mobile mobile: [null, Validators.compose([Validators.required, _Validators.mobile])] }); ``` --- --- title: format subtitle: String,Check,Currency,Mask type: Tools --- ## CurrencyService > You can override to set the `startingUnit`, `megaUnit`, `precision`, `ingoreZeroPrecision` through [Global Configuration](/docs/global-config). ### format Format a number with commas as thousands separators. ```ts 10000 => `10,000` 10000.567 => `10,000.57` ``` > Use anguar `currency` pipe parse when is set, pls refer to [document](https://angular.io/api/common/CurrencyPipe). ### mega Large number format filter. ```ts 1000 => { value: '1', unit: 'K', unitI18n: '千' } 12456 => { value: '12.46', unit: 'K', unitI18n: '千' } ``` ### cny Converted into RMB notation. ```ts 1 => 壹元整 1.34 => 壹元叁角肆分 ``` ## format String formatting. ```ts format('this is ${name}', { name: 'asdf' }) // output: this is asdf format('this is ${user.name}', { user: { name: 'asdf' } }, true) // output: this is asdf ``` ### formatMask Format mask. | Character | Description | | --- | --- | | `0` | Any numbers, if the character at that position does not match, the default is `0` to fill | | `9` | Any numbers | | `#` | Any letter | | `U` | Convert to uppercase | | `L` | Convert to lowercase | | `*` | Convert to `*` character | ```ts formatMask('123', '(###)') => (123) formatMask('15900000000', '999****9999') => 159****0000 formatMask('aBc', 'UUU') => ABC formatMask('ABc', 'LLL') => abc ``` Or custom Token: ```ts const option: FormatMaskOption = { mask: 'CC999', tokens: { C: { pattern: /.*/, transform: char => (char === '你' ? 'N' : 'H') } } } formatMask('你好123', option) => NH123 ``` ## REGEX A set of common regular expressions. You can also get their regular string format through `REGEX_STR`, and cooperate with `new RegExp` to complete some additional processing. ## num Wheter is number. ## int Wheter is integer. ## decimal Wheter is decimal. ## idCard Wheter is People's Republic of China identity card. ## isMobile Wheter is mobile (China). ## isUrl Wheter is url address. ## isIp Wheter is IP address (Support v4, v6). ## isColor Wheter is color. ## isChinese Wheter is chinese. --- --- title: format subtitle: Mask type: Pipes module: import { FormatPipeModule } from '@delon/util/pipes/format'; --- ## mask Format mask. [comment]: --- --- type: Layout title: full-content subtitle: Full Screen Workspace cols: 1 module: import { FullContentModule } from '@delon/abc/full-content'; --- Often used for tables with scroll bars, a simple [demo](https://ng-alain.surge.sh/#/delon/st)。 ## API ### full-content | Property | Description | Type | Default | |----------|-------------|------|---------| | `[(fullscreen)]` | Whether full screen (not including top, sidebar area) | `boolean` | - | | `[hideTitle]` | Hide title when `fullscreen` is true | `boolean` | `true` | | `[padding]` | Padding of work area | `number` | `24` | ### [full-toggle] Switch whether it is full screen. ## Control There are three ways: - Use `fullscreen` property, it support two-way binding - Use `[full-toggle]` directive - Use `FullContentService.toggle()` service --- --- title: g2-chart subtitle: Custom Chart cols: 1 type: G2 module: import { G2CustomModule } from '@delon/chart/custom'; --- Use `g2-custom` component to package your G2 chart. ## How to use A simple example template: ```ts import { Component, ElementRef } from '@angular/core'; @Component({ selector: 'app-demo', template: ` `, }) export class DemoComponent { render(el: ElementRef) { // Coding G2 code } } ``` ### How to develop G2 If throw error `G2` not found, please refer to [Frequently Asked Questions](/chart/faq). ## Links - [G2 Document](https://www.yuque.com/antv/g2-docs-en) - [G2 Demo](https://antv.alipay.com/zh-cn/g2/3.x/demo/index.html) ## API ### g2-custom | Property | Description | Type | Default | |----------|-------------|------|---------| | `[delay]` | Delayed rendering, unit: ms | `number` | `0` | | `[height]` | Height of chart container | `number` | - | | `[resizeTime]` | Resize event debounce time | `number` | `200` | | `(render)` | Render event | `EventEmitter` | - | | `(resize)` | Resize event | `EventEmitter` | - | | `(destroy)` | Destroy event | `EventEmitter` | - | | `[theme]` | Custom chart theme | `string | LooseObject` | - | --- --- type: Layout title: global-footer subtitle: Global footer cols: 1 module: import { GlobalFooterModule } from '@delon/abc/global-footer'; --- The footer is part of the global navigation as a supplement to the top navigation. ## API ### global-footer | Property | Description | Type | Default | |----|----|----|-----| | `[links]` | Link data | `GlobalFooterLink` | - | | `[copyright]` | Copyright Information | `TemplateRef` | - | ### global-footer-item | Property | Description | Type | Default | |----|----|----|-----| | `ng-content` | Title | `string` | - | | `[href]` | Routing link | `string` | - | | `[blankTarget]` | Whether to open a new window | `boolean` | `false` | ### GlobalFooterLink | Property | Description | Type | Default | |----|----|----|-----| | `[title]` | Title | `string` | - | | `[href]` | Routing link | `string` | - | | `[blankTarget]` | Whether to open a new window | `boolean` | `false` | --- --- type: Basic order: 1 title: hotkey subtitle: Hotkey cols: 2 module: import { HotkeyModule } from '@delon/abc/hotkey'; --- Based on the [@github/hotke](https://github.com/github/hotkey) hotkey library. > If you don't know the hotkey value, you can get it through [Hotkey Code](https://github.github.com/hotkey/hotkey_mapper.html). ## API ### [hotkey] | Property | Description | Type | Default | |----------|-------------|------|---------| | `hotkey` | Specify [hotkey format](https://github.com/github/hotkey#hotkey-string-format) | `string` | - | --- --- title: i18n subtitle: Internationalization type: Examples --- JSON Schema just a JSON object, it's support for internationalization. In addition, `sf` also supports some faster internationalization methods, but the elements it supports are based on the following: `title`, `description`, `optionalHelp`. --- --- order: 1 title: keys subtitle: Keys type: Pipe --- Transforms Object or Map into an array of key value pairs. Data: ```typescript const data = { name: 'cipchk', address: { city: 'shanghai', district: 'changning' } }; ``` Use: ```html @for (item of data | keys; track $index) {
{{item.key}} {{item.value | json}}
} ``` **Dictionary** ```typescript const data = { 1: 'Normal', 2: 'Deleted' }; ``` If you want to keep the key name `number` numeric: ``` @for (item of data | keys: true; track $index) {
{{item.key}} {{item.value | json}}
} ``` > Angular `6.1.0` will support natively [KeyValuePipe](https://angular.io/api/common/KeyValuePipe). --- --- type: Basic order: 2 title: loading subtitle: Loading indicator cols: 2 module: import { LoadingModule } from '@delon/abc/loading'; --- Global load indicator is generally used when an operation needs to interrupt the user operation. ## API ### LoadingService | Name | Description | |------|-------------| | `open(options?: LoadingShowOptions)` | Open a new loading indicator | | `close()` | Turn off a loading indicator | ### LoadingShowOptions | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `type` | Show type of loading | `LoadingType` | `spin` | ✅ | | `text` | Text of loading | `string` | `加载中...` | ✅ | | `icon` | Configuration item of type `icon` | `LoadingIcon` | - | ✅ | | `custom` | Configuration item of type `custom` | `LoadingCustom` | - | ✅ | | `delay` | Specifies a delay in milliseconds for loading state (prevent flush), unit: milliseconds | `number` | - | ✅ | --- --- type: Basic order: 4 title: lodop subtitle: Lodop Print cols: 1 module: import { LodopModule } from '@delon/abc/lodop'; --- The basic implementation of [Lodop](http://c-lodop.com/) printing, commercial needs to buy lodop license. (Also thanks lodop for free ng-alain for genuine KEY) > Prerequisites for running the example must be installed [Lodop](http://c-lodop.com/download.html). ## API ### LodopService | Property | Description | Type | Default | |----------|-------------|------|---------| | `cog` | Get or set config | `AlainLodopConfig` | - | | `events` | process notification | `Observable` | - | | `lodop` | Get the Lodop instance | `Observable` | - | **Auxiliary API after Lodop is successfully loaded** | Property | Description | Type | Default | |----------|-------------|------|---------| | `printer` | Get a list of printers | `string[]` | - | | `attachCode()` | Attach code to the `lodop` object | `void` | - | | `design()` | Run the print design manually after closing the return code | `Promise` | - | | `print()` | Print now, generally used for batch printing | `void` | - | ### LodopPrintResult | Property | Description | Type | Default | |----------|-------------|------|---------| | `ok` | Whether print is successful | `boolean` | - | | `error` | Error message | `string` | - | | `code` | Code of lodop | `string` | - | | `item` | Dynamic parameter context object | `any` | - | | `parser` | Code parsing expression | `RegExp` | - | ### LodopResult | Property | Description | Type | Default | |----------|-------------|------|---------| | `ok` | Whether print is successful | `boolean` | - | | `status` | Status of result | `string` | - | | `lodop` | Lodop instance | `Lodop` | - | | `error` | Error message | `any` | - | ### AlainLodopConfig | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `companyName` | Company name | `string` | - | ✅ | | `license` | License | `string` | - | ✅ | | `licenseA` | LicenseA | `string` | - | ✅ | | `licenseB` | LicenseB | `string` | - | ✅ | | `url` | Lodop remote script URL address | `string` | `//localhost:8443/Lodopfuncs.js` | ✅ | | `name` | Lodop variable name | `string` | `LODOP` | ✅ | | `checkMaxCount` | Exceeded the number of inspections as a failure, Because of Lodop needs to connect to WebSocket | `number` | `100` | ✅ | --- --- title: math subtitle: Math, Rounding type: Tools --- ## inRange Checks if `value` is between `start` and `end` to, but not including `end`. If `end` is not specified, it's set to start with `start` then set to `0`. If `start` is greater than `end` the params are swapped to support negative ranges. ```ts inRange(3, 2, 4); // true inRange(4, 8); // true inRange(4, 2); // false inRange(2, 2); // false inRange(1.2, 2); // true inRange(-3, -2, -6); // true ``` ## ceil Computes `number` rounded up to `precision`. ```ts ceil(4.006); // 5 ceil(6.004, 2); // 6.01 ceil(6040, -2); // 6100 ``` ## floor Computes `number` rounded down to `precision`. ```ts floor(4.006); // 4 floor(0.046, 2); // 0.04 floor(4060, -2); // 4000 ``` ## round Computes `number` rounded to `precision`. ```ts round(4.006); // 4 round(4.006, 2); // 4.01 round(4060, -2); // 4100 ``` --- --- type: Basic order: 1 title: media subtitle: HTML5 Media cols: 2 module: import { MediaModule } from '@delon/abc/media'; --- HTML5 player based on [plyr](https://github.com/sampotts/plyr). ## Dependencies The plyr script file takes the form of lazy loading,you can change the default CDN path via [Global Configuration](/docs/global-config). By default: `https://cdn.jsdelivr.net/npm/plyr/dist/plyr.min.js`, `https://cdn.jsdelivr.net/npm/plyr/dist/plyr.css`. **Use local path** ```json // angular.json { "glob": "**/{plyr.min.js,plyr.css,plyr.svg}", "input": "./node_modules/plyr/dist", "output": "assets/plyr/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { media: { urls: ['assets/plyr/plyr.min.js', 'assets/plyr/plyr.css'], options: { iconUrl: 'assets/plyr/plyr.svg', blankVideo: 'https://cdn.plyr.io/static/blank.mp4' } } }; ``` ## API ### [media] | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[type]` | Media type | `audio, video` | `video` | - | | `[source]` | Source of the media | `string, PlyrMediaSource` | - | - | | `[options]` | Source options of the media, please refer to [plyr options](https://github.com/sampotts/plyr#options) | `any` | - | ✅ | | `[delay]` | Delay init plyr player, unit: ms | `number` | - | - | | `(ready)` | Ready callback | `EventEmitter` | - | - | ## FAQ ### How to use Plyr types more friendly ```ts import type * as Plyr from 'plyr'; ``` --- --- title: mention subtitle: Mention type: Non-built-in widgets --- Mention widget. ## How to use Non-built-in modules need to additionally register `withMentionWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Note - You **must** set `valueWith` parameter if there is no `label` property in data. ## Data Source **Static** One time fetching data, data source is from `asyncData`, `enum`. **Realtime** Every select triggers a HTTP request, data source is from `asyncData`. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Static data source | `SFSchemaEnumType[]` | - | | `[readOnly]` | Read only | `boolean` | - | | `[minimum]` | Minimum time of mention | `number` | - | | `[maximum]` | Maximum time of mention | `number` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Asynchronous static data source | `(input: string) => Observable` | - | | `[size]` | Size, equals to `nzSize` | `string` | - | | `[placeholder]` | Placeholder | `string` | - | | `[loadData]` | Realtime data | `(option: MentionOnSearchTypes) => Observable` | - | | `[notFoundContent]` | Content when nothing found | `string` | `无匹配结果,轻敲空格完成输入` | | `[placement]` | Position of suggestion box | `button,top` | `button` | | `[prefix]` | Character to trigger to popup dropdown list | `'string'` `'string[]'` | `@` | | `[valueWith]` | Function that maps a suggestion value | `(value: any) => string` | - | | `[select]` | Callback when option in dropdown list is selected | `(value: any) => void` | - | | `[inputStyle]` | Input type | `text, textarea` | `text` | | `[autosize]` | Adaptive content height, can be set object:`{ minRows: 2, maxRows: 6 }` | `{ minRows?: number; maxRows?: number }` | `{ minRows: 1, maxRows: 0 }` | --- --- order: 2 title: MenuService subtitle: Menu Service type: Service --- The data format is an array of [Menu](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/menu/interface.ts), where `text` text property muse be required, And it's not affected by external components (such as [sidebar-nav](/components/sidebar-nav)), This is because menus it's essential part of the applications, And it can be used more effectively as a service, such as: dynamically generate navigation, title etc. **Suggest:** Start up Angular ([startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service .ts)) After get menu data from remote, call the `add()` method. ## API ### MenuService | Method | Description | |--------|-----------| | `add` | Setting menu data | | `clear` | Clear menu data | | `resume` | Reset menu, may need call when I18N, user acl changed | | `find` | Find a menu item by `url` or `key` | | `getItem` | Get menu item based on `key` | | `getPathByUrl` | Get menu list based on url | | `setItem` | Set menu item | | `open` | Open of the menu | | `toggleOpen` | Toggle menu open or close | | `openAll` | Toggle all menu open or close | | `getDefaultRedirect` | Returns a default menu link | **recursive** Recursive upward find, for example, the menu data source contains `/ware`, then `/ware/1` is equivalent to `/ware`. ### Menu | Property | Description | Type | Default | |----------|-------------|------|---------| | `render_type` | Rendering type of menu item | `item, divider` | - | | `text` | Text of menu item, can be choose one of `text` or `i18n` (Support HTML) | `string` | - | | `i18n` | I18n key of menu item, can be choose one of `text` or `i18n` (Support HTML) | `string` | - | | `group` | Whether to display the group name | `boolean` | `true` | | `link` | Routing for the menu item, can be choose one of `link` or `externalLink` | `string` | - | | `externalLink` | External link for the menu item, can be choose one of `link` or `externalLink` | `string` | - | | `target` | Specifies `externalLink` where to display the linked URL | `_blank,_self,_parent,_top` | - | | `icon` | Icon for the menu item, only valid for the first level menu | `string | MenuIcon` | - | | `badge` | Badget for the menu item when `group` is `true` | `number` | - | | `badgeDot` | Whether to display a red dot instead of `badge` value | `boolean` | - | | `badgeStatus` | Badge [color](https://ng.ant.design/components/badge/en#nz-badge) | `success,processing,default,error,warning` | `error` | | `badgeOverflowCount` | Maximum count to show in badge, show `${badgeOverflowCount}+` when exceed | `number` | - | | `open` | Whether open for the menu item | `boolean` | `false` | | `disabled` | Whether disable for the menu item | `boolean` | `false` | | `hide` | Whether hidden for the menu item | `boolean` | `false` | | `hideInBreadcrumb` | Whether hide in breadcrumbs, which are valid when the `page-header` component automatically generates breadcrumbs | `boolean` | - | | `acl` | ACL configuration, it's equivalent to `ACLService.can(roleOrAbility: ACLCanType)` parameter value | `any` | - | | `shortcut` | Whether shortcut menu item | `boolean` | - | | `shortcutRoot` | Wheter shortcut menu root node | `boolean` | - | | `reuse` | Whether to allow reuse, need to cooperate with the `reuse-tab` component | `boolean` | - | | `open` | Whether to expand, when `checkStrictly` is valid in `sidebar-nav` component | `boolean` | - | | `key` | Unique identifier of the menu item, can be used in `getItem`, `setItem` to update a menu | `string` | - | | `children` | Children for the menu item | `Menu[]` | - | ### MenuIcon | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Type for icon; `img`, `svg` Size uses `14px` width and height | `class,icon,iconfont,img,svg` | `icon` | | `[value]` | Value for the icon, can be set Class Name, nz-icon of `nzType`, image | `string` | - | | `[theme]` | Type of the ant design icon | `outline,twotone,fill` | `outline` | | `[spin]` | Rotate icon with animation | `boolean` | `false` | | `[twoToneColor]` | Only support the two-tone icon. Specific the primary color | `string` | - | | `[iconfont]` | Type of the icon from iconfont | `string` | - | | `[rotate]` | Rotate degrees | `number` | - | --- --- title: method subtitle: Built-in methods type: Examples --- `SFComponent` provides some shortcut methods, such as: `setValue`, `setDisabled`, `setRequired`, etc. --- --- title: modal subtitle: Modal type: Examples --- Using a form in a modal box is a very common scenario. In fact, when you run `ng g ng-alain:edit edit`, you will get a complete example; you will get an HTML template like this: ```html ``` `.modal-footer` has been very friendly to integrate custom dynamic boxes. --- --- order: 3 title: ModalHelper subtitle: Modal Helper type: Service --- Based on the `NzModalService` package, it solves some known issues: - More friendly handling callbacks - Default Button Focus - Responsive Width ## Usage ```ts this.modalHelper.create(FormEditComponent, { i }).subscribe(res => this.load()); // Ok callback, Where `nzModalRef` refers to the variable name of the target component in the constructor `NzModalRef` // 1. considered successful this.nzModalRef.close(true); this.nzModalRef.close({ other: 1 }); // 2. considered error this.nzModalRef.close(); // Close this.nzModalRef.destroy(); ``` There are includes `create` & `createStatic` methods to open the normal & static modal. Add a few parameters based on `NzModalService`. **Custom component HTML template** ```html Your body content ``` ### API | Property | Description | Type | Default | | --- | --- | --- | --- | | `size` | Specify modal size | `sm,md,lg,xl,number,string` | `lg` | | `exact` | Exact match return value, default is `true`, If the return value is not null (`null` or `undefined`) is considered successful, otherwise it is considered error. | `boolean` | `true` | | `includeTabs` | Whether to wrap the tab page | `boolean` | `false` | | `drag` | Drag | `boolean, ModalHelperDragOptions` | - | | `useNzData` | Whether it is mandatory to use `nzData` to pass parameters. If it is `false`, it means that the parameters will be directly mapped to the component instance, and other values ​​can only be obtained through `NZ_MODAL_DATA`. | `boolean` | `false` | | `modalOptions` | nz-modal raw parameters [ModalOptions](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/modal/modal-types.ts) | `ModalOptions` | - | --- --- title: monaco-editor subtitle: Monaco Editor type: Third Widgets --- Markdown Editor ## How to Use **Installation dependencies** `yarn add @ng-util/monaco-editor` - 1. Register `provideNuMonacoEditorConfig()` in `app.config.ts` - 2. Register `withMonacoEditorWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). > For more Monaco Editor configuration, please refer to [@ng-util/monaco-editor](https://github.com/ng-util/ng-util/blob/master/packages/monaco-editor/README.md#usage). ## API ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[options]` | Configuration options, [official website](https://microsoft.github.io/monaco-editor/docs.html) | `monaco.editor.IStandaloneEditorConstructionOptions` | - | | `[delay]` | Time of lazy loading | `number` | - | | `[change]` | Callback function when content in editor is changed | `(value: string) => void` | - | | `[height]` | Height of monaco editor | `string` | `200px` | | `[model]` | Model of monaco editor | `NuMonacoEditorModel` | - | | `(event)` | Event callback | `EventEmitter` | - | --- --- type: Business title: notice-icon subtitle: Notification menu cols: 1 module: import { NoticeIconModule } from '@delon/abc/notice-icon'; --- Used on the navigation toolbar as a unified notification center for the entire product. ## API ### notice-icon | Property | Description | Type | Default | |----------|-------------|------|---------| | `[data]` | Data | `NoticeItem[]` | - | | `[count]` | Total number of messages on the icon | `number` | - | | `[loading]` | Pop up card loading status | `boolean` | `false` | | `[centered]` | Whether the tab is centered | `boolean` | `false` | | `(select)` | Click the callback of the list item | `EventEmitter` | - | | `(clear)` | Callback for clicking the clear button | `EventEmitter` | - | | `[popoverVisible]` | Manual control of Popover display | `boolean` | `false` | | `(popoverVisibleChange)` | Popover callback | `EventEmitter` | - | | `[btnClass]` | Class of the button | `ngClass` | - | | `[btnIconClass]` | Class of the icon in button | `ngClass` | - | ### NoticeItem | Property | Description | Type | Default | |----------|-------------|------|---------| | `[title]` | Title of the item | `string` | - | | `[list]` | List data of the item | `NoticeIconList[]` | - | | `[emptyText]` | Customize empty data copy for each tab | `string | TemplateRef` | `无通知` | | `[emptyImage]` | Customize the empty data image for each Tab | `string` | - | | `[clearText]` | Clear button text for each Tab | `string` | `清空` | ### NoticeIconList | Property | Description | Type | Default | |----------|-------------|------|---------| | `[avatar]` | Avatar image link of the icon | `string` | - | | `[title]` | Title of the icon | `string | TemplateRef<{ $implicit: NoticeIconList }>` | - | | `[description]` | Description of the icon | `string | TemplateRef<{ $implicit: NoticeIconList }>` | - | | `[datetime]` | Timestamp of the icon | `string` | - | | `[extra]` | Additional information, in the upper right corner of the icon | `string` | - | | `[read]` | Whether readed status of the item | `boolean` | - | ### NoticeIconSelect | Property | Description | Type | Default | |----------|-------------|------|---------| | `[title]` | Title of the select item | `string` | - | | `[item]` | Data of the select item | `NoticeItem` | - | | `[event]` | Click Event | `Event` | - | --- --- title: number subtitle: Input Number type: Widgets order: 5 --- Enter a number within certain range with the mouse or keyboard. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[minimum]` | min value | `number` | - | | `[exclusiveMinimum]` | Indicate whether minimum are exclusive of the value | `boolean` | - | | `[maximum]` | max value | `number` | - | | `[exclusiveMaximum]` | Indicate whether maximum are exclusive of the value | `boolean` | - | | `[multipleOf]` | Restricted to a multiple of a given number, | `number` | `1` | | `[change]` | Change event for the number | `(val?: number) => void` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[prefix]` | Prefix, simplifying the use of `nzFormatter`, `nzParser` | - | - | | `[unit]` | Unit, simplifying the use of `nzFormatter`, `nzParser` | - | - | | `[formatter]` | Specifies the format of the value presented | - | - | | `[parser]` | Specifies the value extracted from nzFormatter | - | - | | `[precision]` | precision of input value | - | - | | `[widgetWidth]` | Specify `nz-number` width | `number, string` | `90` | | `[hideStep]` | Hide step icon | `boolean` | `false` | ## QA ### Why can't modify `unit` All components of NG-ZORRO are in OnPush mode. A special case is that when the `unit` needs to be dynamically modified, it will only take effect when the `ngModel` change needs to be triggered once, so it's value needs to be changed, for example: ```ts const ageProperty = this.sf.getProperty('/age')!; ageProperty.widget.ui.unit ='c'; ageProperty.widget.setValue(null); ageProperty.widget.setValue(statusProperty.value); ``` --- --- type: G2 title: number-info subtitle: NumberInfo cols: 2 module: import { NumberInfoModule } from '@delon/chart/number-info'; --- Often used in data cards for highlighting the business data. ## API ### number-info | Property | Description | Type | Default | |----------|-------------|------|---------| | `[title]` | title | `TemplateRef` | - | | `[subTitle]` | subtitle | `TemplateRef` | - | | `[total]` | total amount | `string, number` | - | | `[suffix]` | total amount suffix | `string` | - | | `[subTotal]` | total amount of additional information | `string, number` | - | | `[status]` | increase state | `'up','down'` | - | | `[theme]` | state style | `'light','default'` | `'light'` | | `[gap]` | set the spacing (pixels) between numbers and descriptions | `number` | 8 | --- --- title: object subtitle: Object type: Widgets order: 1 --- Create an object widget, valid only for `schema.type="object"`. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[properties]` | Defining the properties of an object schema | `{ [key: string]: SFSchema }` | - | | `[required]` | The data object to be valid should contain all properties with names equal to the elements in the keyword value | `string[]` | - | | `[maxProperties]` | The data object to be valid should have not more (less) properties than the keyword value | `number` | - | | `[minProperties]` | The data object to be valid should have not more (less) properties than the keyword value | `number` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[showExpand]` | Whether to show the card body, click to hide the content, only `type ==='card'` | `boolean` | `true` | | `[expand]` | Expanded state, only `type ==='card'` | `boolean` | `true` | | `[showTitle]` | Whether to display the title | `boolean` | `false` | | `[type]` | Render type of object | `card, default` | `default` | | `[cardSize]` | The same as `nzSize` | `small, default` | `small` | | `[cardBodyStyle]` | The same as `nzBodyStyle` | `{ [key: string]: string }` | - | | `[cardBordered]` | The same as `nzBordered` | `boolean` | `false` | | `[cardExtra]` | The same as `nzExtra` | `string, TemplateRef` | - | | `[cardActions]` | The same as `nzActions` | `Array>` | - | --- --- type: Basic title: Observers order: 7 module: import { ObserversModule } from '@delon/abc/observers'; --- The `ObserversModule` package provides convenience directives built on top of native web platform observers, such as `MutationObserver`. ## API ### [observeSize] Watch the DOM size change. | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `(observeSize)` | Event | `MutationRecord[]` | - | | --- --- type: Basic title: onboarding subtitle: Onboarding order: 7 module: import { OnboardingModule } from '@delon/abc/onboarding'; --- The user guidance is to help users better understand and use the product. ## API ### OnboardingService The components only support the use of `OnboardingService` service to build. | Name | Description | |------|-------------| | `start(data: OnboardingData)` | Start a new user guidance | | `next()` | Next | | `prev()` | Prev | | `done()` | Done | ### OnboardingData | Property | Description | Type | Default | |----------|-------------|------|---------| | `[key]` | Storage identification Key, The default is `localStorage` local storage, allowing the use of `ONBOARDING_STORE_TOKEN` to change the storage method | `string` | - | | `[keyVersion]` | Current version | `unknown` | - | | `[items]` | Onboarding items | `OnboardingItem[]` | `[]` | | `[mask]` | Whether to show mask or not | `boolean` | `true` | | `[maskClosable]` | Clicking on the mask (area outside the onboarding) to close the onboarding or not | `boolean` | `true` | | `[showTotal]` | Whether to show total | `boolean` | `false` | ### OnboardingItem | Property | Description | Type | Default | |----------|-------------|------|---------| | `[selectors]` | The CSS selector, which identifies the html element you want to describe | `string` | - | | `[position]` | Positioning of the selector element, relative to the contents of the children | `top, left, right, bottom, topLeft, topRight, bottomLeft, bottomRight, leftTop, leftBottom, rightTop, rightBottom` | `bottomLeft` | | `[className]` | Class name of the panel | `string` | - | | `[width]` | Width of the panel | `number` | - | | `[title]` | Headline text of the panel | `string, TemplateRef` | - | | `[content]` | Detail text of the panel | `string, SafeHtml, TemplateRef` | - | | `[skip]` | Skip button of the panel, `null` Don't show | `string, TemplateRef, null` | `Skip` | | `[prev]` | Prev button of the panel, `null` Don't show | `string, TemplateRef, null` | `Prev` | | `[next]` | Next button of the panel, `null` Don't show | `string, TemplateRef, null` | `Next` | | `[done]` | Done button of the panel, `null` Don't show | `string, TemplateRef, null` | `Done` | | `[url]` | Target router url | `string` | - | | `[before]` | Callback before entering, triggered when call `next` operates, `number` indicate delay | `Observable, number` | - | | `[after]` | Callback after entering, triggered when call `prev` operates, `number` indicate delay | `Observable, number` | - | --- --- title: other subtitle: Deep get,copy,merge,lazy,assert type: Tools --- ## omit Omit specified keys from an object. ```ts omit({ a: 1, b: 2, c: 3 }, 'a') // { b: 2, c: 3 } omit({ a: 1, b: 2, c: 3 }, ['a', 'c']) // { b: 2 } omit({ a: 1, b: 2 }, key => key === 'a') // { a: 1 } ``` ## deepGet Gets the value at `path` of `object`, like `_.get` in lodash. ```ts const obj = { id: 1, user: { name: 'cipchk', age: 18 } }; deepGet(obj, 'id'); // 1 deepGet(obj, 'user.age'); // 18 ``` ## deepCopy Base on [extend](https://github.com/justmoon/node-extend) deep copy. ```ts const source = { a: 1, user: { name: 'cipchk' } }; const obj = deepCopy(source); ``` ## deepMerge Deep merge object. ```ts let original = { a: 1, b: { c: 'c' } }; deepMerge(original, { b: { d: 'd' }, arr: [ 1 ] }); // output: { a: 1, b: { c: 'c', d: 'd' }, arr: [ 1 ] } ``` ## LazyService `LazyService` delay loading JS or CSS files. ```ts import { LazyService } from '@delon/util/other'; export class AppComponent { constructor(private lazy: LazyService) {} ngOnInit() { this.lazy.load([ `https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js` ]).then(() => { // do something }); } } ``` ## assert Assert whether the expression and throw an error into console in dev mode. - `assert` Assert whether the expression - `assertEmpty` Assert whether empty (`null` or `undefined`) - `assertNumber` Assert whether `number` type - `assertString` Assert whether `string` type - `assertArray` Assert whether `array` type - `assertObservable` Assert whether `Observable` type --- --- type: Layout title: page-header subtitle: Page Header cols: 1 module: import { PageHeaderModule } from '@delon/abc/page-header'; --- The page header is used to declare the subject of the page and contains the most important information that the user is concerned about, so that the user can quickly understand what the current page and functions. ## API ### page-header | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[title]` | Title of the page | `string,TemplateRef` | - | ✅ | | `[titleSub]` | Sub title of the page | `string,TemplateRef` | - | ✅ | | `[autoTitle]` | Whether to automatically generate the title and locate it from the main menu with the current route | `boolean` | `true` | ✅ | | `[syncTitle]` | Whether to automatically synchronize the title to `TitleService`, `ReuseService`, only valid when `title` is of type `string` | `boolean` | `true` | ✅ | | `[home]` | Home page text of the breadcrumb, if empty is specified, it will not be displayed | `string` | `首页` | ✅ | | `[homeLink]` | Home page link of the breadcrumb | `string` | `/` | ✅ | | `[homeI18n]` | Home page i18n of the breadcrumb | `string` | - | ✅ | | `[autoBreadcrumb]` | Whether to automatically generate navigation, use the current route to locate from the main menu | `boolean` | `true` | ✅ | | `[recursiveBreadcrumb]` | Search automatically upward recursively, if the menu data source contains `/ware`, then `/ware/1` is also regarded as `/ware` item | `boolean` | `false` | ✅ | | `[loading]` | Whether loading | `boolean` | `false` | - | | `[wide]` | Whether wide | `boolean` | `false` | - | | `[fixed]` | Whether fixed mode | `boolean` | `false` | ✅ | | `[fixedOffsetTop]` | Fixed offset of the fixed mode | `number` | `64` | ✅ | | `[breadcrumb]` | Custom navigation area | `TemplateRef` | - | - | | `[logo]` | Custom LOGO area | `TemplateRef` | - | - | | `[action]` | Custom action area | `TemplateRef` | - | - | | `[content]` | Custom content area | `TemplateRef` | - | - | | `[extra]` | Customize extra information area | `TemplateRef` | - | - | | `[tab]` | Custom tab area | `TemplateRef` | - | - | ## FAQ ### Automatically generated navigation By default, the navigation is automatically generated based on the menu data. Sometimes you may want to hide the menu data of a certain node, you can specify the menu `hideInBreadcrumb: true`. ### Fixed mode The fixed mode will override the reuse-tab component during scrolling. --- --- type: Basic order: 3 title: pdf subtitle: Pdf cols: 1 module: import { PdfModule } from '@delon/abc/pdf'; --- Pdf view based on [pdf.js](https://mozilla.github.io/pdf.js/). pdf.js libary is lazy loading by default,you can change the default root CDN path via [Global Configuration](/docs/global-config). > The component inspired by [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer). **Use local path** ```bash npm i -S pdfjs-dist ``` ```json // angular.json { "glob": "{build,web}/**", "input": "./node_modules/pdfjs-dist/", "ignore": ["*.js.map", "*.d.ts"], "output": "assets/pdfjs/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { pdf: { lib: '/assets/pdfjs/' } }; ``` ## API ### pdf | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[src]` | Specify path of the pdf | `string, object, UInt8Array` | - | - | | `[pi]` | Current page | `number` | `1` | - | | `[showAll]` | Whether to show all pages | `boolean` | `true` | ✅ | | `[renderText]` | Enable text rendering, allows to select text | `boolean` | `true` | ✅ | | `[textLayerMode]` | Text rendering mode | `PdfTextLayerMode` | `ENABLE` | - | | `[showBorders]` | Show page borders | `boolean` | `false` | ✅ | | `[stickToPage]` | Sticks view to the page | `boolean` | `false` | - | | `[originalSize]` | Control document display size, `true` size will be as same as original document, `false` size will be as same as container block | `boolean` | `true` | ✅ | | `[fitToPage]` | Works in combination with `originalSize`. You can show your document in original size, and make sure that it's not bigger then container block. | `boolean` | `false` | ✅ | | `[zoom]` | Zoom pdf | `number` | `1` | - | | `[zoomScale]` | Defines how the Zoom scale is computed | `PdfZoomScale` | `page-width` | - | | `[rotation]` | Rotate PDF, Allowed step is 90 degree, ex. 0, 90, 180 | `number` | `0` | - | | `[autoReSize]` | Turn on or off auto resize | `boolean` | `true` | ✅ | | `[externalLinkTarget]` | Link target of the external | `PdfExternalLinkTarget` | `BLANK` | - | | `[delay]` | Delayed rendering, unit: ms | `number` | `0` | - | | `(change)` | change event | `EventEmitter` | - | - | ### Component properties Used API interfaces. | Name | Description | | --- | ---- | | `pdf` | Current PDF instance | | `eventBus` | Event bus for PDF files, eg: find document, etc. | | `findController` | Find controller, now instead by `eventBus` | | `pageViewer` | View Controls | | `linkService` | Navigation Service | ## FAQ ### Why need to specify the height of the pdf component When `showAll` is enabled, if you want to make the page number control effective, you need to ensure that the height of the component is a valid value. --- --- order: 1 title: PreloadOptionalModules type: Router --- Optional pre-loading module loading, when it's necessary to load the resource at the first page load for some lazy routes. For example, by default, the `order` module must first access the `/order` route before it will actually start download resource files. When using `PreloadOptionalModules` and specifying `preload: true`, it will automatically download resource files after the Angular project is started. ```ts @NgModule({ providers: [PreloadOptionalModules], imports: [ RouterModule.forRoot([ { path: 'order', loadChildren: () => import('./order/order.module').then(m => m.OrderModule), data: { preload: true } }, ], { preloadingStrategy: PreloadOptionalModules })] }) ``` --- --- title: qr-code subtitle: QRCode type: Non-built-in widgets --- Used when the link needs to be converted into a QR Code. ## How to use Non-built-in modules need to additionally register `withQrCodeWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## API ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[color]` | QR code Color | `string` | `#000` | | `[bgColor]` | QR code background color | `string` | `#FFFFFF` | | `[qrSize]` | QR code Size | `number` | `160` | | `[padding]` | QR code Padding | `number \| number[]` | `0` | | `[icon]` | Icon address in QR code | `string` | - | | `[iconSize]` | The size of the icon in the QR code | `number` | `40` | | `[bordered]` | Whether has border style | `boolean` | `true` | | `[status]` | QR code status | `'active'|'expired' |'loading'` | `active` | | `[level]` | Error Code Level | `'L'|'M'|'Q'|'H'` | `M` | | `(refresh)` | callback | `EventEmitter` | - | --- --- type: Business title: quick-menu subtitle: Quick Menu cols: 1 module: import { QuickMenuModule } from '@delon/abc/quick-menu'; --- Quick menu for the implicit auxiliary list on the right. ## API ### quick-menu | Property | Description | Type | Default | |----------|-------------|------|---------| | `[icon]` | Icon for the quick menu | `string,TemplateRef` | `question-circle` | | `[top]` | Icon from top | `number` | `120` | | `[width]` | Width after opening | `number` | `200` | | `[bgColor]` | Background for the quick menu | `string` | - | | `[borderColor]` | Border for the quick menu | `string` | - | | `[expand]` | Current expand status, double binding | `boolean` | - | | `(expandChange)` | Expand status change callback | `EventEmitter` | - | --- --- title: radio subtitle: Radio type: Widgets --- Radio. ## API ### schema | Property | Description | Type | Default | |--------------|------------------------------|----------------------|---------| | `[readOnly]` | Whether to disable the state | `boolean` | - | | `[enum]` | Render radio group | `SFSchemaEnumType[]` | - | ### ui | Property | Description | Type | Default | |---------------|-------------------------|----------------------------------------|-----------| | `[asyncData]` | Async data source | `() => Observable` | - | | `[size]` | Size of the `nz-radio` | `string` | - | | `[styleType]` | Style of the `nz-radio` | `default, button` | `default` | | `[change]` | Changed event | `(res: SFValue) => void` | - | | `[buttonStyle]` | style type of radio button | `'outline'|'solid'` | `'outline'` | --- --- type: Form title: range-picker subtitle: Data Range cols: 1 module: import { DatePickerModule } from '@delon/abc/date-picker'; --- Based on the `nz-range-picker` further optimization, better service to start and end dates. ## API ### [extend] It needs to be used with [nz-range-picker](https://ng.ant.design/components/date-picker/zh#nz-range-picker), for example: ```html ``` | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[(ngModelEnd)]` | End date, start and end values at the same time | `Date` | - | | | `[shortcut]` | Set shortcuts | `boolean | DateRangePickerShortcut` | `false` | ✅ | #### DateRangePickerShortcut | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enabled]` | Whether to enable | `boolean` | `false` | | `[closed]` | Whether to close the panel after clicking | `boolean` | `true` | | `[list]` | Shortcut list, supports using `['today', 'yesterday', '-3', '-7', 'week', 'lastWeek', 'month', 'lastMonth', 'year']` to represent... | `DateRangePickerShortcutItem[]` | `今天,昨天,近3天,近7天,本周,本月,全年` | --- --- title: rate subtitle: Rate type: Non-built-in widgets --- A quick rating operation on something. ## How to use Non-built-in modules need to additionally register `withRateWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[maximum]` | star count | `number` | `5` | | `[multipleOf]` | `0.5` indicates allow semi selection | `number` | `0.5` | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[allowClear]` | whether to allow clear when click again | `boolean` | `true` | | `[autoFocus]` | get focus when component mounted | `boolean` | `false` | | `[text]` | Reminder text template, `{{value}}` indicates the current value | `string` | - | | `[tooltips]` | Customize tooltip by each character | `string[]` | `[]` | --- --- type: Service order: 5 title: ResponsiveService subtitle: Responsive Service --- More friendly use responsive rules, the original `xs`, `sm` etc. reduced to `col` property, the default response rule: | `col` | `<576px` | `≥576px` | `≥768px` | `≥992px` | `≥1200px` | `≥1600px` | | ----- | -------- | -------- | -------- | -------- | --------- | --------- | | `1` | 1 | 1 | 1 | 1 | 1 | 1 | | `2` | 1 | 2 | 2 | 2 | 2 | 2 | | `3` | 1 | 2 | 3 | 3 | 3 | 3 | | `4` | 1 | 2 | 3 | 4 | 4 | 4 | | `5` | 1 | 2 | 3 | 4 | 6 | 6 | | `6` | 1 | 2 | 3 | 4 | 6 | 12 | In order to better develop responsives this view or edit pages, the rule will be used by default in `sg`, `sv`, `se` components. You can via `ResponsiveConfig` to override the default rules. ## ResponsiveConfig ```ts import { AlainThemeConfig } from '@delon/theme'; export function fnAlainThemeConfig(): AlainThemeConfig { return Object.assign(new AlainThemeConfig(), { responsive: { rules: { 1: { xs: 24 }, 2: { xs: 24, sm: 12 }, 3: { xs: 24, sm: 12, md: 8 }, 4: { xs: 24, sm: 12, md: 8, lg: 6 }, 5: { xs: 24, sm: 12, md: 8, lg: 6, xl: 4 }, 6: { xs: 24, sm: 12, md: 8, lg: 6, xl: 4, xxl: 2 }, } }, }); } @NgModule({}) export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ { provide: AlainThemeConfig, useFactory: fnAlainThemeConfig }, ], }; } } ``` --- --- type: Basic order: 1 title: reuse-tab subtitle: Reuse Route Tab cols: 1 module: import { ReuseTabModule } from '@delon/abc/reuse-tab'; --- Reuse route tab are extremely common for admin interfaces, and the problem of component data is not lost when switching routes. The newly opened is always the current page, and the route reuse means that when we leave the current page, if the reuse condition is met (ie: matching mode), all component states (including subcomponents) of the current route are saved, wait for the next time you enter the route to restore the component data based on the cache. ## Usage 1. Provide `provideReuseTabConfig()` configuration in `app.config.ts` file 2. Add `` in the `src/app/layout/basic/basic.ts` file, like this: ```html - + + ``` > **Note: If you do not specify the `(activate)` event, you cannot refresh current tab when uncached.** ## Matching Mode Inject the `ReuseTabService` class anywhere in the project and set the `mode` property, or pass `` Reset values. **0. Menu (Default)** Press the ([Menu](/theme/menu#Menu)) to configure. Reusable: ``` { text:'Dashboard' } { text:'Dashboard', reuse: true } ``` Not reusable: ``` { text:'Dashboard', reuse: false } ``` **1. MenuForce** Press the ([Menu](/theme/menu#Menu)) to force the configure. Reusable: ``` { text:'Dashboard', reuse: true } ``` Not reusable: ``` { text:'Dashboard' } { text:'Dashboard', reuse: false } ``` **2. URL** Valid for all routes, can be combined with `excludes` filtering without reusing. ## Tab Text Get the tab text in the following order: 1. Overwrite text within the component with `ReuseTabService.title = 'new title' 2. The `data` property in the routing configuration 3. `text` property in menu data Use `ReuseTabService` example: ```ts export class DemoReuseTabEditComponent implements OnInit { id: any; constructor(private route: ActivatedRoute, private reuseTabService: ReuseTabService) {} ngOnInit(): void { this.route.params.subscribe(params => { this.id = params.id; this.reuseTabService.title = `Edit ${this.id}`; }); } } ``` ## Reuse Events Route reusing does not touch the Angular component lifecycle hooks (eg: `ngOnInit`, etc.), but often requires data to be refreshed during the reuse process, so two new lifecycle hooks are provided to temporarily resolve such problems. **OnReuseInit** Interface - `_onReuseInit(type?: ReuseHookOnReuseInitType): void;` Triggered when the current route is in the reusing process, The values of `type` are: -`init` when routing process -`refresh` when refresh action via tab **OnReuseDestroy** Interface - `_onReuseDestroy(): void;` Triggered when the current route allows reusing and leave route. A simple example: ```ts import { OnReuseDestroy, OnReuseInit, ReuseHookOnReuseInitType } from '@delon/abc/reuse-tab'; @Component() export class DemoComponent implements OnReuseInit, OnReuseDestroy { _onReuseInit(type: ReuseHookOnReuseInitType) { console.log('_onReuseInit', type); } _onReuseDestroy() { console.log('_onReuseDestroy'); } } ``` ## Scroll Position Turning on `keepingScroll` will restore the previous scrollbar position after reused, pls attention to detail: > **Make sure** to use the routing option [scrollPositionRestoration](https://angular.io/api/router/ExtraOptions#scrollPositionRestoration) to manage the scrollbar position. - `true`: Keep the previous scroll bar position - `false`: Do't operate on the scroll bar - If all page used route reuse, you can set `scrollPositionRestoration: 'disabled'` to avoid delayed scrolling. - If part of the page used route reuse, it's limited by `scrollPositionRestoration` **priority value**, there will be a `1ms` delay to restore the scrollbar position. ## API ### ReuseTabService **Property** | Property | Description | Type | Default | |----------|-------------|------|---------| | `[max]` | Maximum of reuse | `number` | `10` | | `[mode]` | Matching Mode | `ReuseTabMatchMode` | `0` | | `[debug]` | Whether Debug mode | `boolean` | `false` | | `[keepingScroll]` | Keep the scrollbar position | `boolean` | `false` | | `[keepingScrollContainer]` | Keep the scroll bar container | `Element` | `window` | | `[excludes]` | Exclusion rules, limited by `mode=URL` | `RegExp[]` | - | | `[items]` | Get cached routes | `ReuseTabCached[]` | - | | `[count]` | Get the number of cached routes | `number` | - | | `[change]` | Cache change callback | `Observable` | - | | `[title]` | Customize the current title | `string` | - | | `[closable]` | Customize the current `closable` state | `boolean` | - | **Method** | Name | Description | Type | |------|-------------|------| | `index(url)` | Returns the index of the first element in the caches, and -1 otherwise | `number` | | `exists(url)` | Exists cache by URL | `boolean` | | `get(url)` | Get cache data by URL | `ReuseTabCached` | | `getTitle(url, route?: ActivatedRouteSnapshot)` | Get title | `string` | | `clearTitleCached()` | Clear all title caches | `void` | | `getClosable(url, route?: ActivatedRouteSnapshot)` | Get `closable` state | `string` | | `clearClosableCached()` | Clear cached of closable | `void` | | `remove(url)` | Remove the tag, touch change remove event | `void` | | `move(url, position)` | Move of caches, touch change move event | `void` | | `clear()` | Clear caches, touch change clear event | `void` | | `refresh()` | Just touch change refresh event | `void` | | `replace(url)` | Force closed current route (including the non-closable) and navigate back to the `newUrl` route | `void` | ### reuse-tab | Property | Description | Type | Default | |----------|-------------|------|---------| | `[i18n]` | Context Menu internationalization, support HTML | `ReuseContextI18n` | - | | `[mode]` | Matching Mode | `ReuseTabMatchMode` | `0` | | `[debug]` | Whether Debug mode | `boolean` | `false` | | `[max]` | Maximum of reuse | `number` | `10` | | `[keepingScroll]` | Keep the scrollbar position | `boolean` | `false` | | `[keepingScrollContainer]` | Keep the scroll bar container | `string | Element` | `window` | | `[excludes]` | Exclusion rules, limited by `mode=URL` | `RegExp[]` | - | | `[allowClose]` | Whether to allow close tab | `boolean` | `true` | | `[customContextMenu]` | Custom context click menu | `ReuseCustomContextMenu[]` | - | | `[tabBarExtraContent]` | Extra content in tab bar | `TemplateRef` | - | | `[tabBarStyle]` | Tab bar style object | `object` | - | | `[tabBarGutter]` | The gap between tabs | `number` | - | | `[tabType]` | Basic style of tabs | `line, card` | `line` | | `[tabMaxWidth]` | The maximum width of each tab, unit: `px` | `number` | - | | `[routeParamMatchMode]` | Match the pattern when routing parameters are included, for example:`/view/:id`
- `strict` Strict mode `/view/1`, `/view/2` Different pages
- `loose` Loose mode `/view/1`, `/view/2` Same page and only one tab of component
- `((future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean)` custom function | `strict,loose` | `strict` | | `[disabled]` | Whether to disabled | `boolean` | `false` | | `[titleRender]` | Custom rendering of the title | `TemplateRef<{ $implicit: ReuseItem }>` | - | | `[storageState]` | Whether to store the state, keep the last browser state | `boolean` | `false` | | `[canClose]` | A function to determine what should be closed | `(options: { item: ReuseItem; includeNonCloseable: boolean }) => Observable` | - | | `[trackByFn]` | `track` function for `nz-tabs` | `(item: ReuseItem) => item.url` | - | | `(close)` | Close callback event | `EventEmitter` | - | | `(change)` | Callback when switching | `EventEmitter` | - | **Context Menu** The non-closeable item is forcibly removed when the keyboard `ctrl` is pressed. ### ReuseTabCached | Property | Description | Type | Default | |----------|-------------|------|---------| | `[title]` | Title | `string` | - | | `[url]` | URL | `string` | - | | `[closable]` | Whether to allow close | `boolean` | - | ### ReuseTabNotify | Property | Description | Type | Default | |----------|-------------|------|---------| | `[active]` | Event types | `title,close,closeRight,clear,move,closable,refresh,add` | - | ### ReuseContextI18n | Property | Description | Type | Default | |----------|-------------|------|---------| | `[close]` | Close | `string` | - | | `[closeOther]` | Close other tabs | `string` | - | | `[closeRight]` | Close tabs to the right | `string` | - | | `[clear]` | Clear tabs | `string` | - | ### ReuseCustomContextMenu | Property | Description | Type | Default | |----------|-------------|------|---------| | `[id]` | Identifier for current context menu | `string` | - | | `[title]` | Title for current context menu | `string` | - | | `[fn]` | Process method for current context menu | `(item: ReuseItem, menu: ReuseCustomContextMenu) => void` | - | | `[disabled]` | Whether to disabled | `(item: ReuseItem) => boolean` | - | ### Route data By routing the `data` property, you can provide partial [global configuration](/docs/global-config) for some pages, for example: ```ts // Specify no route { path: 'p1', component: DemoComponent, data: { reuse: false } } // Specify title { path: 'p1', component: DemoComponent, data: { title: 'New Title' } } ``` | Property | Description | Type | Default | |----------|-------------|------|---------| | `[reuse]` | Whether to reuse | `boolean` | - | | `[title]` | Title | `string` | - | | `[titleI18n]` | Ii18n title key | `string` | - | | `[reuseClosable]` | Whether to allow close | `boolean` | - | | `[keepingScroll]` | Keep the scrollbar position | `boolean` | - | > **Note:** The above data can also be reflected in the [Menu](/theme/menu#Menu) data. ## FAQ ### How to Debug Route reuse preserves component data state, which may bring another drawback. The Angular lifecycle hook is not triggered during the reuse process. In most cases, it can run normally. There are several common problems: - `OnDestroy` may handle the external style in component (eg: `body`), which can be resolved by Reuse Events - Turn on the `debug` mode to help you analyze ### Max Limiting the maximum number of reuse can reduce memory growth. There are several issues to be aware of: - `max` Forces a close and ignores the closable state when value changes - When it's out of `max` range, it will turn off the first open tab (Only **closable**), ingore close when all pages are **non-closable** ### Not supported QueryString parameters Route reuse preserves uses URLs to distinguish whether the same page, and QueryString query parameters will be repeatedly misused, so not supported, and the QueryString part is forced to be ignored. ### Multi-application cache processing Use `provideReuseTabConfig(storeKey: 'newKey')` Or overriding `REUSE_TAB_CACHED_MANAGER` to change the cache storage, for example when using a micro-frontend (similar to [ngx-planet](https://github.com/worktile/ngx-planet)) can rewrite cached data to `window` guaranteed data sharing. --- --- type: Service order: 6 title: RTLService subtitle: RTL Service --- RTL service control. ## API | Method | Property | Description | |--------|----------|-------------| | `dir` | `Direction` | Get or Set the current text direction | | `nextDir` | `Direction` | Get the next text direction | | `change` | `Observable` | Subscription change notification | | `toggle()` | - | Toggle text direction | --- --- order: 1 title: safe subtitle: XSS type: Pipe --- ## html Simplify the use of `bypassSecurityTrustHtml`. ```html
``` ## url Simplify the use of `bypassSecurityTrustUrl`. ```html ``` --- --- type: CURD title: se subtitle: Edit cols: 1 order: 3 module: import { SEModule } from '@delon/abc/se'; --- A higher-order components of the form HTML template. And optimized some details: - More friendly form validation status - Automated responsive layout - Automatic maintenance `id` The form HTML template consists of `se-container` container (directive) and `se` component, like this: ```html ``` Also, automatically processed all Angular built-in validation, such as `required`, `maxlength`, `min`, `pattern`, etc., with a red border to indicate invalid value status. ## API ### se-container | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[gutter]` | specify the distance between two items, unit is `px`, only `nzLayout:horizontal` | `number` | `32` | ✅ | | `[se-container]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | - | | | `[col]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | - | ✅ | | `[labelWidth]` | label text of width, unit is `px` | `number` | `150` | ✅ | | `[nzLayout]` | type of layout when `inline` forced size is `compact` | `'horizontal','vertical','inline'` | `'horizontal'` | ✅ | | `[size]` | size of edit, forced ingores `error`, `extra` | `'default','compact'` | `'default'` | ✅ | | `[firstVisual]` | Immediately show validation error message | `boolean` | `false` | ✅ | | `[ingoreDirty]` | Whether to ignore the `dirty` check | `boolean` | `false` | ✅ | | `[line]` | whether separation line style | `boolean` | `false` | - | | `[title]` | Display title | `string,TemplateRef` | - | - | | `[errors]` | Batch modify `se` error value | `SEErrorRefresh[]` | - | | | `[noColon]` | Whether to not display : after label text | `boolean` | `false` | - | ### se | Property | Description | Type | Default | |----------|-------------|------|---------| | `[col]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) (Inherited from `se-container`) | `'1','2','3','4','5','6'` | - | | `[label]` | Label | `string, TemplateRef` | - | | `[labelWidth]` | label text of width, unit is `px` (Inherited from `se-container`) | `number` | - | | `[hideLabel]` | Whether to hide the current label | `boolean` | `false` | | `[optional]` | Label optional information | `string, TemplateRef` | - | | `[optionalHelp]` | Label optional help | `string, TemplateRef` | - | | `[optionalHelpColor]` | The background color of label optional help | `string` | - | | `[error]` | validation error message | `string, TemplateRef, { [key: string]: string, TemplateRef }` | - | | `[extra]` | The extra prompt message. It is similar to help. | `string, TemplateRef` | - | | `[required]` | Whether required identifier, if not set, the value is automatically set according to whether the form element has `RequiredValidator` validation | `string` | - | | `[controlClass]` | Control area classes | `string` | - | | `[id]` | Custom `id` value of component | `string` | - | | `[line]` | whether separation line style (Inherited from `se-container`) | `boolean` | - | | `[noColon]` | Whether to not display : after label text | `boolean` | `false` | - | ### se-title Display title. ## QA ### When customize the component id? Matching for and `id` values associate the label with the appropriate form control. Because `id` must be unique, and `ng-alain` automatic maintenance `id`. In most cases, you don't need to care about the binding state of `id`. When you manually specify the `id` value, the priority is higher, but you also need to maintain the corresponding `id` value of the component. --- --- title: search subtitle: Search type: Examples --- Use expand/collapse to save space when there are many search conditions. Set `mode="search"` to quickly configure search mode, which automatically sets `layout` to `inline`, `liveValidate` to `false`, and changes button text to `Search`. Combine with expandable to mark less important fields as collapse, collapsed by default to keep the UI clean. --- --- title: segmented subtitle: Segmented type: Non-built-in widgets --- - When displaying multiple options and user can select a single option; - When switching the selected option, the content of the associated area changes. ## How to use Non-built-in modules need to additionally register `withSegmentedWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## API ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[block]` | Option to fit width to its parent\'s width | `boolean` | false | | | `[asyncData]` | Set children optional | `() => Observable` | - | | | `(valueChange)` | Emits when index of the currently selected option changes | `(data: { index: number; item: SFValue }) => void` | - | | --- --- title: select subtitle: Select type: Widgets order: 6 --- Select. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Data source | `SFSchemaEnumType[]` | - | | `[readOnly]` | Read only | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Asynchronous static data source | `() => Observable` | - | | `[size]` | Size, equals to `nzSize` | `string` | - | | `[compareWith]` | Same with [SelectControlValueAccessor](https://angular.io/api/forms/SelectControlValueAccessor#caveat-option-selection) | `(o1: any, o2: any) => boolean` | `(o1: any, o2: any) => o1===o2` | | `[placeholder]` | Placeholder | `string` | - | | `[autoClearSearchValue]` | Whether clear search box when an option is selected, only valid when `mode` is `multiple` or `tags`. | `boolean` | `true` | | `[allowClear]` | Allow clear | `boolean` | `false` | | `[clearValue]` | Default value when cleared | `any` | `undefined` | | `[variant]` | Variant | `outlined,borderless,filled,underlined` | `outlined` | | `[autoFocus]` | Focused by default | `boolean` | `false` | | `[dropdownClassName]` | className property of dropdown list | `string` | - | | `[dropdownMatchSelectWidth]` | Dropdown list and select have same width | `boolean` | `true` | | `[dropdownStyle]` | style property of dropdown list | `object` | - | | `[serverSearch]` | Whether using server search, won't fliter nz-option in frontend when it is true | `boolean` | `false` | | `[searchDebounceTime]` | Debounce time of server search | `number` | `300` | | `[searchLoadingText]` | Loading text of searching | `string` | - | | `[onSearch]` | Callback when search content is changed, parameter is search content, must return `Promise` object | `(text: string) => Promise` | - | | `[maxMultipleCount]` | Maximum selected labels | `number` | `Infinity` | | `[mode]` | Set mode for nz-select, suggest to add `default: null` for `tags`, otherwise, it would initialize an empty label. | `multiple,tags,default` | `default` | | `[notFoundContent]` | Content when dropdown list is empty | `string` | - | | `[showSearch]` | Enable search for signle select mode | `boolean` | `false` | | `[showArrow]` | Whether to show the drop-down arrow | `boolean` | `true`(for single select), `false`(for multiple select) | | `[tokenSeparators]` | Separator for automatic word segmentation when it is tags and multiple mode | `string[]` | `[]` | | `[maxTagCount]` | Maximum count of tag | `number` | - | | `[change]` | Callback function when selected nz-option is changed | `(ngModel:any丨any[], orgData: SFSchemaEnum丨SFSchemaEnum[])=>void` | - | | `[openChange]` | Callback function when dropdown list is open or closed | `(status: boolean) => void` | - | | `[scrollToBottom]` | Callback function when dropdown list is scrolled to bottom, can be used to trigger dynamic load | `() => void` | - | --- --- order: 1 title: SettingsService subtitle: Project configuration type: Service --- Project config data, includes [App](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/settings/interface.ts#L1), [Layout](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/settings/interface.ts#L15), [User](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/settings/interface.ts#L8), three data types And store persistence in `localStorage` (Please refer to [#1737](https://github.com/ng-alain/ng-alain/issues/1737) to switch to `sessionStorage`). **Suggest:** Call the `setApp()`, `setUser()` methods to restore project config data, via Angular startup service ([startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts)). ## API ### SettingsService | Name | Type | Return Value | Description | |------|------|--------------|-------------| | `layout` | `property` | `Layout` | Data of layout | | `app` | `property` | `App` | Data of app | | `user` | `property` | `User` | Data of User | | `notify` | `property` | `Observable` | Notify when layout, app, user information changes | | `setLayout(name: string, value: any)` | `method` | `boolean` | Set property value of layout | | `setApp(value: App)` | `method` | `boolean` | Set app data | | `setUser(value: User)` | `method` | `boolean` | Set user data | ### App | Property | Description | Type | Default | |----------|-------------|------|---------| | `[name]` | Name for app | `string` | - | | `[description]` | Description for app | `string` | - | ### User | Property | Description | Type | Default | |----------|-------------|------|---------| | `[name]` | Name for current user | `string` | - | | `[avatar]` | Avatar for current user | `string` | - | | `[email]` | Email for current user | `string` | - | ### Layout | Property | Description | Type | Default | |----------|-------------|------|---------| | `[collapsed]` | Whether to fold menu | `boolean` | - | | `[lang]` | Current language | `string` | - | | `[colorWeak]` | Color weak | `boolean` | `false` | ## FAQ **How to change the local storage key name?** Support add the configuration of `ALAIN_SETTING_KEYS` via the `global-config.module.ts` global configuration file, for example: ```diff const alainProvides = [ provideAlainConfig(alainConfig), + { + provide: ALAIN_SETTING_KEYS, + useValue: { + layout: 'new-layout', + user: 'new-user', + app: 'new-app', + }, + }, ]; ``` --- --- type: Layout order: 1 title: sg subtitle: Simple Grid cols: 1 module: import { SGModule } from '@delon/abc/sg'; --- A higher-order components of grid systems, that consists of `sg-container` (directive) and `sg` components: ```html
1 2 3 4
``` More friendly: ```html
1 2 3 4
``` ## API ### sg-container | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[gutter]` | specify the distance between two items, unit is `px`, only `nzLayout:horizontal` | `number` | `32` | ✅ | | `[sg-container]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | - | - | | `[col]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | `2` | ✅ | ### sg | Property | Description | Type | Default | |----------|-------------|------|---------| | `[col]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | - | --- --- title: slider subtitle: Slider type: Non-built-in widgets --- A Slider component for displaying current value and intervals in range. ## How to use Non-built-in modules need to additionally register `withSliderWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Notice - Ingored `exclusiveMinimum`, `exclusiveMaximum` ## API ### schema Property | Description | Type | Default -------- | ----------- | ---- | ------- `[minimum]` | The minimum value the slider can slide to | `number` | `0` `[maximum]` | The maximum value the slider can slide to | `number` | `100` `[multipleOf]` | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min). When `ui.marks` no null, `step` can be null. | `number` | `1` ### ui Property | Description | Type | Default -------- | ----------- | ---- | ------- `[range]` | dual thumb mode | `Boolean` | - `[marks]` | Tick mark of Slider, type of key must be `number`, and must in closed interval `[min, max]` ,each mark can declare its own style. | `NzMarks` | - `[dots]` | Whether the thumb can drag over tick only | `Boolean` | `false` `[included]` | Make effect when `marks` not null,`true` means containment and `false` means coordinative | `Boolean` | `true` `[vertical]` | If true, the slider will be vertical | `boolean` | `false` `[afterChange]` | Fire when `onmouseup` is fired. | `(value: NzSliderValue) => void` | - `[formatter]` | Slider will pass its value to `nzTipFormatter`, and display its value in Tooltip, and hide Tooltip when return value is null | `(value: number) => string` | - --- --- type: CURD title: st subtitle: Table cols: 1 order: 1 module: import { STModule } from '@delon/abc/st'; --- `st` is not creating another table component, but use **configurable** rendering table on base of `nz-table`. this method can satisfy most scenes in admin interfaces, but it's easier to rendering table. ## Data Source `data` supports multiple formats of data sources: URL and static data. ### URL The value is URL. - Resolve request data format problems with parameters such as `req.params`, `req.method` - Resolve backend data format through `res.reName` mapping data - Use `res.process` to optimize data before rendering table - Use `page.zeroIndexed` to adjust the http request when `pi` parameter follows the `0` base index, default is `1` base index - Automatically cancel paging when the response body value is an array type - Use `_HttpClient` send request, so applies to [AlainThemeConfig](/theme/http#AlainThemeConfig) configuration ### Static The value is `STData[]` or `Observable`, both follow the following rules: - `page.front` Front paging, default is `true` - `true` controlled by `st` according to `data` length, including: sorting, filtering, etc. - `false` controlled by the user through the `total` and `data` parameters, and maintains `(change)` when the page changes to reload data - Whether `page.show` displays pager; if not specified, it will not be displayed automatically if `ps>total` ### FAQ **Cannot read property 'text' of undefined** If the component has been rendered, this error may appear when `columns` is changed again. This is because `st` will only process the data according to `columns` each time. When the column definition changes, it may be because of the column definition. Unable to match with existing data, you may need to use `this.st.resetColumns({ columns: [], emitReload: true })` to update the column definition and reload the data. **Show INVALID DATA** When an exception is thrown when parsing column data, *INVALID DATA* will be forced to display. For example, when a column is specified `type:'number'`, an exception will be thrown when the actual value obtained is not a valid number type. ## API ### st | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[columns]` | Columns description | `STColumn[]` | - | - | | `[data]` | Data source | `string, STData[], Observable` | - | - | | `[req]` | Http request configuration | `STReq` | - | ✅ | | `[res]` | Http response configuration | `STRes` | - | ✅ | | `[pi]` | Page index | `number` | `1` | ✅ | | `[ps]` | Page size, default is `10` | `number` | `10` | ✅ | | `[total]` | Total data count, should set when nzServerRender is true, default is `0` | `number` | `0` | - | | `[page]` | Pager configuration | `STPage` | - | ✅ | | `[noResult]` | Custom no result content | `string,TemplateRef` | - | ✅ | | `[bordered]` | Whether to show all table borders | `boolean` | `false` | ✅ | | `[size]` | Size of table | `'small','middle','default'` | `'default'` | ✅ | | `[widthMode]` | Set the table width mode | `STWidthMode` | - | ✅ | | `[rowClassName]` | Row class name of table | `(record: STData, index: number) => string` | - | ✅ | | `[clickRowClassName]` | Row class name of click the row | `string, STClickRowClassNameType` | - | ✅ | | `[loading]` | Loading status of table, when specifying `null` is controlled by st | `boolean | null` | `null` | - | | `[loadingIndicator]` | The spinning indicator | `TemplateRef` | - | ✅ | | `[loadingDelay]` | Specifies a delay in milliseconds for loading state (prevent flush) | `number` | `0` | ✅ | | `[delay]` | Whether to delay table rendering, requires manual call to `refreshColumns()` to render | boolean | false | - | | `[scroll]` | Whether table can be scrolled in x/y direction, x or y can be a string that indicates the width and height of table body | `{ y?: string; x?: string }` | - | - | | `[virtualScroll]` | Enable virtual scroll mode,work with `[nzScroll]` | `boolean` | `false` | ✅ | | `[virtualItemSize]` | The size of the items in the list, same as [cdk itemSize](https://material.angular.io/cdk/scrolling/api) | `number` | `54` | ✅ | | `[virtualMaxBufferPx]` | The number of pixels worth of buffer to render for when rendering new items, same as [cdk maxBufferPx](https://material.angular.io/cdk/scrolling/api) | `number` | `200` | ✅ | | `[virtualMinBufferPx]` | The minimum amount of buffer rendered beyond the viewport (in pixels),same as [cdk minBufferPx](https://material.angular.io/cdk/scrolling/api) | `number` | `100` | ✅ | | `[virtualForTrackBy]` | The TrackByFunction to use for tracking changes. | `TrackByFunction` | - | ✅ | | `[singleSort]` | Single sort config
If not specified, return: `columnName=ascend|descend`
If specified, return: `sort=columnName.(ascend|descend)` | `STSingleSort` | `null` | ✅ | | `[multiSort]` | Whether to mulit-sort, recommended use in URL data source | `boolean, STMultiSort` | `false` | ✅ | | `[header]` | Table header renderer | `string,TemplateRef` | - | - | | `[showHeader]` | Whether show the head of the columns of the table | `boolean` | `true` | - | | `[footer]` | Table footer renderer | `string,TemplateRef` | - | - | | `[bodyHeader]` | Table extra body renderer in header, generally used to add total rows | `TemplateRef` | - | - | | `[body]` | Table extra body renderer, generally used to add total rows | `TemplateRef` | - | - | | `[widthConfig]` | Set col width can not used with width of STColumn | `string[]` | - | - | | `[expandRowByClick]` | Whether to expand row by clicking anywhere in the whole row | `boolean` | `false` | ✅ | | `[expandAccordion]` | Accordion mode | `boolean` | `false` | ✅ | | `[expand]` | Whether current column include expand icon | `TemplateRef<{ $implicit: STData; index: number }>` | - | - | | `[expandIcon]` | Custom expand icon | `TemplateRef<{ $implicit: STData; index: number }>` | - | | `[responsive]` | Whether to turn on responsive | `boolean` | `true` | ✅ | | `[responsiveHideHeaderFooter]` | Whether to display the header and footer under the small screen | `boolean` | `false` | ✅ | | `[resizable]` | Resize header of the current table, **Multiple headers not supported** | `STResizable, boolean` | - | - | | `[trackBy]` | `TrackByFunction` function of list loop `@for` | `TrackByFunction` | - | - | | `[drag]` | Drag soring | `STDragOptions, boolean` | - | - | | `(change)` | Events | `EventEmitter` | - | - | | `(error)` | Error event | `EventEmitter` | - | - | ### Properties & Methods | Name | Description | |------|-------------| | `[filteredData]` | Get all data after filtering & sorting
- Local data: including sorting, filtering
- Remote data: Don't pass `pi`, `ps` parameters in http request | | `[count]` | Get the number of the current page | | `[list]` | Get the data of the current page | | `resetColumns(options?: STResetColumnsOption)` | Reset columns | | `load(pi = 1, extraParams?: any, options?: STLoadOptions)` | Load specified page | | `reload(extraParams?: any, options?: STLoadOptions)` | Refresh current page | | `reset(extraParams?: any, options?: STLoadOptions)` | Reset data and `pi` to `1`, including single multi-select, sort, filter status (Covered default state) | | `addRow(data: STData | STData[], options?: { index?: number })` | Add a rows in the table | | `removeRow(data: STData | STData[] | number)` | Remove a row in the table | | `setRow(index: number | STData, item: STData, options?: { refreshSchema?: boolean; emitReload?: boolean; arrayProcessMethod?: boolean })` | Sets the row value for the `index` in the table | | `pureItem(itemOrIndex: STData | number)` | Return pure data, `st` internally maintains a set of data for caching, this part of data may affect the backend | | `clear(cleanStatus = true)` | Clear all data | | `clearStatus()` | Clean all status (like this: single multi-select, sort, filter status) | | `clearCheck()` | Clear all `checkbox` | | `clearRadio()` | Clear all `radio` | | `export(newData?: STData[] | true, opt?: STExportOptions)` | Export Excel and make sure you have imported `XlsxModule` | Some details: - `extraParams` Keep original values when is null - `STLoadOptions.merge` merge mode, if `true` merges with new values instead of replacing - `STLoadOptions.toTop` Whether to jump to the top, if not specified, it's determined by `page.toTop` **Usage** ```ts @Component({ template: ` ` }) class TestComponent { @ViewChild('st', { static: false }) comp: STComponent; // this.comp.load(); } ``` ### STReq | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[type]` | Pagination type, `page` used `pi`, `ps`; `skip` used `skip`, `limit` | `page,skip` | `page` | ✅ | | `[params]` | Request parameters, default to auto append `pi`, `ps` to URL | `any` | - | - | | `[ignoreParamNull]` | Whether to ignore `null` or `unfind` values in parameters | `Boolean` |`false` | ✅ | | `[method]` | Request method | `'POST','GET','HEAD','PUT','PATCH','DELETE'` | `'GET'` | ✅ | | `[body]` | Request body (only method is POST) | `any` | - | - | | `[headers]` | Request header | `any` | - | ✅ | | `[reName]` | Map name `pi`、`ps` | `STReqReNameType, ((result: any, options: { pi: number; ps: number; total: number }) => { total: number; list: T[] })` | `{ pi: 'pi', ps: 'ps', skip: 'skip', limit: 'limit' }` | ✅ | | `[allInBody]` | Whether to request all parameter data into `body` (except `url` itself parameter) | `boolean` | `false` | ✅ | | `[lazyLoad]` | Whether to delay loading data in first render `st` component | `boolean` | `false` | ✅ | | `[process]` | Pre-request data processing | `(requestOptions: STRequestOptions) => STRequestOptions` | - | ✅ | ### STRes | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[reName]` | Map name `total`、`list`, could be set like `a.b.c` | `{total:string;list:string}` | - | ✅ | | `[process]` | Data preprocessing | `(data: STData[], rawData?: any) => STData[]` | - | ✅ | ### STPage | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[front]` | Front paging when `data` is `any[]` or `Observable` | `boolean` | `true` | ✅ | | `[zeroIndexed]` | Whether the backend paging uses the `0` base index (only data is url) | `boolean` | `false` | ✅ | | `[position]` | Specify the position of Pagination | `top,bottom,both` | `bottom` | ✅ | | `[placement]` | Specify the direction of Pagination | `left,center,right` | `right` | ✅ | | `[show]` | Whether to show pager | `boolean` | `true` | ✅ | | `[showSize]` | Determine whether `ps` can be changed | `boolean` | `false` | ✅ | | `[pageSizes]` | Specify the sizeChanger options | `number[]` | `[10, 20, 30, 40, 50]` | ✅ | | `[showQuickJumper]` | Determine whether you can jump to pages directly | `boolean` | `false` | ✅ | | `[total]` | To display the total number and range, support custom string template (Three variable names: `total` for total data, `range[0]` and `range[1]` for current data range; **Variable name** must be double curly braces wrapper) | `boolean, string` | `false` | ✅ | | `[toTop]` | To top when pager changed | `boolean` | `true` | ✅ | | `[toTopOffset]` | To top offset value | `number` | `100` | ✅ | | `[itemRender]` | To customize Pagination item, same as Pagination | `TemplateRef<{ $implicit: 'page' \| 'prev' \| 'next', page: number }>` | - | ✅ | | `[simple]` | Whether to use simple mode | `boolean` | - | ✅ | | `[checkboxIdMap]` | The identifier column of the cached checkbox list | `string` | - | ✅ | ### STError | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Error type | `req` | - | | `[error]` | Error message | `any` | - | ### STChange | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Change type | `STChangeType` | - | | `[pi]` | Page index | `number` | - | | `[ps]` | Page size | `number` | - | | `[total]` | Total data | `number` | - | | `[loaded]` | Parameters of type `loaded` | `STData[]` | - | | `[checkbox]` | Parameters of type `checkbox` | `STData[]` | - | | `[radio]` | Parameters of type `radio` | `STData` | - | | `[sort]` | Parameters of type `sort` | `STChangeSort` | - | | `[filter]` | Parameters of type `filter` | `STColumn` | - | | `[click]` | Parameters of type `click` | `STChangeRowClick` | - | | `[dblClick]` | Parameters of type `dblClick` | `STChangeRowClick` | - | | `[expand]` | Parameters of type `expand` | `STData` | - | ### STChangeSort | Property | Description | Type | Default | |----------|-------------|------|---------| | `[value]` | Current column sort status | `ascend,descend` | - | | `[map]` | All column sorting states | `{ [key: string]: string }` | - | | `[column]` | Column description | `STColumn` | - | ### STChangeRowClick | Property | Description | Type | Default | |----------|-------------|------|---------| | `[e]` | Current rows event | `Event` | - | | `[item]` | Current rows data | `STData` | - | | `[index]` | Current rows index | `number` | - | ### STExportOptions | Property | Description | Type | Default | |----------|-------------|------|---------| | `[sheetname]` | Sheet name | `string` | `Sheet1` | | `[filename]` | Save file name | `string` | `export.xslx` | | `[callback]` | Callback before saving | `(wb: WorkBook) => void` | - | ### STSingleSort | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[key]` | Request parameter name | `string` | `sort` | ✅ | | `[nameSeparator]` | Column name and state separator | `string` | `.` | ✅ | ### STMultiSort | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[key]` | Request parameter name | `string` | `sort` | ✅ | | `[separator]` | Separator between attributes | `string` | `-` | ✅ | | `[nameSeparator]` | Column name and state separator | `string` | `.` | ✅ | | `[arrayParam]` | Whether to pass parameters as an array
`true` Indicates the use of `url?sort=name.asc&sort=age.desc`
`false` Indicates the use of `url?sort=name.asc-age.desc` | `boolean` | `false` | ✅ | | `[keepEmptyKey]` | Whether to keep send empty key
`true` send the `key` name anyway
`false` don't send `key` when not sorting | `boolean` | `true` | ✅ | | `[global]` | **Only global config**, Whether global multi sort mode
`true` all `st` defaults multi-sort
`false` all `st` non-multiple sorting, just only configurable for rule | `boolean` | `true` | ✅ | ### STData | Property | Description | Type | Default | |----------|-------------|------|---------| | `[checked]` | Select or radio button `checked` status value | `boolean` | - | | `[disabled]` | Select or radio button `disabled` status value | `boolean` | - | | `[expand]` | Whether to expand the status value | `boolean` | - | | `[showExpand]` | Whether show expand icon | `boolean` | - | | `[className]` | Class name of the row | `string` | - | ### STColumn | Property | Description | Type | Default | |----------|-------------|------|---------| | `[title]` | Name of this column | `string, STColumnTitle` | - | | `[i18n]` | I18n key of this column | `string` | - | | `[type]` | `no` Rows number
`checkbox` selection
`radio` selection
`link` Link that triggers `click`
`img` Align to the center
`number` Align to the right
`currency` Align to the right
`date` Align to the center
`badge` [Nz-Badge](https://ng.ant.design/components/badge/en)
`tag` [Nz-Tag](https://ng.ant.design/components/tag/en)
`yn` Make boolean as [badge](/theme/yn)
`cell` Rendered using the `cell` component, see [cell](/components/cell)
`widget` Custom widgets to render columns | `string` | - | | `[cell]` | Rendered using the `cell` component, see [cell](/components/cell). | `CellOptions | ((record: T, column: STColumn) => CellOptions)` | - | | `[index]` | Display field of the data record, could be set like `a.b.c` | `string, string[]` | - | | `[render]` | Custom render template ID | `string, TemplateRef, TemplateRef<{ $implicit: STData; index: number }>` | - | | `[renderTitle]` | Title custom render template ID | `string, TemplateRef, TemplateRef<{ $implicit: STColumn; index: number }>` | - | | `[default]` | Replace with default value when no data exists (value typeof is `undefined`) | `string` | - | | `[buttons]` | Buttons of this column | `STColumnButton[]` | - | | `[maxMultipleButton]` | Max button option can be showed, and the extra part are auto generated under `more` | `STColumnMaxMultipleButton, number` | - | | `[width]` | Width of this column (**NOTICE:** If the fixed column must be a number), e.g: `100`, `10%`, `100px` | `string,number` | - | | `[fixed]` | Set column to be fixed, must specify `width` | `left,right` | - | | `[format]` | Format value of this column | `(item: STData, col: STColumn, index: number) => string` | - | | `[className]` | Class name of this column, e.g: `text-center`, `text-right`, `text-error`, pls refer to [Style Tools](/theme/tools) | `string` | - | | `[colSpan]` | Span of this column's title | `number` | - | | `[onCell]` | Set props on per cell | `(item: T, index: number) => STOnCellResult;` | - | | `[sort]` | Sort config of this column, Remote Data Configuration**Priority** Rule:
`true` allow sorting, should be auto generate compose `compare: (a, b) => a[index] - b[index]` method when data is local
`ascend`
`descend`
`string` corresponding `key` value | `true,string,STColumnSort` | - | | `[filter]` | Filter config of this column | `STColumnFilter` | - | | `[selections]` | Config of type is checkbox | `STColumnSelection[]` | - | | `[numberDigits]` | Config of type is number | `string` | - | | `[dateFormat]` | Config of type is date | `yyyy-MM-dd HH:mm` | - | | `[currency]` | Currency format option, `type=currency` is valid | `STColumnCurrency` | - | | `[yn]` | Config of type is yn | `STColumnYn` | - | | `[exported]` | Whether to allow export | `boolean` | `true` | | `[acl]` | ACL permission (Use `can()` verify) | `ACLCanType` | - | | `[click]` | Callback of type is link | `(record: STData, instance?: STComponent) => void` | - | | `[badge]` | Config of type is badge | `STColumnBadge` | - | | `[tag]` | Config of type is tag | `STColumnTag` | - | | `[enum]` | Config of type is enum | `{ [key: string]: string; [key: number]: string }` | - | | `[widget]` | Config of type is widget | `STWidgetColumn` | - | | `[noIndex]` | Line number index start value | `number,(item: STData, col: STColumn, idx: number) => number` | `1` | | `[iif]` | Custom conditional expression
1. Execute only once when `columns` is assigned
2. Call `resetColumns()` to trigger again | `(item: STColumn) => boolean` | - | | `[statistical]` | Statistics | `STStatisticalType,STStatistical` | - | | `[resizable]` | Resize header, **Multiple headers not supported** | `STResizable, boolean` | - | - | | `[children]` | Group columns | `STColumn[]` | - | | `[safeType]` | Safe rendering type, Support [global config](https://ng-alain.com/docs/global-config) | `text,html,safeHtml` | `safeHtml` | | `[customRequest]` | Override the default request behavior, you can customize your own request implementation, for example: Graphql, Support [global config](https://ng-alain.com/docs/global-config) | `(options: STCustomRequestOptions) => Observable` | - | ### STColumnTitle | Property | Description | Type | Default | |----------|-------------|------|---------| | `[text]` | Text of header, can be choose one of `text` or `i18n` | `string` | - | | `[i18n]` | I18n key of header, can be choose one of `text` or `i18n` | `string` | - | | `[optional]` | Optional information of header | `string` | - | | `[optionalHelp]` | Optional help of header | `string` | - | ### STColumnSort | Property | Description | Type | Default | |----------|-------------|------|---------| | `[default]` | Default order of sorted values | `ascend,descend` | - | | `[compare]` | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction, `null` ingore local sort but keeping sort function. | `(a: STData, b: STData) => number, null` | - | | `[key]` | Unique key of this column, default is `index` property value
`multiSort: false` => `key: 'name' => ?name=1&pi=1`
`multiSort: true` allow multiple sort keys, or use `STMultiSort` to specify multi-column sort key merge rule | `string` | - | | `[reName]` | Map name
`{ ascend: '0', descend: '1' }` => `?name=1&pi=1`
`{ ascend: 'asc', descend: 'desc' }` => `?name=desc&pi=1` | `{ ascend?: string, descend?: string }` | - | | `[directions]` | Supported sort order, could be `'ascend'`, `'descend'`, `null` | `Array<'ascend' \| 'descend' \| null>` | `['ascend', 'descend', null]` | ✅ | ### STColumnFilter | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Type of the filter, `keyword` render by input | `default,keyword,number,date,custom` | `default` | | `[menus]` | Filter menu config | `STColumnFilterMenu[]` | - | | `[fn]` | Filter function for local data | `(filter: STColumnFilterMenu, record: STData) => boolean` | - | | `[default]` | Whether the `data` is filtered | `boolean` | - | | `[icon]` | Customized filter icon
When `type='default'` default `filter`
when `type='keyword'` default `search` | `string | STIcon` | `filter` | | `[multiple]` | Whether multiple filters can be selected | `boolean` | `true` | | `[confirmText]` | Text of the confirm button | `string` | - | | `[clearText]` | Text of the clear button | `string` | - | | `[key]` | Unique key of this column, default is `index` property value | `string` | - | | `[reName]` | Map name | `(list: STColumnFilterMenu[], col: STColumn) => Object` | - | | `[custom]` | Custom template | `TemplateRef<{ $implicit: STColumnFilter; col: STColumn; handle: STColumnFilterHandle }>` | - | | `[showOPArea]` | Whether to display the operation area | `boolean` | `true` | | `[placeholder]` | placeholder | `boolean` | `true` | | `[number]` | Option for the type is `number` | `Object` | - | | `[date]` | Option for the type is `date` | `Object` | - | ### STColumnFilterMenu | Property | Description | Type | Default | |----------|-------------|------|---------| | `[text]` | Filter text
When `type: 'keyword'` is `placeholder` value | `string` | - | | `[value]` | Filter value | `any` | - | | `[checked]` | Whether checked | `boolean` | - | | `[acl]` | ACL permission (Use `can()` verify) | `ACLCanType` | - | ### STIcon | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[type]` | Type of the ant design icon | `string` | - | - | | `[theme]` | Type of the ant design icon | `outline | twotone | fill` | `outline` | ✅ | | `[spin]` | Rotate icon with animation | `boolean` | `false` | ✅ | | `[twoToneColor]` | Only support the two-tone icon. Specific the primary color. | `string` | - | v | | `[iconfont]` | Type of the icon from iconfont | `string` | - | ✅ | ### STColumnButton | Property | Description | Type | Default | |----------|-------------|------|---------| | `[text]` | Text of button, coexist with icon | `string | (record: T, btn: STColumnButton) => string` | - | | `[icon]` | Icon of button, coexist with text | `string | STIcon | ((record: T, btn: STColumnButton) => STIcon | null | undefined)` | - | | `[i18n]` | I18n key of button | `string` | - | | `[type]` | Type of button | `none,del,modal,static,drawer,link` | - | | `[click]` | Click callback;
**function** when `type=modal` will only fire when `confirmed`
**reload** Refresh current page
**load** load `1` page | `(record: STData, modal?: any, instance?: STComponent) => void | reload` | - | | `[pop]` | Whether to pop confirm | `boolean, string, STColumnButtonPop` | `false` | | `[modal]` | Config of type is modal or static | `STColumnButtonModal` | - | | `[drawer]` | Config of type is drawer | `STColumnButtonDrawer` | - | | `[children]` | Drop-down menu, only supports level 1 | `STColumnButton[]` | - | | `[acl]` | ACL permission (Use `can()` verify) | `ACLCanType` | - | | `[iif]` | Custom conditional expression | `(item: STData, btn: STColumnButton, column: STColumn) => boolean` | `() => true` | | `[iifBehavior]` | Render button mode when the conditional expression `false` value | `hide,disabled` | `hide` | | `[tooltip]` | Button popup tip | `string` | - | | `[className]` | Class name of this button, e.g: `text-error`, pls refer to [Style Tools](/theme/tools) | `string | ((record: T, btn: STColumnButton) => NgClassType | null | undefined)` | - | ### STColumnButtonModal | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[component]` | Modal component class | `any` | - | - | | `[params]` | Dialog parameter | `(record: STData) => Object` | - | - | | `[paramsName]` | Receive parameter name of the target component, If target component receive value is null, pls check [global-config.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/global-config.module.ts#L52) Global settings | `string` | `record` | ✅ | | `[size]` | Size of modal, support number type | `'sm','md','lg','xl'` | `'lg'` | ✅ | | `[exact]` | Exact match return value, default is `true`, If the return value is not null (`null` or `undefined`) is considered successful, otherwise it is considered error. | `boolean` | `true` | ✅ | | `[includeTabs]` | Whether to wrap the nz-tabset, fix content spacing problem | `boolean` | - | - | | `[modalOptions]` | nz-modal raw parameters [ModalOptions](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/modal/modal-types.ts) | `any` | - | ✅ | ### STColumnButtonDrawer | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[title]` | Title of drawer | `string` | - | - | | `[component]` | Drawer component class | `any` | - | - | | `[params]` | Dialog parameter | `(record: STData) => Object` | - | - | | `[paramsName]` | Receive parameter name of the target component, If target component receive value is null, pls check [global-config.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/global-config.module.ts#L52) Global settings | `string` | `record` | ✅ | | `[size]` | Size of drawer, support number type | `'sm','md','lg','xl'` | `'md'` | ✅ | | `[drawerOptions]` | nz-drawer raw parameters [NzDrawerOptions](https://ng.ant.design/components/drawer/zh#nzdraweroptions) | `any` | - | ✅ | | `[footer]` | Whether to include the bottom toolbar | `boolean` | `true` | ✅ | | `[footerHeight]` | Height of bottom toolbar | `number` | `55` | ✅ | ### STColumnSelection | Property | Description | Type | Default | |----------|-------------|------|---------| | `[text]` | Selection text | `string` | - | | `[select]` | Select callback event | `(data: STData[]) => void` | - | | `[acl]` | ACL permission (Use `can()` verify) | `ACLCanType` | - | ### STColumnYn | Property | Description | Type | Default | |----------|-------------|------|---------| | `[truth]` | Truth condition value | `any` | `true` | | `[yes]` | Badge `true` text | `string` | `是` | | `[no]` | Badge `false` text | `string` | `否` | | `[mode]` | Display mode for yn | `full,icon,text` | `icon` | ### STcolumnCurrency | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Currency rendering type | `commas, mega` | `commas` | | `[format]` | See [CurrencyService.format](https://ng-alain.com/util/format/en#format) | `CurrencyFormatOptions` | - | ### STColumnBadge | Property | Description | Type | Default | |----------|-------------|------|---------| | `[text]` | Badge text | `string` | - | | `[color]` | Badge color value | `success,processing,default,error,warning` | - | | `[tooltip]` | Text popup tip | `string` | - | ### STColumnTag | Property | Description | Type | Default | |----------|-------------|------|---------| | `[text]` | Tag text | `string` | - | | `[color]` | Tag color value | `string` | - | | `[tooltip]` | Text popup tip | `string` | - | ### STWidgetColumn | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Specify the type name, which can be customized by `STWidgetRegistry`, [for example](https://github.com/ng-alain/delon/blob/master/src/app/shared/st-widget/st-widget.module.ts) | `string` | - | | `[params]` | Parameters of the target component | `(options: { record: STData; column: STColumn }) => {}` | - | ### STWidthMode | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[type]` | Type of width mode | `strict,default` | `default` | ✅ | | `[strictBehavior]` | Behavior type of `strict` | `wrap,truncate` | `truncate` | ✅ | ### STStatistical | Property | Description | Type | Default | |----------|-------------|------|---------| | `[type]` | Statistic type of current column | `STStatisticalType | STStatisticalFn` | - | | `[digits]` | The number of digits to appear after the decimal point | `number` | `2` | | `[currency]` | Whether formatting currency, default to `true` when `type` is `STStatisticalFn`,`sum`,`average`,`max`,`min` | `boolean` | - | **STStatisticalFn** ```ts ( values: number[], col: STColumn, list: STData[], rawData?: any, ) => STStatisticalResult ``` ### STResizable | Property | Description | Type | Default | |----------|-------------|------|---------| | `[disabled]` | Disable resize | `boolean` | `true` | | `[bounds]` | Specifies resize boundaries | `window, parent, ElementRef` | `window` | | `[maxWidth]` | Maximum width of resizable elemen | `number` | `360` | | `[minWidth]` | Minimum width of resizable element | `number` | `60` | | `[preview]` | Enable preview when resizing | `boolean` | `true` | ## Theme | Property | Description | Default |----|----|----| | `@nz-table-img-radius` | Radius size for the image in td | `4px` | | `@nz-table-img-margin-right` | Margin right for the image in td | `4px` | | `@nz-table-img-max-height` | Max height for the image in td | `32px` | | `@nz-table-img-max-width` | Max width for the image in td | `32px` | | `@nz-table-even-background` | Even background for the row | `none` | | `@nz-table-rep-max-width` | Trigger when the viewable area is less than | `` | | `@nz-table-rep-min-width` | Trigger when the visible area is greater than | `` | | `@nz-table-rep-header-background` | Header background in responsive | `@border-color-split` | | `@nz-table-rep-even-background` | Event background in responsive | `#f9f9f9` | | `@nz-table-rep-column-name-color` | Name color in responsive | `rgba(0, 0, 0, 0.5)` | | `@nz-table-rep-column-name-text-align` | Name text align in responsive | `right` | | `@nz-table-rep-column-name-width` | Name column width in responsive | `100px` | | `@nz-table-rep-column-name-padding-right` | Right padding of title and content in responsive | `8px` | | `@table-row-hover-bg` | Hover background color of the row | `#fafafa` | | `@st-btn-disabled-color` | Text color of the button | `rgba(0, 0, 0, 0.25)` | | `@st-title-optional-color` | Optional color of title | `rgba(0, 0, 0, 0.35)` | | `@st-resizable-handle-width` | Width of the rasizable handle | `1px` | | `@st-resizable-handle-height` | Height of the rasizable handle | `60%` | | `@st-resizable-handle-color` | Color of the rasizable handle | `@border-color-base` | --- --- title: string subtitle: Input type: Widgets order: 3 --- Default widget, A basic widget for getting the user input is a text field. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[maxLength]` | Maximum length of the input | `number` | - | | `[readOnly]` | Whether to disable the state | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[size]` | Size of the `nz-input` | `string` | - | | `[type]` | type of the input, e.g: `password` | `string` | - | | `[placeholder]` | placeholder of the input | `string` | - | | `[variant]` | Variant | `outlined,borderless,filled,underlined` | `outlined` | | `[autocomplete]` | autocomplete of the input | `HTML Attribute` | - | | `[autofocus]` | autofocus of the input | `HTML Attribute` | - | | `[addOnBefore]` | The label text displayed before (on the left side of) the input field. | `string` | - | | `[addOnAfter]` | The label text displayed after (on the right side of) the input field. | `string` | - | | `[addOnBeforeIcon]` | The label icon's ngClass displayed before. | `string` | - | | `[addOnAfterIcon]` | The label icon's ngClass displayed after. | `string` | - | | `[prefix]` | The prefix icon for the Input. | `string` | - | | `[prefixIcon]` | The prefix icon's ngClass for the Input. | `string` | - | | `[suffix]` | The suffix icon for the Input. | `string` | - | | `[suffixIcon]` | The suffix icon's ngClass for the Input. | `string` | - | | `[changeDebounceTime]` | `change` event throttling and sequence control threshold | `number` | - | | `[changeMap]` | Convert data, equivalent to `switchMap` operation | `(val: string) => Observable` | - | | `[change]` | The content event for the Input. | `(val: string) => void` | - | | `[focus]` | The focus event for the Input. | `(e: FocusEvent) => void` | - | | `[blur]` | The blur event for the Input. | `(e: FocusEvent) => void` | - | | `[enter]` | The enter event for the Input. | `(e: KeyboardEvent) => void` | - | --- --- type: CURD title: sv subtitle: View cols: 1 order: 2 module: import { SVModule } from '@delon/abc/sv'; --- Viewing grid system is a higher-order components based on the original [Grid System](https://ng.ant.design/components/grid/zh) that server for view pages. ## API ### sv-container | Property | Description | Type | Default | Global Config | |----------|-------------|------|---------|---------------| | `[sv-container]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | `3` | ✅ | | `[col]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | `'1','2','3','4','5','6'` | `3` | ✅ | | `[size]` | size of view | `'small','large'` | `'large'` | ✅ | | `[layout]` | type of layout | `'horizontal','vertical'` | `'horizontal'` | ✅ | | `[gutter]` | specify the distance between two items, unit is `px` | `number` | `32` | ✅ | | `[labelWidth]` | label text of width | `number` | - | ✅ | | `[default]` | whether default text | `boolean` | `true` | ✅ | | `[title]` | Display title | `string,TemplateRef` | - | - | | `[noColon]` | Whether to not display : after label text | `boolean` | `false` | - | | `[bordered]` | Whether to display the border | `boolean` | `false` | - | ### sv | Property | Description | Type | Default | |----------|-------------|------|---------| | `[col]` | specify the maximum number of columns to display, the final columns number is determined by col setting combined with [Responsive Rules](/theme/responsive) | - | | | `[label]` | label of view | `string,TemplateRef` | - | | `[unit]` | unit of view | `string,TemplateRef` | - | | `[default]` | whether default text, inherit `sv-container` | `boolean` | - | | `[type]` | type of view | `'primary','success','danger','warning'` | - | | `[optional]` | Label optional information | `string, TemplateRef` | - | | `[optionalHelp]` | Label optional help | `string, TemplateRef` | - | | `[optionalHelpColor]` | The background color of label optional help | `string` | - | | `[noColon]` | Whether to not display : after label text | `boolean` | `false` | - | | `[hideLabel]` | Whether to hide the current label | `boolean` | `false` | ### sv-title Display title. ### sv-value 值展示。 | Property | Description | Type | Default | |----------|-------------|------|---------| | `[prefix]` | Prefix of value | `string` | - | | `[unit]` | Unit of value | `string` | - | | `[tooltip]` | Tooltip text of value | `string, TemplateRef` | - | | `[size]` | Size of value | `'large','small','default'` | `default` | --- --- title: tag subtitle: Tag type: Non-built-in widgets --- Tag for categorizing or markup, **Notice:** Just only supported `checkable` tag mode. ## How to use Non-built-in modules need to additionally register `withTagWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Data source | `SFSchemaEnumType[]` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Async data source | `() => Observable` | - | | `[mode]` | Mode of tag | `'closeable'|'default'|'checkable'` | `'checkable'` | | `[onClose]` | Callback executed when tag is closed | `(e:MouseEvent) => void` | - | | `[checkedChange]` | Checked status change call back | `(status: boolean) => void` | - | --- --- type: Layout title: tag-select subtitle: Tag Select cols: 1 module: import { TagSelectModule } from '@delon/abc/tag-select'; --- Increase the label expansion and retraction function. ## API ### tag-select | Property | Description | Type | Default | |----------|-------------|------|---------| | `[expandable]` | Whether to enabled | `boolean` | `true` | | `(change)` | Callback | `EventEmitter` | - | --- --- title: text subtitle: Text type: Widgets --- Text in form. ## Rules - Forced remove of the `required` effect - Auto render `-` if the `defaultText` value does not exist ## API ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[defaultText]` | Default text of this item | `string` | `-` | | `[html]` | Whether to support HTML | `boolean` | `true` | --- --- title: textarea subtitle: Textarea type: Widgets --- Textarea. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[maxLength]` | Maximum length of the input | `number` | - | | `[readOnly]` | Whether to disable the state | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[size]` | Size of the `nz-input` | `string` | - | | `[placeholder]` | placeholder of the input | `string` | - | | `[autosize]` | height autosize feature, can be set object `{ minRows: 2, maxRows: 6 }` | `{ minRows?: number; maxRows?: number }` | `{ minRows: 1, maxRows: 0 }` | | `[variant]` | Variant | `outlined,borderless,filled,underlined` | `outlined` | | `[maxCharacterCount]` | `textarea` maximum character count displayed | `number` | - | | `[computeCharacterCount]` | customized `characterCount` computation function | `(v: string) => number` | `v => v.length` | | `[change]` | The content event for the Input. | `(val: string) => void` | - | | `[focus]` | The focus event for the Input. | `(e: FocusEvent) => void` | - | | `[blur]` | The blur event for the Input. | `(e: FocusEvent) => void` | - | --- --- type: Theme title: theme-btn subtitle: Component-Change Style cols: 1 order: 1001 module: import { ThemeBtnModule } from '@delon/theme/theme-btn'; --- It is used to switch the customized style file during the running process, so as to play the online skin change function. ## API ### layout-default | Property | Description | Type | Default | |----------|-------------|------|---------| | `[types]` | Type of theme list | `ThemeBtnType[]` | `[ { key: 'default', text: 'Default Theme' }, { key: 'dark', text: 'Dark Theme' }, { key: 'compact', text: 'Compact Theme' }, ]` | | `[devTips]` | Tips in development | `String` | `When the dark.css file can't be found, you need to run it once: npm run theme` | | `[deployUrl]` | URL where files will be deployed. Generally needed when using `ng b --deploy-url` | `String` | `-` | | `(themeChange)` | Theme Change Notification | `EventEmitter` | `-` | --- --- title: time subtitle: Time type: Non-built-in widgets --- To select/input a time. ## How to use Non-built-in modules need to additionally register `withTimeWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Notice - Format is divided into two types: **Data format** means form data, **Display format** means display data ([nzFormat](https://ng.ant.design/components/time-picker/en#api)) - All **Data format** units, reference [date-fns format](https://date-fns.org/v1.29.0/docs/format) (China mirror: [moment format](http://Momentjs.cn/docs/#/displaying/format/)) - Specify `schema.format` must follow [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) time format, otherwise considered as a format error, default rules: - `time`、`full-time` default is `HH:mm:ss` - When `schema.format` is not specified, the data formatting (Allows you to reassign default values via [Global Configuration](/docs/global-config)) is determined by the `schema.type` type: - `string` default is `HH:mm:ss` - `number` default is `T` 13-bit Unix Timestamp - Since `disabledHours`, `disabledMinutes`, `disabledSeconds` will cause the time format to be corrupted, it may cause the display format error. The solution is specify a complete `Date` object in the default value (`schema.default` or `formData`) ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[readOnly]` | Whether to disable the state | `boolean` | - | | `[format]` | Data format type | `string` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[size]` | Size of the `nz-date-picker` | `string` | - | | `[placeholder]` | Placeholder of date input | `string` | - | | `[displayFormat]` | Display format([nzFormat](https://ng.ant.design/components/date-picker/en#api)) | `string` | `yyyy-MM-dd HH:mm:ss` | | `[utcEpoch]` | Whether UTC (represents the number of milliseconds from `1970`) | `boolean` | `false` | | `[allowEmpty]` | allow clearing text | `boolean` | `true` | | `[clearText]` | clear tooltip of icon | `string` | `清除` | | `[defaultOpenValue]` | default open panel value | `Date` | `new Date()` | | `[disabledHours]` | to specify the hours that cannot be selected | `() => number[]` | - | | `[disabledMinutes]` | to specify the minutes that cannot be selected | `(hour: number) => number[]` | - | | `[disabledSeconds]` | to specify the seconds that cannot be selected | `(hour: number, minute: number) => number[]` | - | | `[hideDisabledOptions]` | hide the options that can not be selected | `boolean` | `false` | | `[hourStep]` | interval between hours in picker | `number` | `1` | | `[minuteStep]` | interval between minutes in picker | `number` | `1` | | `[secondStep]` | interval between seconds in picker | `number` | `1` | | `[popupClassName]` | className of panel | `string` | - | | `[change]` | a callback function, can be executed when the selected time is changing | `(value: Date) => void` | - | | `[openChange]` | a callback function which will be called while panel opening/closing | `(status: boolean) => void` | - | | `[nowText]` | text of the Now button | `string` | - | | `[okText]` | text of the Ok button | `string` | - | --- --- title: tinymce subtitle: Tinymce Rich Text type: Third Widgets --- Tinymce rich text. ## How to Use **Installation dependencies** Since the Tinymce editor relies on a third-party plug-in [ngx-tinymce](https://github.com/cipchk/ngx-tinymce), the dependency should be installed first when using it `npm i -S ngx-tinymce` - 1. Register `provideNuMonacoEditorConfig()` in `app.config.ts` - 2. Register `withTinymceWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). > For more tinymce configuration, please refer to [ngx-tinymce](https://github.com/cipchk/ngx-tinymce). ## API ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | config | `any` | | see [configure](https://www.tinymce.com/docs/configure/integration-and-setup/) | | loading | `string,TemplateRef` | - | Loading status of tinymce | | disabled | `boolean` | `false` | Set tinymce mode is `readonly` if `true` | | inline | `boolean` | `false` | Inline editor | | delay | `number` | 0 | Delayed rendering, unit is 'millisecond' | | placeholder | `string` | - | Placeholder for tinymce, **NOTE:** dependent on [tinymce-placeholder](https://github.com/mohan/tinymce-placeholder) | | ready | `EventEmitter` | - | Tinymce ready callback | --- --- order: 4 title: TitleService subtitle: Title Service type: Service --- Used to set page title, generally listen for route changed and refresh title, for example: [app.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/app.ts#L54); The default data from `MenuService`. **Suggest:** Set the `prefix` or `suffix` value via Angular startup service ([startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts)). ## Orders Get the `title` value according to the following order: 1. Routing configuration `{ data: { title: 'page name', titleI18n: 'page-name' } } 2. Parse the menu data based on the current URL 3. Get `h1` content in page `alain-default__content-title` or `page-header__title` 4. Default title ## API | Name | Type | Description | | ---------------------------------------------- | ---------- | ------------------------------- | | `default` | `property` | Default title of document title | | `selector` | `property` | Set the default CSS selector string | | `separator` | `property` | Separator | | `prefix` | `property` | Prefix of document title | | `suffix` | `property` | Suffix of document title | | `reverse` | `property` | Whether to reverse | | `setTitle(title?: string | string[])` | `method` | Set the document title, will be delay `25ms`, pls refer to [#1261](https://github.com/ng-alain/ng-alain/issues/1261) | | `setTitleByI18n(key: string, params?: Object)` | `method` | Set i18n key of the document title | --- --- title: token subtitle: Token type: Tools --- ## WINDOW Access to global `window` object. ## PAGE_VISIBILITY Use the `visibilitychange` event to monitor whether the browser tab is visible, which is generally used when the user leaves the browser tab to temp interrupt the backend to continue sending requests. --- --- title: transfer subtitle: Transfer type: Non-built-in widgets --- Double column transfer choice box. ## How to use Non-built-in modules need to additionally register `withTransferWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Note - `default` value `direction: 'right'` indicates right column. ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Data source | `SFSchemaEnumType[]` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Asynchronous data | `() => Observable` | - | | `[titles]` | Title list, the order is from left to right | `string[]` | `['', '']` | | `[operations]` | Operation list, the order is from top to buttom | `string[]` | `['', '']` | | `[listStyle]` | A custom CSS style used for rendering the transfer columns. equals to `ngStyle` | `object` | - | | `[itemUnit]` | single unit | `string` | `item` | | `[itemsUnit]` | multiple unit | `string` | `items` | | `[showSearch]` | If included, a search box is shown on each column | `boolean` | `false` | | `[filterOption]` | Accept `inputValueoption` two parameters, return `true` when `option` matches, otherwise, return `false` | - | - | | `[searchPlaceholder]` | Placeholder of search box | `string` | - | | `[notFoundContent]` | Text to display when a column is empty | `string` | - | | `[canMove]` | Second verification when transfer choice box | `function` | - | | `[oneWay]` | Display as single direction | `boolean` | `false` | | `(change)` | Callback function when the transfer between columns is complete | `(options: TransferChange) => void` | - | | `(searchChange)` | Callback function when search field is changed | `(options: TransferSearchChange) => void` | - | | `(selectChange)` | Callback function when selected items are changed | `(options: TransferSelectChange) => void` | - | --- --- title: tree-select subtitle: Tree Select type: Non-built-in widgets --- Tree select widget. **Note:** - Data source of `tree-select` must have keys of `title`、`key` ## How to use Non-built-in modules need to additionally register `withTreeSelectWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[enum]` | Data source | `SFSchemaEnumType[]` | - | | `[readOnly]` | Read only | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Asynchronous data source | `() => Observable` | - | | `[size]` | Size, equals to `nzSize` | `string` | `default` | | `[placeholder]` | Placeholder | `string` | - | | `[notFoundContent]` | Text to display when a column is empty | `string` | - | | `[allowClear]` | Whether show clear button | `boolean` | `false` | | `[clearValue]` | Default value when cleared | `any` | `undefined` | | `[dropdownMatchSelectWidth]` | Determine whether the dropdown menu and the select input are the same width | `boolean` | `true` | | `[dropdownStyle]` | Set the style of the dropdown menu | `object` | - | | `[dropdownClassName]` | Set className of the dropdown menu | `string` | - | | `[multiple]` | Support multiple select(set to true automatically when `checkable` is set) | `boolean` | `false` | | `[hideUnMatched]` | Hide unmatched nodes | `boolean` | `false` | | `[checkable]` | Whether to show checkbox on the treeNodes | `boolean` | `false` | | `[checkStrictly]` | Fully control node itself when it's checkable status(No relationship about check status between parent and child nodes) | `boolean` | `false` | | `[showIcon]` | Whether to show icon in front of TreeNode title, there is no default style | `boolean` | `false` | | `[showExpand]` | Show a expand icon before the treeNodes | `boolean` | `true` | | `[showLine]` | Shows a connecting line | `boolean` | `false` | | `[defaultExpandAll]` | Whether to expand all treeNodes by default | `boolean` | `false` | | `[displayWith]` | How to display the selected node value in input box | `(node: NzTreeNode) => string | undefined` | `(node: NzTreeNode) => node.title` | | `[expandedKeys]` | Expand specific tree nodes by default | `string[]` | - | | `[maxTagCount]` | Maximun number of tag | `number` | - | | `[maxTagPlaceholder]` | Placeholder for not showing tags | `TemplateRef<{ $implicit: NzTreeNode[] }>` | - | | `[treeTemplate]` | Custom Nodes | `TemplateRef<{ $implicit: NzTreeNode; origin: NzTreeNodeOptions }>` | - | | `[expandChange]` | Callback function for when a treeNode is expanded or collapsed | `(e: NzFormatEmitEvent) => Observable` | - | | `[virtualHeight]` | The height of virtual scroll | `string` | `-` | | `[virtualItemSize]` | The size of the items in the list, same as [cdk itemSize](https://material.angular.io/cdk/scrolling/api) | `number` | `28` | | `[virtualMaxBufferPx]` | The number of pixels worth of buffer to render for when rendering new items, same as [cdk maxBufferPx](https://material.angular.io/cdk/scrolling/api) | `number` | `500` | | `[virtualMinBufferPx]` | The minimum amount of buffer rendered beyond the viewport (in pixels),same as [cdk minBufferPx](https://material.angular.io/cdk/scrolling/api) | `number` | `28` | > Asynchronous data must set initial data (Using either `enum` or `asyncData`), otherwise, `expandChange` cannot be triggered. --- --- title: upload subtitle: Upload type: Non-built-in widgets --- Upload file widget by select or drag. ## How to use Non-built-in modules need to additionally register `withUploadWidget` in [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9). ## Note - **Must** set `resReName` to get correct data - `multiple` determine return array or one element - If `enum` or `asyncData` is set, it will be converted to `fileList` (`nzFileList`), and **must** initially guarantee a `response` property to indicate remote data and make sure `resReName` can be obtained correctly - Image preview: by default, it uses `nzModal` to show `url` or `thumbUrl` of file object ## API ### schema | Property | Description | Type | Default | |----------|-------------|------|---------| | `[readOnly]` | Read only | `boolean` | - | ### ui | Property | Description | Type | Default | |----------|-------------|------|---------| | `[asyncData]` | Asynchronous data source | `() => Observable` | - | | `[type]` | Upload type | `select,drag` | `select` | | `[text]` | Text of button | `string` | `点击上传` | | `[hint]` | Text of hint, it is valid during drag | `string` | `支持单个或批量,严禁上传公司数据或其他安全文件` | | `[resReName]` | Rename return parameter, support nested style `a.b.c`, the whole return body will be used if it is not set | `string` | - | | `[urlReName]` | Rename image preview URl return parameter, support nested style `a.b.c`, `url`, `thumbUrl` of file object will be used if it is not set | `string` | - | | `[action]` | Required attribute, upload URL | `string, ((file: UploadFile) => string, Observable)` | - | | `[accept]` | File types that can be accepted, see details from [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept) | `string, string[]` | - | | `[limit]` | limit number of single upload when `multiple` is set, 0 means unlimited | `number` | `0` | | `[filter]` | Custom filter when choosed file | `UploadFilter[]` | - | | `[fileList]` | File list | `UploadFile[]` | - | | `[fileSize]` | Limit file size, unit: KB; `0` means unlimited | `number` | `0` | | `[fileType]` | Limit file type, e.g. `image/png,image/jpeg,image/gif,image/bmp` | `string` | - | | `[headers]` | Set request header of upload | `Object, (file: UploadFile) => {} | Observable<{}>` | - | | `[listType]` | Built-in style of upload list | `text,picture,picture-card` | `text` | | `[showUploadList]` | Whether show upload list, can set as an object, used to set `showPreviewIcon` and `showRemoveIcon` separately | `boolean` | `true` | | `[multiple]` | Whether support multiple file selection. IE10+ supported. You can select multiple files with holding `ctrl` | `boolean` | `false` | | `[name]` | File name when sending to backend | `string` | `file` | | `[data]` | Uploading params or function which can return uploading params | `Object, (file: UploadFile) => {} | Observable<{}>` | - | | `[withCredentials]` | Whether set cookie during upload | `boolean` | `false` | | `[directory]` | Support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | `boolean` | `false` | | `[openFileDialogOnClick]` | Click to open file dialog | `boolean` | `true` | | `[beforeUpload]` | Hook function which will be executed before uploading, parameter is file to be uploaded, stop uploading when `false` is returned | `(file: UploadFile, fileList: UploadFile[]) => boolean|Observable` | - | | `[customRequest]` | Override the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | `(item: UploadXHRArgs) => Subscription` | - | | `[remove]` | Callback function when remove is clicked, won't remove when `false` is returned | `(file: UploadFile) => boolean|Observable` | - | | `[preview]` | Callback function when file link or preview icon is clicked | `(file: UploadFile) => void` | - | | `[previewFile]` | Customize preview file logic | `(file: UploadFile) => Observable` | - | | `[download]` | Callback function when clicking the method to download the file, jump to a new tab with url of file | `(file: UploadFile) => void` | - | | `[transformFile]` | Transform file before file conversion. Support to return Observable object | `(file: UploadFile) => UploadTransformFileType` | - | | `[change]` | Callback function when uploading state is changing | `(args: UploadChangeParam) => void` | - | --- --- type: Basic title: xlsx order: 6 subtitle: Excel cols: 1 module: import { XlsxModule } from '@delon/abc/xlsx'; --- An Excel file operation based on [sheetjs](http://sheetjs.com/). > Note: You can also use [js-xlsx](https://github.com/protobi/js-xlsx) to be another Fork by sheetjs library that provides additional options including: images, styles, and more. Finally, use `callback` option to render your excel. ## Dependencies The sheetjs script file takes the form of lazy loading,you can change the default CDN path via [Global Configuration](/docs/global-config). By default: `https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js`. **Use local path** ```bash yarn add -D xlsx ``` ```json // angular.json { "glob": "**/{xlsx.full.min,cpexcel}.js", "input": "./node_modules/xlsx/dist", "output": "assets/xlsx/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { xlsx: { url: '/assets/xlsx/xlsx.full.min.js', modules: [`/assets/xlsx/cpexcel.js`] } }; ``` ## API ### LazyService | Property | Description | Type | Default | |----------|-------------|------|---------| | `import(fileOrUrl: File | string)` | Import Excel, return JSON | `Promise<{ [key: string]: any[][] }>` | - | | `export(options: XlsxExportOptions)` | Export Excel | `Promise` | - | | `numberToSchema(val: number)` | Numeric to schema name | `string` | - | ### XlsxExportOptions | Property | Description | Type | Default | |----------|-------------|------|---------| | `[sheets]` | Data source | `{ [sheet: string]: WorkSheet } | XlsxExportSheet[]` | - | | `[filename]` | file name of excel | `string` | `export.xlsx` | | `[opts]` | Excel options, see [WritingOptions](https://github.com/SheetJS/sheetjs/blob/master/docbits/81_writeopts.md) | `WritingOptions` | - | | `[callback]` | Trigger before saving | `(wb: WorkBook) => void` | - | ### [xlsx] xlsx directive. ```html
Export
``` --- --- order: 1 title: yn subtitle: Badge type: Pipe --- `yn` Make boolean as badge. ```html Output: ``` --- --- type: Basic title: zip order: 7 subtitle: Zip cols: 1 --- A Zip file operation based on [jszip](http://stuk.github.io/jszip/). ## Dependencies The jszip script file takes the form of lazy loading,you can change the default CDN path via [Global Configuration](/docs/global-config). By default: `https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js`. **Use local path** ```json // angular.json { "glob": "**/jszip.min.js", "input": "./node_modules/jszip/dist", "output": "assets/jszip/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { zip: { url: '/assets/jszip/jszip.min.js' } }; ``` ## API ### ZipService Property | Description | Type | Default ----|------|-----|------ `read(fileOrUrl: File | string, options?: JSZip.JSZipLoadOptions)` | Decompression | `Promise` | - `create()` | Create a Zip instance for creating a compressed file | `Promise` | - `pushUrl(zip: JSZip, path: string, url: string)` | Download the URL resource and write it to zip | `Promise` | - `save(zip: JSZip, options?: ZipWriteOptions)` | Save | `Promise` | - ---