Contact Us

Using ASP.NET Zero with DevExtreme (Angular)

Using ASP.NET Zero with DevExtreme (Angular)

Using ASP.NET Zero with DevExtreme Angular Part 4

The ASP.NET Zero framework provides a robust foundation for building web applications with Angular and ASP.NET Core. In this blog post series, we explore the integration of ASP.NET Zero with DevExtreme Angular, a powerful UI component library.

In the last part of the series, we focus on implementing the editing functionality in the phone book application. We start by adding a EditPerson method to the server-side application service, IPersonAppService, and define the necessary DTO (Data Transfer Object) to transfer person information.

With these steps completed, we enhance the phone book application by enabling users to edit person details, improving the overall user experience and data management capabilities.

Editing a Person

In this section, we will focus on adding the ability to edit a person in the phone book application. We will make changes to the application service, generate the service proxies, and update the component script accordingly.

Application Service

To enable editing functionality, we need to add a EditPerson method to the server-side IPersonAppService. We'll define the method as follows:

Task EditPerson(EditPersonInput input);

Additionally, we need to create a DTO (Data Transfer Object) to transfer the person's Id, Name, Surname, and EmailAddress. Let's define the EditPersonInput DTO:

public class EditPersonInput: EntityDto
{
    public string Name {get;set;}
    public string Surname {get;set;}
    public string EmailAddress {get;set;}
}

The EntityDto is a convenient shortcut provided by ABP (ASP.NET Boilerplate) when we only need to transfer an Id value.

The implementation of the EditPerson method in the PersonAppService is straightforward. We retrieve the person entity based on the provided Id and update its properties with the values from the input DTO. Finally, we save the changes using the _personRepository. Here's the implementation:

public async Task EditPerson(EditPersonInput input)
{
    var person = await _personRepository.GetAsync(input.Id);
    if (input.Name != null)
    {
        person.Name = input.Name;
    }

    if (input.Surname != null)
    {
        person.Surname = input.Surname;
    }

    if (input.EmailAddress != null)
    {
        person.EmailAddress = input.EmailAddress;
    }

    await _personRepository.UpdateAsync(person);
}

Service Proxy Generation

Since we have made changes to the server-side services, it is important to regenerate the client-side service proxies using NSwag. Ensure that the server-side application is running, and then run the userefresh.bat script, as explained in the previous parts of this series.

Component Script

To incorporate the update functionality into the phone book component, we need to make changes to the phonebook.component.ts file.

First, import the necessary dependencies and update the PhoneBookComponent class definition as follows:

import {Component, Injector} from '@angular/core';
import {AppComponentBase} from '@shared/common/app-component-base';
import {appModuleAnimation} from '@shared/animations/routerTransition';
import {EditPersonInput, PersonServiceProxy} from "@shared/service-proxies/service-proxies";
import CustomStore from "@node_modules/devextreme/data/custom_store";

@Component({
    templateUrl: './phonebook.component.html',
    animations: [appModuleAnimation()]
})

export class PhoneBookComponent extends AppComponentBase {

    dataSource: any;
    refreshMode: string;

    constructor(
        injector: Injector,
        private _personService: PersonServiceProxy
    ) {
        super(injector);
        this.getData();
    }

    getData() {
        this.refreshMode = "full";

        this.dataSource = new CustomStore({
            key: "id",
            load: (loadOptions) => {
                return this._personService.getPeople("").toPromise();
            },
            insert: (values) => {
                return this._personService.createPerson(values).toPromise()
            },
            update: (key, values) => {
                return this._personService.editPerson(new EditPersonInput({
                    id: key,
                    name: values.name,
                    emailAddress: values.emailAddress,
                    surname: values.surname
                })).toPromise();
            },
            remove: (key) => {
                return this._personService.deletePerson(key).toPromise();
            }
        });
    }
}

Go to phonebook.component.html and add dxo-editing tag with allowUpdating true option:

<div [@routerTransition]>
    <div class="content d-flex flex-column flex-column-fluid">
        <sub-header [title]="'PhoneBook' | localize">
        </sub-header>

        <div [class]="containerClass">
            <div class="card card-custom">
                <div class="card-body">
                    <dx-data-grid
                        id="phonebookgrid"
                        [dataSource]="dataSource"
                        [repaintChangesOnly]="true"
                        [showBorders]="true">

                        <dxo-scrolling mode="virtual"></dxo-scrolling>
                        <dxo-editing
                            mode="row"
                            [refreshMode]="refreshMode"
                            [allowAdding]="true"
                            [allowUpdating]="true"
                            [allowDeleting]="true">
                        </dxo-editing>

                        <dxi-column dataField="name" caption="{{'Name' | localize}}"></dxi-column>
                        <dxi-column dataField="surname" caption="{{'Surname' | localize}}"></dxi-column>
                        <dxi-column dataField="emailAddress" caption="{{'EmailAddress' | localize}}"></dxi-column>
                    </dx-data-grid>
                </div>
            </div>
        </div>
    </div>
</div>

It first shows a confirmation message when we click the edit button:

Confirmation message Confirmation message

Multi Tenancy

We have built a fully functional application until here. Now, we will see how to convert it to a multi-tenant application easily. Logout from the application before any change.

Enable Multi Tenancy

We disabled multi-tenancy at the beginning of this document. Now, re-enabling it in PhoneBookDemoConsts class:

public const bool MultiTenancyEnabled = true;

Make Entities Multi Tenant

In a multi-tenant application, a tenant's entities should be isolated by other tenants. For this example project, every tenant should have own phone book with isolated people and phone numbers.

When we implement IMustHaveTenant interface, ABP automatically filters data based on current Tenant, while retrieving entities from database. So, we should declare that Person entity must have a tenant using IMustHaveTenant interface:

public class Person : FullAuditedEntity, IMustHaveTenant
{
    public virtual int TenantId { get; set; }

    //...other properties
}

We may want to add IMustHaveTenant interface to also Phone entity. This is needed if we directly use phone repository to get phones.In this sample project, it's not needed.

Since entities have changed, we should create a new database migration:

Add-Migration "Implemented_IMustHaveTenant_For_Person"

This command creates a new code-first database migration. The migration class adds an annotation this is needed for automatic filtering. We don't have to know what it is since it's done automatically. It also adds a TenantId column to PbPersons table as shown below:

migrationBuilder.AddColumn<int>(name: "TenantId",table: "PbPersons",nullable: false,defaultValue: 1);

I added defaultValue as 1 to AddColumn options. Thus, current people are automatically assigned to default tenant (default tenant's id is always 1).

Now, we can update the database again:

Update-Database

Running the Application

It's finished! We can test the application. Run the project, login as the host admin (click Change link and clear tenancy name) shown below:

Login as host

Default admin password is 123qwe. After login, we see the tenant list which only contains a default tenant. We can create a new tenant:

Creating tenant

I created a new tenant named trio. Now, tenant list has two tenants:

Tenant management page

I can logout and login as trio tenant admin (Change current tenant to trio):

Login as tenant admin

After login, we see that phone book is empty:

Empty phonebook of new tenant

It's empty because trio tenant has a completely isolated people list. You can add people here, logout and login as different tenants (you can login as default tenant for example). You will see that each tenant has an isolated phone book and can not see other's people.

Conclusion

In this blog post series, we have explored the integration of ASP.NET Zero with DevExtreme Angular, showcasing the implementation of various features in the phone book application. We started by adding the necessary server-side methods and DTOs for editing a person's information. Then, we regenerated the client-side service proxies to ensure seamless communication between the Angular application and the ASP.NET Zero backend.

ASP.NET Zero empowers developers to build sophisticated web applications by providing a solid architectural foundation and flexibility for customization. The integration with DevExtreme Angular further enhances the user interface and improves the overall user experience.

We hope this blog post series has been insightful and helpful in understanding the integration of ASP.NET Zero with DevExtreme Angular. By leveraging these powerful frameworks, you can create feature-rich applications with ease.

Thank you for joining us on this journey, and we wish you success in building your own applications using ASP.NET Zero and DevExtreme Angular!

Source Code

You should purchase ASP.NET Zero in order to get source code. After purchasing, you can get the sample project from private Github repository: https://github.com/aspnetzero/aspnet-zero-samples

ASP.NET Zero Awarded As One of the Best 5 Application Development Tools by Get App Logo Learn More