TU Tran

Technologies should serve for business purpose.

NAVIGATION - SEARCH

[TinyERP]Add new Role

Introduction

The first thing I would like to share with you is "We do not learn technologies, we learn how to use technologies for our business".

Note: In this code, we use Relase Candidate version of Angular 2

In this part, we will to through the list of steps to know how to use my code for implementing the add Role page as photo below:

 

How to Get the Code

Checkout the code at https://github.com/techcoaching/TinyERP.git.

Analyze the Page

As a matter of habit, I analyze the page before implementing, this will help us:

  • List out the steps we need to do to complete the page.
  • Figure out the missing information, so we can ask for help immediately.
  • Think about the flow of logic from the client to server side, from UI to repository. So the written code will be nicer. For this, many of my co-workers try to write the code first, and debug it later. When something goes wrong, it changes our behavior, and we try to change the code to make it work. This may break the logic flow and the code does not follow the convention. The architecture was used in the app. This will raise some new potential issues in the future and the code is hard for maintenance.

After analyzing the page, we figure out the list of things that need to be completed as below:

On Client

  • Register route for "Add Role" page (Component in angular2)
  • Create component files (html file for UI, ts file for logic handling and ts for view model of that component).
  • Implement the UI of "Add Role" component
  • Implement logic of "Add Role" component.
  • Implement service for this page (service will make the call to REST API for creating/ updating data on server).

On API

  • Add new method into RolesController for handling create request related to Role
  • Add new method into Role service and Role repository for creating Role

Implement the Client

In this section, <root> folder is the folder of client code (the path to "client" folder).

  1. Register route for "Add Role" page (Component in angular2):

    Go to "<root>/app/modules/secutiry/_share/config" and update addRoutes method to:

    import {IModule, Module, MenuItem} from "../../../../common/models/layout";
    import {Roles} from "../../role/roles";
    import {AddRole} from "../../role/addRole";
    import {AuthenticationMode} from "../../../../common/enum";
    import route from "./route";
    let module: IModule = createModule();
    export default module;
    function createModule() {
        let module = new Module("app/modules/secutiry", "secutiry");
        module.menus.push(
            new MenuItem(
                "Security", route.role.roles.name, "fa fa-user-md",
                new MenuItem("Roles", route.role.roles.name, ""),
            )
        );
        module.addRoutes([
            { path: route.role.roles.path, name: route.role.roles.name, component: Roles, 
              data: { authentication: AuthenticationMode.Require }, useAsDefault: true },
            { path: route.role.addRole.path, name: route.role.addRole.name, component: AddRole, 
              data: { authentication: AuthenticationMode.Require } }
        ]);
        return module;
    }
    The content of route.ts will be:
    let route = {
        role: {
            roles: { name: "Roles", path: "/roles" },
            addRole: { name: "Add Role", path: "/addRole" }
        }
    };
    export default route;
  2. Create component files (HTML file for UI, ts file for logic handling and ts for view model of that component).

  3. Implement the UI of "Add or Edit Role" component:
    <page>
        <page-header>
            {{i18n.security.addOrUpdateRole.title}}
        </page-header>
        <page-content>
            <form-default>
                <form-text-input [placeHolderText]=i18n.security.addOrUpdateRole.inputName 
                  [labelText]=i18n.security.addOrUpdateRole.name
    
                    [validation]="['security.addOrUpdateRole.validation.nameIsRequire',
                     'security.addOrUpdateRole.validation.keyAlreadyExisted']"
                    [(model)]=model.name>
                </form-text-input>
                <form-textarea [placeHolderText]=i18n.security.addOrUpdateRole.inputDesc 
                 [labelText]=i18n.security.addOrUpdateRole.desc [(model)]=model.description>
                </form-textarea>
    
                <form-permission-select [(values)]=model.permissions 
                 [placeHolderText]=i18n.security.addOrUpdateRole.inputPermissions 
                 [labelText]=i18n.security.addOrUpdateRole.permission
    
                    [(model)]=model.permissions>
                </form-permission-select>
    
                <form-footer>
                    <button id="save" 
                    (click)="onSaveClicked($event)" type="button" 
                     class="btn btn-primary">{{i18n.common.form.save}}</button>
                    <button id="cancel" 
                    (click)="onCancelClicked($event)" type="button" 
                     class="btn btn-default">{{i18n.common.form.cancel}}</button>
                </form-footer>
            </form-default>
        </page-content>
    </page>
    The output to browser as photo below:

    In this file:

    • We did not use traditional HTML for creating the UI of the form, there are a number of directives for this purpose, such as: form, form-textarea, ....
    • The page was structured as:
      • page-header: This contains the header text of the page
      • page-content: This is the main content region of the page
  4. Implement logic of "Add Role" component.
    import {BasePage} from "../../../common/models/ui";
    import {Router, RouteParams} from "angular2/router";
    import {Component} from "angular2/core";
    import {AddRoleModel} from "./addRoleModel";
    import {Page, SelectPermission, Form, FormTextInput, FormFooter, 
            FormTextArea, FormPermissionSelect} from "../../../common/directive";
    import {ValidationDirective} from "../../../common/directive";
    import roleService from "../_share/services/roleService";
    import {FormMode} from "../../../common/enum";
    import route from "../_share/config/route";
    @Component({
        templateUrl: "app/modules/security/role/addRole.html",
        directives: [Page, Form, FormTextInput, FormFooter, FormTextArea, FormPermissionSelect]
    })
    export class AddRole extends BasePage {
        public model: AddRoleModel = new AddRoleModel();
        private router: Router;
        constructor(router: Router, routeParams: RouteParams) {
            super();
            let self: AddRole = this;
            self.router = router;
        }
        public onSaveClicked(event: any): void {
            let self: AddRole = this;
            roleService.create(this.model).then(function () {
                self.router.navigate([route.role.roles.name]);
            });
        }
        public onCancelClicked(event: any): void {
            let self: AddRole = this;
            self.router.navigate([route.role.roles.name]);
        }
    }


    In this file, we:

    • Get the id of selected role by routeParams.get("id")
    • Navigate between page using router.navigate method
  5. Implement service for this page (service will make the call to REST API for creating data on server).
    import configHelper from "../../../../common/helpers/configHelper";
    import {Promise} from "../../../../common/models/promise";
    let roleServices = {
        getRoles: getRoles,
        create: create
    };
    export default roleServices;
    function getRoles(): Promise {
        let connector = window.ioc.resolve("IConnector");
        let url = String.format("{0}roles", configHelper.getAppConfig().api.baseUrl);
        return connector.get(url);
    }
    
    function create(role: any): Promise {
        let connector = window.ioc.resolve("IConnector");
        let url = String.format("{0}roles", configHelper.getAppConfig().api.baseUrl);
        return connector.post(url, role);
    }


Implement API

  1. Add new method into RolesController for handling create request related to Role.
  2. Add new method into IRoleService and RoleService for creating Role.
    • IRoleService.cs
    namespace App.Service.Security
    {
        public interface IRoleService
        {
            System.Collections.Generic.IList<RoleListItemSummary> GetRoles();
            CreateRoleResponse Create(CreateRoleRequest request);
        }
    }
    • RoleService.cs
    using System.Collections.Generic;
    using App.Service.Security;
    using App.Common.DI;
    using App.Repository.Secutiry;
    using App.Entity.Security;
    using App.Common;
    namespace App.Service.Impl.Security
    {
        public class RoleService : IRoleService
        {
            public CreateRoleResponse Create(CreateRoleRequest request)
            {
                ValidateCreateRequest(request);
                using (App.Common.Data.IUnitOfWork uow = 
                new App.Common.Data.UnitOfWork(new App.Context.AppDbContext(IOMode.Write)))
                {
                    IRoleRepository roleRepository = 
                    IoC.Container.Resolve<IRoleRepository>(uow);
                    IPermissionRepository permissionRepo = 
                    IoC.Container.Resolve<IPermissionRepository>(uow);
                    IList<Permission> permissions = 
                    permissionRepo.GetPermissions(request.Permissions);
                    Role role = new Role(request.Name, request.Description, permissions);
                    roleRepository.Add(role);
                    uow.Commit();
                }
                return new CreateRoleResponse();
            }
            private void ValidateCreateRequest(CreateRoleRequest request)
            {
                if (string.IsNullOrWhiteSpace(request.Name))
                {
                    throw new App.Common.Validation.ValidationException
                    ("security.addOrUpdateRole.validation.nameIsRequire");
                }
            }
            public IList<RoleListItemSummary> GetRoles()
            {
                IRoleRepository repository = IoC.Container.Resolve<IRoleRepository>();
                return repository.GetItems<RoleListItemSummary>();
            }
        }
    }

    In Role service:

    • User creates new Role calling Create method of Role repository.
    • In the UnitOfWork, we specify explicitly the mode we want to use for this UnitOfWork. This avoids unexpected change from being saved to database.
    • "security.addOrUpdateRole.validation.nameIsRequire", this will be resolved on client base on current language and display the error as the tooltip on each form component. In my code, the service should only return client the key of error, and client will display in appropriated language itself.
  3. IRoleRepository and RoleRepository for creating Role.

    We do not need to change this repository as it inherited appropriate method from IBaseContentRepository and BaseContentRepository.

    namespace App.Repository.Secutiry
    {
        public interface IRoleRepository: App.Common.Data.IBaseContentRepository<Role>
        {
        }
    }
    using App.Common.Data.MSSQL;
    using App.Entity.Security;
    using App.Repository.Secutiry;
    
    namespace App.Repository.Impl.Security
    {
        public class RoleRepository: BaseContentRepository<Role>, IRoleRepository
        {
            public RoleRepository() : base(new App.Context.AppDbContext(App.Common.IOMode.Read))
            {
            }
    
            public RoleRepository(IUnitOfWork uow) : base(uow.Context as IMSSQLDbContext)
            {
            }
        }
    }

Summary

Until this point, we can create new Role using the code.

For more information about other articles in this series

 

 

Thank you for reading,

Note: Please like and share to your friends if you think this is useful article, I really appreciate

 

 

Comments (1) -

Hi. Cool post. There�s an issue with your site in chrome, and you may want to test this� The browser is the marketplace chief and a good element of people will omit your excellent writing because of this problem.

Reply

Add comment