Skip to content

Commit 7a4f8a1

Browse files
authored
Merge pull request #20 from BIATeam/develop
Develop
2 parents eaadd30 + 580af7d commit 7a4f8a1

File tree

12 files changed

+756
-444
lines changed

12 files changed

+756
-444
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
---
2+
sidebar_position: 1
3+
---
4+
5+
# Prevent form closing on error
6+
When opening a form on popup or calc mode, we want sometimes prevent the closing of the form even in error case. This page explains how to handle this.
7+
8+
## Block form
9+
### Front
10+
**NOTE :** these configuration instructions are already included into features generated from the version **4.1.0** of BIA Framework.
11+
1. Ensure that your feature has store action called after success or fail to create or edit an item using the matching effect into the `my-feature-effects.ts` file.
12+
Example with the `update$` effect, the store action on success is on **line 127** and on fail on **line 134** :
13+
![PopupConflict](../../Images/BlockForm_EffectsStore.png)
14+
1. Add in your feature service that inherits from `CrudItemService<>` the values for the properties `_updateSuccessActionType`, `_createSuccessActionType` and `_updateFailureActionType` matching the same store action :
15+
``` typescript title="my-feature.service.ts"
16+
export class MyFeatureService extends CrudItemService<MyFeature> {
17+
/**
18+
* Type of store action called after update effect is successful.
19+
* See update effect in store effects of CRUD item.
20+
*/
21+
_updateSuccessActionType = FeatureMyFeaturesActions.loadAllByPost.type;
22+
23+
/**
24+
* Type of store action called after create effect is successful.
25+
* See create effect in store effects of CRUD item.
26+
*/
27+
_createSuccessActionType = FeatureMyFeaturesActions.loadAllByPost.type;
28+
29+
/**
30+
* Type of store action called after update effect has failed.
31+
* See update effect in store effects of CRUD item.
32+
*/
33+
_updateFailureActionType = FeatureEnginesActions.failure.type;
34+
}
35+
```
36+
37+
## Inform user of conflict
38+
Sometimes, the data opened for edit in the form can be changed by another user at the same time. You can handle these changes directly in your form to inform your user of this conflict.
39+
### Back
40+
**NOTE :** these configuration instructions are already included into features generated from the version **4.1.0** of BIA Framework.
41+
1. Your entity must derived of `VersionedTable` type
42+
2. Your mapper should include in the `EntityToDto()` method a mapping for the `RowVersion` property of the DTO based on the same property from the entity :
43+
``` csharp title="MyEntityMapper.cs"
44+
public override Expression<Func<MyEntity, MyEntityDto>> EntityToDto()
45+
{
46+
return entity => new MyEntityDto
47+
{
48+
// [...]
49+
50+
// Add the RowVersion mapping
51+
RowVersion = Convert.ToBase64String(entity.RowVersion),
52+
}
53+
}
54+
```
55+
3. Add in your `Update()` endpoint of your entity controller the catch of `OutDatedException` and the handle of `409` status code :
56+
``` csharp title="MyEntityController.cs"
57+
// Add this attribute
58+
[ProducesResponseType(StatusCodes.Status409Conflict)]
59+
public async Task<IActionResult> Update(int id, [FromBody] MyEntityDto dto)
60+
{
61+
try
62+
{
63+
// [...]
64+
}
65+
//Add this catch block
66+
catch (OutDatedException)
67+
{
68+
return this.Conflict();
69+
}
70+
}
71+
```
72+
### Front
73+
1. Add in your feature model the assign of the `BiaFieldConfig` for the `rowVersion` :
74+
``` typescript title="my-feature.ts"
75+
export const myFeatureFieldsConfiguration : BiaFieldsConfig<MyFeature> = {
76+
columns: [
77+
// [...]
78+
Object.assign(new BiaFieldConfig('rowVersion', 'myFeature.rowVersion'), {
79+
isVisible: false,
80+
isHideByDefault: true,
81+
}),
82+
]
83+
}
84+
```
85+
2. In your feature edit component HTML template, add the binding for the `isCrudItemOutdated` property between the feature edit component and the feature form component :
86+
``` html title="my-feature-edit.component.html"
87+
<app-my-feature-form
88+
[isCrudItemOutdated]="isCrudItemOutdated"></app-my-feature-form>
89+
```
90+
91+
Warning displayed into the edit popup :
92+
![PopupConflict](../../Images/BlockForm_Popup_Conflict.png)
93+
94+
Warning displayed as toast on calc mode :
95+
![CalcConflict](../../Images/BlockForm_Calc_Conflict.png)
96+
97+
### SignalR
98+
You can inform your user with SignalR that the data on the opened form has been updated by another user.
99+
This feature works only with the **edit component** on **popup mode**.
100+
#### Back
101+
1. On your feature controller, enable the `UseHubForClientInAirport` constant definition and add a sending instruction to the client with the dedicated identifier and hub :
102+
``` csharp title="MyFeatureController"
103+
#define UseHubForClientInAirport
104+
105+
namespace MyCompany.MyProject.Presentation.Api.Controllers.MyFeature
106+
{
107+
// [...]
108+
public class MyFeatureController : BiaControllerBase
109+
{
110+
// [...]
111+
public async Task<IActionResult> Update(int id, [FromBody] MyEntityDto dto)
112+
{
113+
try
114+
{
115+
// [...]
116+
var updatedDto = await this.airportService.UpdateAsync(dto);
117+
118+
// Add the signalR notification
119+
#if UseHubForClientInAirport
120+
await this.clientForHubService.SendTargetedMessage(string.Empty, "myfeatures", "update-myfeature", updatedDto);
121+
#endif
122+
123+
return this.Ok(updatedDto);
124+
}
125+
catch
126+
{
127+
// [...]
128+
}
129+
}
130+
}
131+
}
132+
```
133+
134+
#### Front
135+
1. Edit your constant feature file to use signalR :
136+
``` typescript title="my-feature.constants.ts"
137+
export const airportCRUDConfiguration: CrudConfig<Airport> = new CrudConfig({
138+
// [...]
139+
useSignalR: true,
140+
});
141+
```
142+
2. Create or complete your feature signalR service with following methods `registerUpdate()` and `unregisterUpdate()` :
143+
``` typescript title="my-feature-signalr.service.ts"
144+
import { Injectable } from '@angular/core';
145+
import { BiaSignalRService } from 'src/app/core/bia-core/services/bia-signalr.service';
146+
import { TargetedFeature } from 'src/app/shared/bia-shared/model/signalR';
147+
148+
@Injectable({
149+
providedIn: 'root',
150+
})
151+
export class MyFeatureSignalRService {
152+
private targetedFeature: TargetedFeature;
153+
154+
constructor(private signalRService: BiaSignalRService) {}
155+
156+
initialize() {
157+
this.targetedFeature = {
158+
parentKey: '',
159+
featureName: 'myFeatures',
160+
};
161+
this.signalRService.joinGroup(this.targetedFeature);
162+
}
163+
164+
registerUpdate(callback: (args: any) => void) {
165+
console.log(
166+
'%c [MyFeatures] Register SignalR : update-myFeature',
167+
'color: purple; font-weight: bold'
168+
);
169+
this.signalRService.addMethod('update-myFeature', args => {
170+
callback(args);
171+
});
172+
}
173+
174+
destroy() {
175+
this.unregisterUpdate();
176+
this.signalRService.leaveGroup(this.targetedFeature);
177+
}
178+
179+
private unregisterUpdate() {
180+
console.log(
181+
'%c [MyFeatures] Unregister SignalR : update-myFeature',
182+
'color: purple; font-weight: bold'
183+
);
184+
this.signalRService.removeMethod('update-myFeature');
185+
}
186+
}
187+
```
188+
3. Complete your feature edit component in order to react to signalR notifications :
189+
``` typescript title="my-feature-edit.component.ts"
190+
import { Component, Injector, OnDestroy, OnInit } from '@angular/core';
191+
import { firstValueFrom } from 'rxjs';
192+
import { CrudItemEditComponent } from 'src/app/shared/bia-shared/feature-templates/crud-items/views/crud-item-edit/crud-item-edit.component';
193+
import { myFeatureCRUDConfiguration } from '../../my-feature.constants';
194+
import { MyFeature } from '../../model/my-feature';
195+
import { MyFeatureSignalRService } from '../../services/my-feature-signalr.service';
196+
import { MyFeatureService } from '../../services/my-feature.service';
197+
198+
@Component({
199+
selector: 'app-my-feature-edit',
200+
templateUrl: './my-feature-edit.component.html',
201+
})
202+
export class MyFeatureEditComponent
203+
extends CrudItemEditComponent<MyFeature>
204+
implements OnInit, OnDestroy
205+
{
206+
private useSignalR = myFeatureCRUDConfiguration.useSignalR;
207+
208+
constructor(
209+
protected injector: Injector,
210+
public myFeatureService: MyFeatureService,
211+
protected signalrService: MyFeatureSignalRService
212+
) {
213+
super(injector, myFeatureService);
214+
this.crudConfiguration = myFeatureCRUDConfiguration;
215+
}
216+
217+
ngOnInit() {
218+
super.ngOnInit();
219+
220+
if (this.useSignalR) {
221+
this.signalrService.initialize();
222+
223+
this.signalrService.registerUpdate(async args => {
224+
const updatedCrudItem = JSON.parse(args) as MyFeature;
225+
const currentCrudItem = await firstValueFrom(
226+
this.myFeatureService.crudItem$
227+
);
228+
229+
if (
230+
currentCrudItem.id === updatedCrudItem.id &&
231+
currentCrudItem.rowVersion !== updatedCrudItem.rowVersion
232+
) {
233+
this.isCrudItemOutdated = true;
234+
}
235+
});
236+
}
237+
}
238+
239+
ngOnDestroy() {
240+
super.ngOnDestroy();
241+
242+
if (this.useSignalR) {
243+
this.signalrService.destroy();
244+
}
245+
}
246+
}
247+
```
248+
249+

0 commit comments

Comments
 (0)