Skip to content

zeprototypeng-community/zeprototypeng

Repository files navigation

ZePrototype NG

An Angular admin template

Prerequisites

Installation

Open the command prompt at the root of the project and type the following command.

npm install

Configuration

Edit environment files /environments/environment.ts and /environments/environment.prod.ts in relation to your development and deployment environment.

Quick Start

Run the application and enjoy ☻:

ng serve

Usage

Authentication page

Enter username and password to login. You can adjust the authentication system as you wish.

Components

Create your components in module main

ng generate component main/mycomponentname

Application routes configuration

Add a property to appRoutes const variable in /src/app/application/routing/app-routes.ts file :

export const appRoutes = {
  ...
  myroute: { path: 'my-route-path', label: 'My route label' }
};

Append route configuration to children property of first Route object in /src/app/main/main-routing.module.ts file:

...
{
    path: appRoutes.myroute.path, // the property that you added to the constant variable
    component: MyComponent, // the generated component
    data: { breadcrumb: appRoutes.myroute.label } //Label to display
}

Your component is now accessible through the left sidebar.

Generate a form

Open /src/app/application/forms/forms.ts file, add these lines to formIds and Iform const variables.

...
export const formIds = {
    ...
    myFormId: 7 // Your form id must be unique
}
...
export const forms: Iform[] = [
    ...
    {
        id: formIds.myFormId,
        controls: [
            {
                // refer to the 'controls' property of the object whose 'id' property value is equal to 'formIds.testFormId'
            }
        ]
    },
]

Now, edit your component like this:

/src/app/main/my-component/my-component.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { formIds } from 'src/app/application/forms/forms';
import { Iform } from 'src/app/core/form-module/dynamic-form/dynamic-form.component';
import { FormService } from 'src/app/core/form-module/services/form.service';
import { UiNotificationService, UIStateStatusCode } from 'src/app/partials/ui-notification/ui-notification.service';
...
export class MyComponent implements OnInit {

    form: Iform;
    formGroup: FormGroup;

    constructor(private uiState: UiNotificationService, private client: HttpRequestService, private formService: FormService) { }
    
    ngOnInit(): void {
        this.buildForm();
    }

    async buildForm() {
        this.form = await this.formService.getForm(formIds.myFormId);
        this.formGroup = this.formService.getFormGroup(this.form.controls);
    }
}

/src/app/main/my-component/my-component.component.html

<form [formGroup]="formGroup" *ngIf="formGroup">
    <app-dynamic-form [formGroup]="formGroup" [form]="form"></app-dynamic-form>
</form>

API routes configuration

Add a property to minRoutes and apiRoutes const variables in /src/app/application/routing/app-routes.ts file :

export const minRoutes = {
  ...
  my_min_api_route: 'my-api-route-prefix-name'
};

export const apiRoutes = {
  ...
  my_api_route: environment.APP_SERVER_HOST + environment.URLPART1 + minRoutes.my_min_api_route
};

List, Create, Update and Delete data

Create a .ts file under the directory /src/app/application/models/ as a model.

export class MyModel {
    public id: number = undefined;
    public label: string = undefined;
}

Import model into component and enjoy the result.

/src/app/main/my-component/my-component.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ClrDatagridStateInterface } from '@clr/angular';
import { Subject, takeUntil } from 'rxjs';
import { formIds } from 'src/app/application/forms/forms';
import { MyModel } from 'src/app/application/models/MyModel';
import { responsesMessages } from 'src/app/application/response/messages';
import { PaginateResponse, getResponseState, getResponseData, getResponseCode } from 'src/app/application/response/response';
import { apiRoutes } from 'src/app/application/routing/api-routes';
import { Iform } from 'src/app/core/form-module/dynamic-form/dynamic-form.component';
import { FormService } from 'src/app/core/form-module/services/form.service';
import { HttpRequestService } from 'src/app/core/http/http-request.service';
import { UiNotificationService, UIStateStatusCode } from 'src/app/partials/ui-notification/ui-notification.service';
import { getFiltersFromDatagrid } from 'src/app/core/util/datagrid-util';

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.scss']
})
export class MyComponent implements OnInit {

  _destroy$ = new Subject();

  form : Iform;
  formGroup : FormGroup;

  url = apiRoutes.my_api_route;
  singleData: MyModel;
  data: PaginateResponse = new PaginateResponse(new MyModel);
  initialGridState: ClrDatagridStateInterface = { page: { current: 1, size: 10 } };
  loading = true;
  updating = false;
  openModal = false;

  constructor(private uiState: UiNotificationService, private client: HttpRequestService, private formService: FormService) { }

  ngOnInit(): void {
    this.buildForm();
  }

  async buildForm() {
    this.form = await this.formService.getForm(formIds.myFormId);
    this.formGroup = this.formService.getFormGroup(this.form.controls);
  }

  /**
   * get data 
   * 
   * @param param 
   */
  getData(param?: string) {
    this.loading = true;
    this.uiState.startAction();
    param = param ? param : getFiltersFromDatagrid(this.initialGridState);
    this.client.get(this.url + '?' + param)
      .pipe(takeUntil(this._destroy$))
      .subscribe((result) => {
        if (getResponseState(result)) {
          this.data = getResponseData(result);
          this.uiState.endAction();
        } else {
          this.uiState.endAction(getResponseCode(result), responsesMessages.FAILED);
        }

        this.loading = false;
      })
  }


  /**
   * submit form data
   * 
   * @param param 
   */
  onSubmit(param: FormGroup) {
    this.uiState.startAction();
    let dataToSend = param.getRawValue();
    this.client.post(this.url, dataToSend)
    .pipe(takeUntil(this._destroy$))
    .subscribe((result) => {
      if (getResponseState(result)) {
        this.formGroup.reset();
        this.getData();
        this.uiState.endAction(UIStateStatusCode.CREATED, responsesMessages.SUCCESS);
      } else {
        this.uiState.endAction(getResponseCode(result), responsesMessages.FAILED);
      }
    });
  }

  /**
   * set form with values from the ressource id
   * 
   * @param id 
   */
  onEdit(id: number) {
    this.client.get(this.url + '/' + id)
    .pipe(takeUntil(this._destroy$))
    .subscribe((result) => {
      if (getResponseState(result)) {
        this.singleData = getResponseData(result);
        Object.keys(this.singleData).forEach((v, i) => {
          this.formGroup.controls?.[v]?.setValue(this.singleData?.[v]);
          this.updating = true;
        })
        this.uiState.endAction(UIStateStatusCode.CREATED, responsesMessages.SUCCESS);
      } else {
        this.uiState.endAction(getResponseCode(result), responsesMessages.FAILED);
      }
    });
  }

  /**
   * cancel form editing
   */
  onCancel() {
    this.formGroup.reset();
    this.updating = false;
  }

  /**
   * update data
   * 
   * @param param 
   */
  onUpdate(param: FormGroup) {
    this.uiState.startAction();
    let dataToSend = param.getRawValue();
    this.client.put(this.url + '/' + this.singleData.id, dataToSend)
    .pipe(takeUntil(this._destroy$))
    .subscribe((result) => {
      if (getResponseState(result)) {
        this.formGroup.reset();
        this.updating = false;
        this.getData();
        this.uiState.endAction(UIStateStatusCode.OK, responsesMessages.SUCCESS);
      } else {
        this.uiState.endAction(getResponseCode(result), responsesMessages.FAILED);
      }
    });
  }

  /**
   * attempt to delete data
   */
  onDeleteAttempt(id: number) {
    if (!this.singleData) {
      this.singleData = new MyModel();
    }
    this.singleData.id = id;
    this.openModal = true;
  }

  /**
   * delete data by the given resource id
   * 
   * @param id 
   */
  onDelete(id: number) {
    this.uiState.startAction();
    this.client.delete(this.url + '/' + id)
    .pipe(takeUntil(this._destroy$))
    .subscribe((result) => {
      if (getResponseState(result)) {
        this.getData();
        this.uiState.endAction(UIStateStatusCode.OK, responsesMessages.SUCCESS);
      } else {
        this.uiState.endAction(getResponseCode(result), responsesMessages.FAILED);
      }
      this.openModal = false;
    });
  }

  /**
   * on datagrid refresh event
   * 
   * @param state 
   */
  onDgRefresh = (state: ClrDatagridStateInterface) => {
    this.getData(getFiltersFromDatagrid(state))
  }

  ngOnDestroy(): void {
    this._destroy$.next('');
  }
}

/src/app/main/my-component/my-component.component.html

<form [formGroup]="formGroup" *ngIf="formGroup">
    <app-dynamic-form [formGroup]="formGroup" [form]="form"></app-dynamic-form>

    <button class="btn btn-sm btn-success" type="submit" *ngIf="updating == false" [disabled]="formGroup.invalid">
        SAVE
    </button>
    <button class="btn btn-sm btn-success" type="button" *ngIf="updating == true" (click)="onUpdate(formGroup)" [disabled]="formGroup.invalid">
        EDIT
    </button>
    <button class="btn btn-sm btn-danger" type="button" *ngIf="updating == true" (click)="onCancel()">
        CANCEL
    </button>
</form>

<clr-datagrid (clrDgRefresh)="onDgRefresh($event)" [clrDgLoading]="loading">
    <clr-dg-column>ID</clr-dg-column>
    <clr-dg-column>Label</clr-dg-column>

    <clr-dg-row *ngFor="let item of data.data">
        <clr-dg-cell>{{item.id}}</clr-dg-cell>
        <clr-dg-cell>{{item.label}}</clr-dg-cell>
        <clr-dg-action-overflow>
            <button class="action-item" (click)="onEdit(item.id)">MODIFIER</button>
            <button class="action-item" (click)="onDeleteAttempt(item.id)">SUPPRIMER</button>
        </clr-dg-action-overflow>
    </clr-dg-row>

    <clr-dg-footer>
        <clr-dg-pagination #pagination [clrDgPageSize]="initialGridState.page.size" [clrDgTotalItems]="data?.total">
            <clr-dg-page-size [clrPageSizeOptions]="[10, 20, 30]"></clr-dg-page-size>
            {{ pagination.firstItem + 1 }} - {{ pagination.lastItem + 1 }} of {{ pagination.totalItems }} elements
        </clr-dg-pagination>
    </clr-dg-footer>
</clr-datagrid>

<clr-modal [(clrModalOpen)]="openModal">
    <h3 class="modal-title">DELETE</h3>
    <div class="modal-body">
      <p>Do you want to delete?</p>
    </div>
    <div class="modal-footer">
      <button type="button" class="btn btn-sm btn-outline" (click)="openModal = false">CANCEL</button>
      <button type="button" class="btn btn-sm btn-danger" (click)="onDelete(singleData.id)">YES, CONFIRM</button>
    </div>
  </clr-modal>