창, 서비스, NgRx 각도 자동 저장

사용자의 변경 사항을 저장하면 데이터 손실을 방지하고 사용자 체험을 자동으로 개선할 수 있습니다.Angular를 사용하여 자동으로 비헤이비어를 저장하는 방법을 살펴보겠습니다.

자동 저장 및 캐시


내가 가리키는 autosave는 서버 측에서 사용자의 입력 데이터를 자동으로 저장하는 서열화이거나 적어도 어느 정도에 Angular 이외에 오래 저장하는 것이다. 예를 들어 LocalStorage 또는 IndexedDB에서.
만약 응용 프로그램 내 내비게이션 사이의 캐시 입력 필드 상태만 원한다면, 사용자 정의 RouteReuseStrategy을 실현하여 re-use component state을 실현할 수 있습니다.
유사하게, 구성 요소의 수명을 초과한 서비스에 귀속해서 구성 요소의 상태를 유지할 수 있습니다.이것은 사용자 정의 서비스를 바탕으로 하는 solely on change detection 또는 leveraging RxJS behavior subjects, 그리고 NgRx store과 유사한 내용일 수 있다.

양식 자동 저장


각도에서 모양을 자동으로 저장하는 방법을 보여 줍니다.이 프레임워크는 RxJS를 이용하기 때문에 우리는 이미 좋은 상황에서 값이 변경될 때 반응적으로 데이터를 저장할 수 있다.
reactive forms을 사용하면 AbstractControl(예를 들어 FormGroup 또는 단일 FormControl)에 관찰 가능한 속성 valueChanges이 노출됩니다.유감스럽게도 다른 폼 API와 마찬가지로 폼의 값 대상을 보냈지만 이 관찰 가능한 대상의 유형은 여전히 any이다.최근, Angular team announced their work on strongly typed forms, 그래서 이것은 곧 좋아질 것이다!

valueChanges: Observable<any>, A multicasting observable that emits an event every time the value of the control changes, in the UI or programmatically -- Angular Documentation


자동 저장을 편리하게 하기 위해서, 이 관찰 항목에 쉽게 가입할 수 있으며, 폼 값을 서버가 이해하는 내용에 비추어 데이터를 보낼 수 있습니다.
하지만 이렇게 빨리 구독하지 마세요. 구독 리셋에서 수동으로 구독하지 마세요.이것은 실수하기 쉬워서 당신이 원하는 결과가 나오지 않을 수도 있습니다.반대로 저희가 예상한 자동 저장 행위를 위해 적당한 RxJS operators을 선택하겠습니다.
@Component({...})
export class MyComponent implements OnInit, OnDestroy {

    form: FormGroup

    private unsubscribe = new Subject<void>()

    constructor(private service: MyService) {}

    ngOnInit() {
        this.form = /* create reactive form */;
        this.form.valueChanges.pipe(
            switchMap(formValue => service.save(formValue)),
            takeUntil(this.unsubscribe)
        ).subscribe(() => console.log('Saved'))
    }

    ngOnDestroy() {
        this.unsubscribe.next()
    }
}
위의 코드 세그먼트에서 폼의 매번 변경은 저장 호출을 촉발합니다.그러나 switchMap을 사용했기 때문에 최근의save 호출만 어느 시점에 활성 상태입니다.이전의 저장 호출이 완료되지 않았을 때, 후속 값 변경은 이 호출을 취소합니다.
우리는 switchMap을 mergeMap으로 교체하여 모든 자동 저장 요청을 동시에 실행할 수 있습니다.이와 유사하게, 우리는 concatMap을 사용하여 각각save 호출을 실행할 수 있다.다른 옵션은 exhaustMap일 수 있습니다. 현재 저장 호출이 끝날 때까지 값 변경을 무시합니다.
어쨌든, 우리는 장수명을 관찰할 수 있는 대상을 처리하고 있기 때문에, 일단 우리의 표를 봉인한 구성 요소가 파괴되면, 구독 흐름을 취소해야 한다.위의 코드 세션에서 나는 takeUntil 연산자를 사용하여 이 점을 실현했다.
내가 하고 싶은 것은 폼의 최신 버전만 저장하고 debounceTime 연산자를 사용하여 값을 제한하는 것이다.500을 사용하여 떨기를 하는 것은 500ms가 지나야만 관찰 대상이 저장 호출을 시작할 수 있고 더 이상 값 변경이 없다는 것을 의미한다.
this.form.valueChanges.pipe(
    debounceTime(500),
    switchMap(formValue => service.save(formValue)),
    takeUntil(this.unsubscribe)
).subscribe(() => console.log('Saved'))
사용자가 데이터를 계속 입력할 때save를 정기적으로 실행하려면 auditTime 또는 throttleTime 연산자를 사용할 수 있습니다.
mailing list에 가입하여 트위터에서 나를 주목하고 더 깊은 Angular & RxJS 지식을 이해하다

테마 서비스 자동 저장


서비스의 모든 유형의 RxJS 테마를 통해 상태를 처리할 때 같은 원칙을 적용할 수 있습니다.원하는 행동에 적합한 조작부호 조합을 사용하여 테마를 전달할 수 있습니다.
auditTime으로 인해 다음 서비스는 1s 후 정기적으로 모든 설정 변경 사항을 자동으로 저장합니다.concatMap 운영자는 시간 순서대로 배열할 때 저장 요청을 취소하지 않을 것을 확보합니다.
export interface Settings {
    darkMode: boolean
}

export class SettingsService implements OnDestroy {

    private unsubscribe = new Subject<void>()

    private settings = new BehaviorSubject<Settings>({darkMode: false})

    public settings$ = this.settings.asObservable()

    constructor(private service: MyService) {
        this.settings.pipe(
            auditTime(1000),
            concatMap(settings => service.save(settings)),
            takeUntil(this.unsubscribe)
        ).subscribe(() => console.log('Saved'))
    }

    setDarkMode(darkMode: boolean) {
        this.settings.next({...this.settings.getValue(), darkMode})
    }

    ngOnDestroy() {
        this.unsubscribe.next()
    }
}

NgRx 자동 저장


NgRx를 사용할 때 autosave는 effect으로 실현하는 것이 가장 좋다.

Effects are where you handle tasks such as fetching data, long-running tasks that produce multiple events, and other external interactions where your components don't need explicit knowledge of these interactions. -- NgRx Documentation


내가 SceneLab을 위해 선택한 구체적인 방법은 모든 수정이 자동으로 저장되어야 하는 상태를 열거하는 것이다.우리는 이미 implement undo-redo으로 ngrx-wieder과 유사한 일을 했다.
const STATE_MODIFYING_ACTIONS = [
    addElementSuccess,
    undo,
    redo,
    resizeSelection
    ...
]
그리고 우리는 ofType을 사용하여 spread syntax 연산자를 초기화하여 이 조작 중의 어떤 효과도 탐지할 수 있다.
autosave$ = createEffect(() => this.actions$.pipe(
    ofType(...STATE_MODIFYING_ACTIONS),
    debounceTime(500),
    map(() => MyActions.save())
))
공지가 취소되면 이 효과는 저장 작업을 만들고 단독 효과에서 처리합니다.이것은 우리로 하여금 다른 곳의 보존을 쉽게 촉발하고 관심사를 적절하게 분리할 수 있게 한다.실제 저장 효과는 결국 NgRx에 대해 비동기식 효과를 작성하는 것과 유사합니다.나는 또한 withLatestFrom을 사용하여 저장할 최신 상태에 접근한다.
save$ = createEffect(() => this.actions$.pipe(
    ofType(MyActions.save),
    withLatestFrom(this.store)
    switchMap(([action, state]) => this.service.save(state)),
    map(() => MyActions.saveSuccess())
))
save 호출에 오류가 발생할 수 있습니다. handle differently than NgRx이 필요할 수 있습니다.기본적으로 최대 10회까지 관찰할 수 있는 효과를 다시 구독합니다.
또한 Google은 현재 데이터를 저장하고 있음을 표시하는 로고를 Google 상태에서 관리할 수 있습니다.
const myReducer = createReducer(initialState,
    on(...STATE_MODIFYING_ACTIONS, state => {
        return {...state, saved: false}
    }),
    on(MyActions.saveSuccess, state => {
        return {...state, saved: true}
    })
)

@Component({...})
export class MyComponent implements OnInit, OnDestroy {

    saved$ = this.store.select(state => state.saved)

    constructor(private store: Store<State>) {}
}
<p *ngIf="saved$ | async; else saving">saved</p>
<ng-template #saving>
    <p>saving...</p>
</ng-template>
'저장...'을 표시하지 않고 사용자 체험을 정확하게 하려면사용자가 변경하기 전에 pristine flag similar to the one from Angular forms을 관리해야 합니다.
제목에 표시기가 있는 SceneLab을 찾는 방법은 다음과 같습니다.

HTTP 또는 WebSocket?로컬 스토리지?


나는 줄곧 service.save(state)을 자리 표시자로 사용하여 지속적인 데이터를 보내는 HTTP 서버 요청을 하고 있다.그러나 HTTP가 autosave에 사용되는 정확한 프로토콜인지 알고 싶을 수도 있습니다. 저도요. 제 입장에서 볼 때 두 가지 측면이 있습니다.
  • 유효 하중 크기
  • 요청 주파수
  • HTTP는 각 요청의 오버헤드가 적당하기 때문에 낮은 요청 빈도에 더 적합하고 부하 크기는 임의로 클 수 있습니다.그러나 좋은 성능을 얻기 위해 유효 부하 크기를 최대한 낮추기를 원할 수도 있습니다.
    한편, Websockets은 그 다음에 최소한의 메시지만 보낼 수 있도록 연결을 한 번 엽니다.따라서 비교적 작은 유효 부하를 사용하여 더욱 높은 요청 빈도를 얻는 것이 좋다.웹소켓은 특히 서버에서 클라이언트로 데이터를 전송하는데 예를 들어 채팅 응용 프로그램에 사용된다.단, autosave에 대해서는 클라이언트 데이터를 서버에 보내기만 하면 됩니다.
    그러나 낮은 요청 빈도와 높은 요청 빈도는 무엇입니까?나는 사용자 변경을 바탕으로 하는 비공고 구현을 사용하면 저장 빈도가 그렇게 높지 않을 것이라고 생각한다.따라서 서버와 에이전트가 지원해야 할 장수명 연결과 관련된 새로운 프로토콜을 시도해 보시기 바랍니다.단, 서버가 HTTP/2을 사용하고 있는지 확인하십시오.
    SceneLab에 대해 우리는 HTTP를 사용하는데 부하 크기는 보통 수천 바이트 정도이다.app에서 사용해 보세요. 기분이 어떤지 보십시오. (로그인해야 서버에 자동으로 저장할 수 있습니다.)
    참고로 Google Docs은 키를 누를 때마다 HTTP POST 요청을 전송합니다.
    동시에 서버에 데이터를 보내지 않아도 되는 용례가 있을 수 있습니다.아마도 데이터를 LocalStorage 또는 IndexedDB에 저장하면 충분할 것이다.이것이 바로 우리가 SceneLab에서 한 일입니다. 로그인하지 않은 상태에서 프로그램을 사용할 때.로그인하면 등록을 제출하기 전에 작성한 항목을 복원할 수 있습니다.

    좋은 웹페이지 즐겨찾기