Skip to content

RomanTsukanov/devextreme-angular

 
 

Repository files navigation

CircleCI npm version

If you are looking for the v18.1 branch, please follow this link: https://github.com/DevExpress/devextreme-angular/tree/18.1.

Angular UI and Visualization Components Based on DevExtreme Widgets

This project allows you to use DevExtreme Widgets in Angular applications.

Getting Started

You have the following options to start:

Quick Start

You can use the Angular CLI to create a DevExtreme Angular application that includes several sample views and a navigation menu:

ng new app-name --style=scss
cd app-name
npm i devextreme-angular
ng g devextreme-angular:add-app-template
ng serve

You can also use the DevExtreme CLI's new angular-app command to do this:

npx devextreme-cli new angular-app app-name
cd app-name
npm run start

See the DevExtreme Angular Template README for more information.

Prerequisites

Starting with v18.1, DevExtreme-Angular requires Angular version 5 or later

Node.js and npm are required and essential to Angular development.

Gulp is required to build the project and run tests.

Adding DevExteme to an Existing Angular Application

You can use the Angular CLI or the DevExtreme CLI to add DevExtreme to an existing application:

  • Angular CLI

      ng add devextreme-angular
    
  • DevExtreme CLI

      npx devextreme-cli add devextreme-angular
    

Alternatively, you can follow the steps below to add DevExtreme manually.

Install DevExtreme

Install the devextreme and devextreme-angular npm packages:

npm install --save devextreme devextreme-angular

Additional Configuration

The further configuration steps depend on which build tool, bundler or module loader you are using. Please choose the one you need:

Import DevExtreme Modules

Go to your main .ts file (usually src/app.module.ts) and import the required modules to your app:

...
import { DxButtonModule } from 'devextreme-angular';

@NgModule({
    ...
    imports: [
        ...
        DxButtonModule,
        ...
    ]
})
export class AppModule {}

Note, you can import the DevExtremeModule module to include all the DevExtreme components at once, but it might affect the final bundle size and startup time. Check the bundle optimization section for more info.

Use DevExtreme Components

Now you can use a DevExteme component in your application:

@Component({
    selector: 'my-app',
    template: '<dx-button text="Press me" (onClick)="helloWorld()"></dx-button>'
})
export class AppComponent {
    helloWorld() {
        alert('Hello world!');
    }
}

Create a new Angular Application

Depending on your requirements you can choose one of the following ways to start:

Server-side Rendering

Angular Universal provides server-side rendering that significantly reduces the application's loading time. You can use DevExtreme widgets in Angular Universal applications in the same manner as in other Angular apps.

Note: DevExtreme-angular does not support dynamic theme loading in the Server-side rendering mode due to technical restrictions. You can only use a single static theme by linking the corresponding CSS file.

You can create a new Angular Universal app and add DevExtreme widgets to it, or add the Universal module to an existing Angular application with DevExtreme. Use the following command to add the Universal module to an existing app:

ng generate universal my-app

See Angular 5.1 & More Now Available for more information.

This example demonstrates the use of DevExtreme Angular controls in an Angular Universal application.

Cache Requests on the Server

DevExtreme-angular supports caching requests on the server in the server-side rendering mode. This avoids repeatedly requesting data from the browser and renders widgets using data that is initially applied when the page is loaded for the first time.

To enable caching, import the DxServerTransferStateModule module in the module's .ts file (usually src/app.module.ts):

import { DxServerTransferStateModule } from 'devextreme-angular';

@NgModule({
    ...
    imports: [
        ...
        DxServerTransferStateModule,
        ...
    ]
})
export class AppModule {}

and import the the ServerTransferStateModule module in the server module's .ts file (usually src/app.server.module.ts):

import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';

@NgModule({
    imports: [
        AppModule,
        ServerModule,
        ServerTransferStateModule,
        ModuleMapLoaderModule
    ],
    bootstrap: [AppComponent],
})

Also, ensure that the application module is bootstrapped when the document has been loaded (the main.ts file should contain the code below). Otherwise, caching does not work correctly.

document.addEventListener('DOMContentLoaded', () => {
    platformBrowserDynamic().bootstrapModule(AppModule)
        .catch(err => console.log(err));
});

Running the Local Examples

# clone our repo
# --depth 1 removes all but one .git commit history
git clone --depth 1 https://github.com/DevExpress/devextreme-angular.git

# change directory to our repo
cd devextreme-angular

# install the repo with npm
npm install

# start the web-server
npm start

Navigate to http://127.0.0.1:8875/examples/ in the opened browser window. Explore the examples folder of this repository for the examples source code.

Include jQuery integration

Starting with version 17.2, DevExtreme doesn't depend on jQuery. It means that our widgets work without jQuery elements. If you need to use jQuery, you can manually install the jquery npm package and include the jQuery integration as described below:

npm install --save jquery
import 'devextreme/integration/jquery';

Usage Samples

Static String Option Value

To specify a string widget's option statically (the text option of dxButton):

<dx-button text="Simple button"></dx-button>

Static Non-string Option Value

To specify a non-string widget's option statically (the disabled option of dxButton):

<dx-button [disabled]="false"></dx-button>

Event Handling

To bind the dxButton’s click event:

<dx-button (onClick)="handler()"></dx-button>

Callback Functions

To specify a widget's option using a callback function (the layer.customize option of dxVectorMap):

<dx-vector-map>
    ...
    <dxi-layer
        ...
        [customize]="customizeLayers">
    </dxi-layer>
</dx-vector-map>

Note that callback functions are executed outside the context of the component, but if the context is important, you can explicitly bind it to the callback function in the constructor.

constructor() {
    this.customizeLayers = this.customizeLayers.bind(this);
}

customizeLayers(elements) {
    let country = this.myCountry;
    ...
}

One-way Option Binding

If we want changes to the value of ‘bindingProperty’ of the host component to propagate to the value of the dxTextBox widget, a one-way binding approach is used:

<dx-text-box [value]="bindingProperty"></dx-text-box>

Two-way Option Binding

In addition to the one-way binding, we can also perform two-way binding, which propagates changes from the bindingProperty to the widget or vice versa from the widget to the bindingProperty:

<dx-text-box [(value)]="bindingProperty"></dx-text-box>

Custom Templates

Templates allow you to customize widget elements. In the following code, an itemTemplate called listItem and a groupTemplate called listGroup customize items and groups in the List widget. Inside the templates, the itemData and groupData variables expose item and group data objects; the itemIndex variable gives access to the item index.

<dx-list
    [items]="groupedItems"
    [grouped]="true"
    itemTemplate="listItem"
    groupTemplate="listGroup">
    <div *dxTemplate="let itemData of 'listItem'; let itemIndex = index">
        {{itemIndex}} - {{itemData.itemProperty}}
    </div>
    <div *dxTemplate="let groupData of 'listGroup'">
        {{groupData.groupProperty}}
    </div>
</dx-list>

NOTE: The dxTemplate attribute directive cannot be used on custom markup elements.

Refer to the common Custom Templates article for more information.

Angular has a built-in template directive. This causes an error when you try to specify an eponymous property on a configuration component (for instance, on dxo-master-detail). In this case, use the following syntax:

<dxo-master-detail [template]="'masterDetail'"></dxo-master-detail>

Widgets like Resizable, ScrollView, ValidationGroup, Button, and Darwer allow you to declare their content directly in the markup. The following is an example with ScrollView:

<dx-scroll-view>
    <div>Some scrollable content</div>
</dx-scroll-view>

External Templates

External templates are created using the ng-template element. The following code replicates the example above, but here the itemTemplate is the external template. The groupTemplate is omitted.

The ngTemplateOutlet directive uses a template reference variable to reference the external template. The ngTemplateOutletContext directive specifies variables that are accessible in the template.

<dx-list [items]="items" itemTemplate="listItem">
    <div *dxTemplate="let itemData of 'listItem'; let itemIndex = index">
        <ng-template 
            [ngTemplateOutlet]="customItemTemplate" 
            [ngTemplateOutletContext]="{ itemData: itemData, itemIndex: itemIndex }">
        </ng-template>
    </div>
</dx-list>

<ng-template #customItemTemplate let-data="itemData" let-index="itemIndex">
    {{index}} - {{data.itemProperty}}
</ng-template>

In the previous code, the external template is used in the same component in which it is declared. The following code illustrates the case when the external template is declared in another component. The ngTemplateOutlet directive should be set to an input property in this case:

<!-- parent.component.html -->
<ng-template #customItemTemplate let-data="itemData" let-index="itemIndex">
    {{index}} - {{data.itemProperty}}
</ng-template>

<custom-list
    [externalItemTemplate]="customItemTemplate"> 
</custom-list>
<!-- custom-list.component.html -->
<dx-list ...
    itemTemplate="listItem">
    <div *dxTemplate="let itemData of 'listItem'; let itemIndex = index">
        <ng-template 
            [ngTemplateOutlet]="externalItemTemplate" 
            [ngTemplateOutletContext]="{ itemData: itemData, itemIndex: itemIndex }">
        </ng-template>
    </div>
</dx-list>
// custom-list.component.ts
import { Component, Input, TemplateRef } from '@angular/core';
@Component({
    selector: 'custom-list',
    templateUrl: './custom-list.component.html'
})
export class CustomListComponent {
    @Input() externalItemTemplate: TemplateRef<any>
    // ...
}

Data Layer

The DevExtreme framework includes a data layer, which is a set of complementary components that enable you to read and write data. For more details please refer to the documentation on the official website.

DevExtreme Utils

The DevExtreme provides utils that can be used in different application parts such as widgets and data. For more details please refer to the documentation on the official website.

Components with Transcluded Content

In addition to using dxTemplate, it is possible to put the content of the following widgets directly into the markup: dxResizable, dxScrollView, dxValidationGroup. For instance, we can set the content for the dxScrollView widget as shown below:

<dx-scroll-view>
    <div>Some scrollable content</div>
</dx-scroll-view>

Angular Forms

The DevExtreme Angular editors support the 'ngModel' binding as well as the 'formControlName' directive, which are necessary for the Angular forms features.

<form [formGroup]="form">
    <dx-text-box
        name="email"
        [(ngModel)]="email"
        [isValid]="emailControl.valid || emailControl.pristine"
        [validationError]="{ message: 'Email is invalid'}">
    </dx-text-box>
</form>
@Component({
   selector: 'my-app',
   templateUrl: 'app/app.component.html'
})
export class AppComponent implements OnInit {
   email: string;
   emailControl: AbstractControl;
   form: FormGroup;
   ngOnInit() {
       this.form = new FormGroup({
           email: new FormControl('', Validators.compose([Validators.required, Validators.email]))
       });
       this.emailControl = this.form.controls['email'];
   }
}

Using DevExtreme Validation Features

You can use the built-in validators, validation summary and other DevExtreme validation features with Angular DevExtreme editors.

<dx-validation-group>

    <dx-text-box [(value)]="email">
        <dx-validator [validationRules]="validationRules.email"></dx-validator>
    </dx-text-box>
    <dx-text-box [(value)]="password" mode="password">
        <dx-validator [validationRules]="validationRules.password"></dx-validator>
    </dx-text-box>

    <dx-validation-summary></dx-validation-summary>

    <dx-button (onClick)="validate($event)" text="Submit"></dx-button>

</dx-validation-group>
@Component({
    selector: 'my-app',
    templateUrl: 'app/app.component.html'
})
export class AppComponent {
    email: string;
    password: string;
    validationRules = {
        email: [
            { type: 'required', message: 'Email is required.' },
            { type: 'email', message: 'Email is invalid.' }
        ],
        password: [
            { type: 'required', message: 'Email is required.' }
        ]
    };
    validate(params) {
        let result = params.validationGroup.validate();
        if (result.isValid) {
            // form data is valid
            // params.validationGroup.reset();
        }
    }
}

Configuration Components

You can use dxo- ('o' is a contraction of 'option') prefixed components to configure complex nested options for widgets. The following example demonstrates how to configure the tooltip option of the dxTreeMap widget:

<dx-tree-map [dataSource]="treeData">
    <dxo-tooltip [enabled]="showTooltip" format="thousands"></dxo-tooltip>
</dx-tree-map>

<dx-button text="Toggle tooltip" (onClick)="toggleTooltip()"></dx-button>
@Component({
    selector: 'my-app',
    templateUrl: 'app/app.component.html'
})
export class AppComponent {
    treeData = ...;
    showTooltip = false;
    toggleTooltip() {
        this.showTooltip = !this.showTooltip;
    }
}

You can also use dxi- ('i' is a contraction of 'item') prefixed components to configure complex collection options for widgets. The following example demonstrates how to configure the columns option of the dxDataGrid widget:

<dx-data-grid [dataSource]="data">
    <dxi-column dataField="firstName" caption="First Name"></dxi-column>
    <dxi-column dataField="lastName" caption="Last Name" [visible]="showLastName"></dxi-column>
</dx-data-grid>

<dx-button text="Toggle the 'Last Name' column" (onClick)="toggleLastName()"></dx-button>
@Component({
    selector: 'my-app',
    templateUrl: 'app/app.component.html'
})
export class AppComponent {
    data = ...;
    showLastName = false;
    toggleLastName() {
        this.showLastName = !this.showLastName;
    }
}

To configure options that can accept a configuration object or an array of configuration objects, use dxi- prefixed components. The following example demonstrates how to configure the valueAxis option of the dxChart widget:

<dx-chart [dataSource]="data">
    <dxi-series valueField="value" argumentField="argument"></dxi-series>
    <dxi-value-axis>
        <dxo-label format="millions"></dxo-label>
    </dxi-value-axis>
</dx-chart>
@Component({
    selector: 'my-app',
    templateUrl: 'app/app.component.html'
})
export class AppComponent {
    data = ...;
}

It is possible to specify an item template inside the dxi- prefixed components and use Angular structural directives such as ngFor. Note that the available item properties are described in the Default Item Template section of a corresponding widget documentation reference.

<dx-list>
    <dxi-item>
        <h1>Items available</h1>
    </dxi-item>
    <dxi-item *ngFor="let item of listItems" [badge]="item.badge">
        <h2>{{item.text}}</h2>
    </dxi-item>
</dx-list>
@Component({
    selector: 'my-app',
    templateUrl: 'app/app.component.html'
})
export class AppComponent {
    listItems = [
        {
            text: 'Cars',
            badge: '12'
        },
        {
            text: 'Bikes',
            badge: '5'
        }
    ];
}

If your item template contains some nested components, declare it using the parameterless dxTemplate structural directive as follows:

<dx-list>
    <dxi-item>
        <div *dxTemplate>
            <dx-button text="I'm a nested child component"></dx-button>
        </div>
    </dxi-item>
</dx-list>

Angular has a built-in template directive. To define the template property of the configuration component (for example, dxo-master-detail), use the following code:

<dxo-master-detail [template]="'masterDetail'"></dxo-master-detail>

Note that some options with an object type are not implemented as nested components - for example, editorOptions of dxDataGrid, editorOptions of dxForm, the widget option of dxToolbar.

Accessing a DevExtreme Widget Instance

You can access a DevExtreme widget instance using the @ViewChild or @ViewChildren decorator (depending on whether you are getting just one or several instances of one widget) and the component's 'instance' property. Both decorators accept a component name or a template reference variable. In the example below, the refresh method of the dxDataGrid is called:

import { Component, ViewChild } from '@angular/core';
import { DxDataGridComponent } from "devextreme-angular";

@Component({
    selector: 'my-app',
    template: `
        <dx-data-grid #targetDataGrid [dataSource]="dataSource"></dx-data-grid>
        <dx-button text="Refresh data" (onClick)="refresh()"></dx-button>
    `
})
export class AppComponent implements OnChanges {
    @ViewChild(DxDataGridComponent) dataGrid:DxDataGridComponent
    // or
    // @ViewChild("targetDataGrid") dataGrid: DxDataGridComponent
    refresh() {
        this.dataGrid.instance.refresh();
    }
}

To access a DevExtreme widget instance in markup, you can use the same template reference variables. The following example demonstrates how you can get a dxSelectBox value in the template.

<dx-select-box #selectbox [items]="items"></dx-select-box>
{{selectbox.value}}

Angular Change Detection

By default, in Angular, options changes are checked on each user action. If you bind a widget option to this function, it should return a static object. Otherwise, Angular considers that the option is constantly changed after each user action. Alternatively, you can change the default behavior and set the ChangeDetectionStrategy component option to "OnPush".

import {Component, ChangeDetectionStrategy} from '@angular/core';

@Component({
    selector: 'my-app',
    ....
    changeDetection: ChangeDetectionStrategy.OnPush
})

Demos

API Reference

DevExtreme Angular components mirror DevExtreme JavaScript API but use Angular syntax for specifying widget options, subscribing to events and custom templates declaration.

Bundle Size Optimization

Bundlers with Tree Shaking Support

Tree shaking can greatly reduce the downloaded size of the application by removing unused portions of both source and library code. There are a number of bundlers with tree shaking support, such as Webpack 2, Rollup, SystemJS Bundler, etc. Due to specifics of the tree shaking algorithm, your project typescript sources should be prepared accordingly to make tree shaking available. This preparations are performed by the Angular Compiler. You can follow one of the existing guides to configure tree shaking with your bundler (Webpack 2, Angular CLI, Rollup).

To make it work with DevExtreme Angular package, you just need to import only the modules required in your application, not the whole DevExtremeModule. For instance, you can import only DxButtonModule as follows:

import { DxButtonModule } from 'devextreme-angular';

Note, AOT Compilation also decreases a bundle size by precompiling your HTML templates. So, the markup and the template compiler are not included into the final bundle.

Bundlers without Tree Shaking Support

If you are not going to configure tree shaking, you can optimize your bundle size by using imports from specific modules, not from the main 'devextreme-angular' module. You can do this as follows:

import { DxButtonModule } from 'devextreme-angular/ui/button';

License

Familiarize yourself with the DevExtreme License. Free trial is available!

DevExtreme Angular components are released as a MIT-licensed (free and open-source) add-on to DevExtreme.

Support & Feedback

Version history

DevExtreme Angular
v18.1+
v18.2+
v5.0 - v7.0+
v17.1+
v17.2+
v2.4 - v5.0+

About

DevExtreme Angular UI and visualization components

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 69.3%
  • JavaScript 25.5%
  • Scilab 5.2%