Skip to content

Instantly share code, notes, and snippets.

@dennishn
Last active February 13, 2017 05:57
Show Gist options
  • Save dennishn/98650d9cfc938f73edb1eb06401abc32 to your computer and use it in GitHub Desktop.
Save dennishn/98650d9cfc938f73edb1eb06401abc32 to your computer and use it in GitHub Desktop.
Emil Møller and Dennis Haulund Nielsen's amazing DRY, SOC data modelling concept for Angular
/*
I am the base Service, any business specific service will extend upon me.
My purpose is to control generic things, common for all services.
For instance i keep track of when a service is communicating with a server.
*/
class BaseService {
public isSending$: Observable<any>;
public isSendingSubject = new Subject<any>();
constructor() {
this.isSending$ = this.isSendingSubject.asObservable();
}
}
/*
I control communication with servers. I could use Apollo, XHR, WebSockets and anything else.
My purpose is to control asyncronus tasks.
*/
export class DataBackend {
constructor() {}
public post(payload): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, 1000);
});
}
}
/*
I control the data integrity and business logic.
My purpose is expose an interface for a specific business model, and a business model class.
The business model also has responsibility of exposing a FormGroup (or FormArray, or FormControl)
*/
import {FormGroup} from "@angular/forms";
import {FormControl} from "@angular/forms";
import {Validators} from "@angular/forms";
export interface IData {
name?: string;
}
export class Data implements IData {
public name: string;
constructor(
name?: string
) {
this.name = name;
}
}
export class DataModel {
public form: FormGroup;
public internalModelData: IData;
constructor(name?: string) {
this.internalModelData = new Data(
name
);
this.form = new FormGroup({
name: new FormControl(this.internalModelData.name || null, Validators.required)
});
}
}
/*
I am the Injectable Service that Components will integrate with.
My purpose is combine the Model and Backend layers and expose a common API that makes use of these layers.
*/
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {Subject} from "rxjs";
import {DataBackend} from './data.backend';
import {IData, DataModel} from './data.model';
import {BaseService} from './base.service';
@Injectable()
export class DataService extends BaseService {
public dataModel: DataModel;
private dataBackend: DataBackend;
constructor(name?: string) {
super();
this.dataModel = new DataModel(name);
this.dataBackend = new DataBackend();
}
public submit() {
this.isSendingSubject.next(true);
return this.dataBackend.post(this.dataModel.form.getRawValue()).then(
(res) => {
this.isSendingSubject.next(false);
return res;
}
);
}
}
This is just snippets from the component we used to test the implementation against.
public dataSvc: DataService;
public isSubmitting: boolean = false;
## In the Constructor
this.dataSvc = new DataService(...data...);
## In ng OnInit
this.dataSvc.isSending$.subscribe(
(res) => {
console.log('obs', res);
this.isSubmitting = res;
}
);
## A method for the template to call
public submit() {
this.dataSvc.submit().then(
res => console.log('Data Received!', res)
);
}
## In the template
<form [formGroup]="dataSvc.dataModel.form" (ngSubmit)="submit()" novalidate>
<input formControlName="name" type="text">
<button class="button" [disabled]="!dataSvc.dataModel.form.valid" type="submit">SUBMIT</button>
</form>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment