Yükleniyor...

1 Şubat 2020

Angular NGRX Store ve State İçin Local Storage Alt Yapısı

Merhaba arkadaşlar, Angular 8 ve Angular Ngrx kullandığımız projelerimizde, sistemin performansını kat ve kat arttırmak için gayet kullanışlı ve profesyonel bir local storage alt yapısı oluşturabiliriz. Redux alt yapısında oluşturduğumuz State’lerimizi local storage’a en profesyonel şekilde kaydedip işlemlerimizi gerçekleştirebiliriz. Angular Ngrx’de yaşam döngüsü aşağıda resimdeki gibidir.

Bu yaşam döngüsüne ek olarak profesyonel bir şekilde Redux State’lerimiz için bir local storage alt yapısını oluşturabiliriz ve sayfa yenilense dahi State’deki verilerimizin hiçbiri kaybolmaz Local Storage ortamından geri çekebiliriz.

ÖRNEK PROJE LİNKİ

https://friend-finder-server.azurewebsites.net

The purpose of the project is to provide users with a system developed with the latest technology like Angular, Angular Ngrx, .Net Core Web Api to create their own specific post.
https://github.com/dogaanismail/FriendFinder
0 forks.
2 stars.
2 open issues.
Recent commits:

GERÇEK PROJEDEN ÖRNEKLER

1.Adım Örnek Klasörleme

Öncelikle storageMetaReducer ve StoreLocalStorageService adlı iki önemli bir yapı kullanacağız ve örnek klasörleme yukarıdaki gibidir. Siz isterseniz başka bir klasörleme tekniğini kullanabilirsiniz. Hadi gelin bu iki önemli alt yapıyı sistemimize bir kuralım.

2.Adım storage-metareducer oluşturulması

storageMetaReducer adlı fonksiyonumuz aşağıdaki gibidir.

import { ActionReducer, Action } from '@ngrx/store';
import { merge, pick } from 'lodash-es'
import { StoreLocalStorageService } from './store-local-storage.service';
export function storageMetaReducer<S, A extends Action = Action>(saveKeys: string[], localStorageKey: string, storageService: StoreLocalStorageService) {
    let onInit = true; // after load/refresh…
    return function (reducer: ActionReducer<S, A>) {
        return function (state: S, action: A): S {
            // get to the nextState.
            const nextState = reducer(state, action);
            // init the application state.
            if (onInit) {
                onInit = false;
                const savedState = storageService.getSavedState(localStorageKey);
                return merge(nextState, savedState);
            }
            // save the next state to the application storage.
            const stateToSave = pick(nextState, saveKeys);
            storageService.setSavedState(stateToSave, localStorageKey);
            return nextState;
        };
    };
}

3.Adım StoreLocalStorageService oluşturulması

store-local-storage.service.ts adlı servisimizin kodları ise aşağıdaki gibidir.

import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class StoreLocalStorageService {
    constructor() { }
    setSavedState(state: any, localStorageKey: string) {
        localStorage.setItem(localStorageKey, JSON.stringify(state));
    }
    getSavedState(localStorageKey: string): any {
        return JSON.parse(localStorage.getItem(localStorageKey));
    }
}

4.Adım Modül Oluşturulması

Benim üzerinde çalıştığım bir web sayfası ve bu web sayfasından örnek vermek isterim. https://friend-finder-server.azurewebsites.net/timeline Timeline adlı bir web sayfam var ve bu timeline sayfası için bir modül oluşturdum ve bu modülü base modül olan yani sistemin ana modülü olan app.module’a eklemiş bulunmaktayım.

Timeline modül’de ise Ngrx reducer’daki post.reducer için gerekli local storage ayarlarını sağlayacağım. Yukarıda belirtmiş olduğum web sayfasındaki linke tıklarsınız karşınıza postlar gelecektir ve ne demek istediğimi daha iyi anlayabilirsiniz.

post.reducer dosyamda ise benim postlarıma ait State’im mevcuttur ve benim amacım bu State’i local storage sistemine olabildiğince profesyonel bir şekilde kaydetmek ve örneğin herhangi bir kullanıcı yeni bir post eklediği zaman zaten reducer sayesinde bu state’e eklemek ve aynı zamanda local storage’deki veriye de eklenilmesini sağlamaktır.

POST REDUCER

import { Post } from '../../models/post/post';
import * as fromRoot from '../../ngrx/state/app.state';
import { PostActions, PostActionTypes } from '../actions/post.actions';
export interface State extends fromRoot.State {
  posts: PostState;
}
export interface PostState {
  showPostId: boolean;
  currentPost: Post;
  currentPostId: number;
  posts: Post[];
  commentPost: Post;
  isNewPost: boolean;
  error: string;
  isNewComment: boolean;
}
const initialState: PostState = {
  showPostId: true,
  currentPost: null,
  currentPostId: null,
  posts: [],
  commentPost: null,
  isNewPost: false,
  error: '',
  isNewComment: false
};
export function postReducer(state = initialState, action: PostActions): PostState {
  switch (action.type) {
    case PostActionTypes.TogglePost:
      return {
        ...state,
        showPostId: action.payload
      };
    case PostActionTypes.SetCurrentPost:
      return {
        ...state,
        currentPostId: action.payload
      };
    case PostActionTypes.LoadSuccess:
      return {
        ...state,
        posts: action.payload,
        error: ''
      };
    case PostActionTypes.LoadFail:
      return {
        ...state,
        posts: [],
        error: action.payload
      };
    case PostActionTypes.ClearCurrentPost:
      return {
        ...state,
        currentPostId: null
      };
    case PostActionTypes.InitializeCurrentPost:
      return {
        ...state,
        currentPostId: 0
      };
    case PostActionTypes.CreatePost:
      return {
        ...state,
        isNewPost: true
      };
    case PostActionTypes.CreatePostSuccess:
      return {
        ...state,
        posts: [...state.posts, action.payload].sort((a, b) => <any>new Date(b.createdDate) - <any>new Date(a.createdDate)),
        error: '',
        isNewPost: false
      };
    case PostActionTypes.CreatePostFail:
      return {
        ...state,
        error: action.payload,
        isNewPost: false
      };
    case PostActionTypes.CreateComment:
      return {
        ...state,
        isNewComment: true
      };
    case PostActionTypes.CreateCommentSuccess:
      const post: Post = state.posts.filter((item: any) => item.id == action.payload.postId)[0];
      post.comments.push(action.payload);
      return {
        ...state,
        posts: [...state.posts, action.payload],
        error: '',
        isNewComment: false
      };
    case PostActionTypes.CreateCommentFail:
      return {
        ...state,
        error: action.payload,
        isNewComment: false
      };
    case PostActionTypes.CreateGif:
      return {
        ...state,
        isNewPost: true
      };
    case PostActionTypes.CreateGifSuccess:
      return {
        ...state,
        posts: [...state.posts, action.payload].sort((a, b) => <any>new Date(b.createdDate) - <any>new Date(a.createdDate)),
        error: '',
        isNewPost: false
      };
    case PostActionTypes.CreateGifFail:
      return {
        ...state,
        error: action.payload,
        isNewPost: false
      };
    default:
      return state;
  }
}

Yukarıda görmüş olduğunuz PostState adlı mekanizmayı dediğim gibi olabildiğince profesyonel bir şekilde tarayıcının local storage sistemine kaydetmektedir, benim temel amacım bu.

ÖRNEK MODÜL KLASÖRLEME SİSTEMİ

Yukarıdaki resimde görmüş olduğunuz gibi örnek modül klasörleme sistemi bu şekildedir. Tabi herkesin klasörleme sistemi bu şekildedir, siz kendinize göre de ayarlayabilirsiniz.

Modül Token Oluşturulması

Local Storage sistemine state’lerimizi kaydetmek için daha profesyonel olması açısından token veya başka bir deyişle key kullanmak çok daha sağlıklı olacaktır. Başta belirttiğim gibi benim oluşturmuş olduğum modülümün ismi timeline olsun ve token dosyamın adı ise timeline.tokens.ts olsun.

import { InjectionToken } from '@angular/core';
import { StoreConfig } from '@ngrx/store/src/store_module';
import * as fromReducer from '../../ngrx/reducers/post.reducer';
import * as fromActions from '../../ngrx/actions/post.actions';
export const POSTS_STORAGE_KEYS = new InjectionToken<keyof fromReducer.PostState[]>('PostsStorageKeys');
export const POSTS_LOCAL_STORAGE_KEY = new InjectionToken<string[]>('PostsStorage');
export const POSTS_CONFIG_TOKEN = new InjectionToken<StoreConfig<fromReducer.PostState, fromActions.PostActions>>('PostsConfigToken');

fromReducer ve fromActions gibi isimlendirmeleri size bırakıyorum ve ayrıca benim NGRX klasörleme sistemimi de sizinle paylaşacağım merak etmeyin.

Timeline Modül

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TimelineFriendsComponent } from './timeline-friends/timeline-friends.component';
import { TimelineImagesComponent } from './timeline-images/timeline-images.component';
import { TimelineMessagesComponent } from './timeline-messages/timeline-messages.component';
import { TimelineVideosComponent } from './timeline-videos/timeline-videos.component';
import { TimelineNearbyComponent } from './timeline-nearby/timeline-nearby.component';
import { TimelineSidebarComponent } from './timeline-sidebar/timeline-sidebar.component';
import { TimelineComponent } from './timeline/timeline.component';
import { TimelineCreatePostComponent } from './timeline-create-post/timeline-create-post.component';
import { TimelinePostListComponent } from './timeline/timeline-post-list.component';
import { TimelineFollowUserComponent } from './timeline-follow-user/timeline-follow-user.component';
import { TimelineChatOnlineComponent } from './timeline-chat-online/timeline-chat-online.component';
import { SharedModule } from '../../shared/shared.module';
import { storageMetaReducer } from '../../core/store-infrastructure/storage-metareducer';
import * as fromReducer from '../../ngrx/reducers/post.reducer';
import { StoreModule } from '@ngrx/store';
import { POSTS_CONFIG_TOKEN, POSTS_LOCAL_STORAGE_KEY, POSTS_STORAGE_KEYS } from './timeline.tokens';
import { StoreLocalStorageService } from '../../core/store-infrastructure/store-local-storage.service';
const timelineRoutes: Routes = [
    { path: "timeline/friends", component: TimelineFriendsComponent },
    { path: "timeline/images", component: TimelineImagesComponent },
    { path: "timeline/messages", component: TimelineMessagesComponent },
    { path: "timeline/videos", component: TimelineVideosComponent },
    { path: "timeline/nearby", component: TimelineNearbyComponent },
    { path: "timeline", component: TimelineComponent }
];
export function getPostsConfig(saveKeys: string[], localStorageKey: string, storageService: StoreLocalStorageService) {
    return { metaReducers: [storageMetaReducer(saveKeys, localStorageKey, storageService)] };
}
@NgModule({
    imports: [
        SharedModule,
        RouterModule.forChild(timelineRoutes),
        StoreModule.forFeature('posts', fromReducer.postReducer, POSTS_CONFIG_TOKEN)
    ],
    declarations: [
        TimelineFriendsComponent,
        TimelineImagesComponent,
        TimelineMessagesComponent,
        TimelineVideosComponent,
        TimelineNearbyComponent,
        TimelineSidebarComponent,
        TimelineComponent,
        TimelineCreatePostComponent,
        TimelinePostListComponent,
        TimelineChatOnlineComponent,
        TimelineFollowUserComponent
    ],
    providers: [
        StoreLocalStorageService,
        { provide: POSTS_LOCAL_STORAGE_KEY, useValue: '__posts_storage__' },
        { provide: POSTS_STORAGE_KEYS, useValue: ['posts', 'viewMode'] },
        {
            provide: POSTS_CONFIG_TOKEN,
            deps: [POSTS_STORAGE_KEYS, POSTS_LOCAL_STORAGE_KEY, StoreLocalStorageService],
            useFactory: getPostsConfig
        },
    ]
})
export class TimelineModule { }

timeline.module.ts adlı modülümün kodları ise bu şekildedir. Unutmadan söyleyeyim gerekli npm paketlerini kurmayı unutmayın. Tarayıcının local storage sisteminde __posts_storage__ isminde bir alan açılacaktır ve state’deki tüm verilerimiz orada yer alacaktır.

5.Adım Modülün Sisteme Eklenmesi

Oluşturmuş olduğumuz modülü kesinlikle app.module.ts adlı yani ana modüle eklenmesi gerekmektedir. Ana modülün içerisinde imports kısmına modülünüze ekleyiniz.

Posted in Angular, BilişimTaggs:

Bir Cevap Yazın