Skip to content
Snippets Groups Projects
Commit adf101a2 authored by Jimmy Hendrie's avatar Jimmy Hendrie
Browse files

implemented NGRX
parent 46c0d2f0
No related branches found
No related tags found
1 merge request!16AT2-1366 NGRX implemented
Pipeline #555105738 passed
Showing
with 151 additions and 28 deletions
......@@ -4,7 +4,7 @@ import { AppComponent } from './app.component';
import { TranslationService } from './shared/services/translation.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TranslateModule } from '@ngx-translate/core';
import { provideMockStore } from '@ngrx/store/testing';
describe('AppComponent', () => {
let transService: MockTranslationService;
beforeEach(async () => {
......@@ -16,7 +16,10 @@ describe('AppComponent', () => {
TranslateModule.forRoot(),
],
declarations: [AppComponent],
providers: [{ provide: TranslationService, useValue: transService }],
providers: [
{ provide: TranslationService, useValue: transService },
provideMockStore({}),
],
}).compileComponents();
});
......
import { Component } from '@angular/core';
import { AppConfigService } from './shared/services/app-config.service';
import { Component, OnInit } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { SharedService } from './shared/services/shared.service';
import { Store } from '@ngrx/store';
import { appLoaded } from './shared/state/config/config.actions';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
export class AppComponent implements OnInit {
static readonly DARK_THEME_CLASS = 'dark-theme';
static readonly LOCAL_STORAGE_TITLE_THEME = 'skao-theme';
constructor(
private configuration: AppConfigService,
private iconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer,
private sharedService: SharedService
private sharedService: SharedService,
private store: Store
) {
this.checkThemePreference();
this.registerMatIcons();
......@@ -25,6 +26,10 @@ export class AppComponent {
this.sharedService.darkMode.subscribe(() => this.selectDarkTheme());
}
ngOnInit(): void {
this.store.dispatch(appLoaded());
}
checkThemePreference(): void {
const currentTheme = localStorage.getItem(
AppComponent.LOCAL_STORAGE_TITLE_THEME
......@@ -47,10 +52,6 @@ export class AppComponent {
document.documentElement.classList.remove(AppComponent.DARK_THEME_CLASS);
}
getConfiguration() {
return this.configuration.getConfiguration();
}
registerMatIcons() {
this.iconRegistry.addSvgIcon(
'en',
......
import { HttpClient } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LayoutModule } from './layout/layout.module';
import { SharedModule } from './shared/shared.module';
import { metaReducers, reducers } from './shared/state/core.reducer';
// required for AOT compilation
export function HttpLoaderFactory(http: HttpClient) {
......@@ -29,6 +33,13 @@ export function HttpLoaderFactory(http: HttpClient) {
}),
SharedModule,
LayoutModule,
StoreModule.forRoot(reducers, {
metaReducers,
}),
StoreDevtoolsModule.instrument({
maxAge: 25,
logOnly: environment.production,
}),
],
bootstrap: [AppComponent],
})
......
......@@ -6,6 +6,7 @@ import { TranslationService } from '../services/translation.service';
import { TranslateModule } from '@ngx-translate/core';
import * as config from '../../../assets/configuration/configuration.json';
import { MatMenuModule } from '@angular/material/menu';
import { provideMockStore } from '@ngrx/store/testing';
describe('HeaderComponent', () => {
let component: HeaderComponent;
......@@ -20,6 +21,7 @@ describe('HeaderComponent', () => {
providers: [
AppConfigService,
{ provide: TranslationService, useValue: transService },
provideMockStore({}),
],
}).compileComponents();
});
......
import { Component, OnInit } from '@angular/core';
import { Languages } from '../models/language.models';
import { AppComponent } from '../../app.component';
import { AppConfigService } from '../services/app-config.service';
import { TranslationService } from '../services/translation.service';
import { SharedService } from '../services/shared.service';
import { Store } from '@ngrx/store';
import { selectConfig } from '../state/core.state';
@Component({
selector: 'app-header',
......@@ -20,12 +21,14 @@ export class HeaderComponent implements OnInit {
constructor(
private translate: TranslationService,
private configuration: AppConfigService,
private sharedService: SharedService
private sharedService: SharedService,
private store: Store
) {}
ngOnInit(): void {
this.configurationVal = this.configuration.getConfiguration();
this.store.select(selectConfig).subscribe((data) => {
this.configurationVal = data;
});
this.languages = this.getLanguages();
this.checkLanguagePreference();
}
......
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppConfig } from '../models/configuration.models';
import {
configLoadInitiated,
configLoadSubmitted,
} from '../state/config/config.actions';
@Injectable({
providedIn: 'root',
})
export class AppConfigService {
private configuration!: AppConfig;
constructor(private http: HttpClient) {}
constructor(private http: HttpClient, private store: Store) {}
//developers can choose to improve this to load configuration from an external service and then fallback to local configuration file
loadConfiguration() {
this.store.dispatch(configLoadInitiated());
return new Promise((resolve) => {
this.http
.get<AppConfig>('/assets/configuration/configuration.json')
.subscribe((response) => {
this.configuration = response;
.subscribe((response: AppConfig) => {
this.store.dispatch(configLoadSubmitted({ config: response }));
resolve(true);
});
});
}
getConfiguration(): AppConfig | null {
if (this.configuration) {
return this.configuration;
}
return null;
}
}
// src/app/core/state/menus/menus.actions.ts
import { createAction, props } from '@ngrx/store';
import { AppConfig } from '../../models/configuration.models';
export const appLoaded = createAction('[App] App Loaded');
export const configLoadSubmitted = createAction(
'[Load Configuration] Config Load Submitted',
props<{ config: AppConfig }>()
);
export const configLoadInitiated = createAction(
'[Load Configuration] Config Load Initiated'
);
export const configLoadSuccess = createAction(
'[Configuration API] Config Load Success',
props<{ config: AppConfig }>()
);
export const configLoadFailed = createAction(
'[Configuration API] Config Load Failed',
props<{ error: any }>()
);
import { Action, createReducer, on } from '@ngrx/store';
import { ConfigState, initialState } from './config.state';
import * as ConfigActions from './config.actions';
const configReducer = createReducer(
initialState,
on(ConfigActions.configLoadSubmitted, (state, { config }) => ({
...state,
config: config,
}))
);
export function reducer(state: ConfigState | undefined, action: Action) {
return configReducer(state, action);
}
import { AppConfig } from '../../models/configuration.models';
export interface ConfigState {
config: AppConfig | null;
}
export const initialState: ConfigState = {
config: null,
};
export * from './config.state';
export * from './config.actions';
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
import { State } from './core.state';
import * as ConfigReducer from './config/config.reducer';
export const reducers: ActionReducerMap<State> = {
configState: ConfigReducer.reducer,
};
export const metaReducers: MetaReducer<State>[] = [];
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { ConfigState } from './config/config.state';
export interface State {
configState: ConfigState;
}
const featureState = createFeatureSelector<ConfigState>('configState');
export const selectConfig = createSelector(
featureState,
(state) => state.config
);
export * from './core.state';
export * from './core.reducer';
......@@ -1698,6 +1698,27 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@ngrx/effects@^13.2.0":
version "13.2.0"
resolved "https://registry.yarnpkg.com/@ngrx/effects/-/effects-13.2.0.tgz#36aba5af45ac034419509f799ed70acdb80228b1"
integrity sha512-HmWggpl3xGQFfUzON/uel5jSyUWsrGZsR5qR/oFLGjPRWzwKfdHrl0OcBl5IhFgFxT74cAi9F4JTICUytGRbFA==
dependencies:
tslib "^2.0.0"
"@ngrx/store-devtools@^13.2.0":
version "13.2.0"
resolved "https://registry.yarnpkg.com/@ngrx/store-devtools/-/store-devtools-13.2.0.tgz#69193e32aee141397d7d2c11c43278e3fa5191e7"
integrity sha512-k1NifkR/4OjbjAxauVZODCsgs2owMJXvEX2XoTWth7zscbHE8L3pLd0k1ox5pMPUEqWIptWTaJDzYqnQSoJaaw==
dependencies:
tslib "^2.0.0"
"@ngrx/store@^13.2.0":
version "13.2.0"
resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-13.2.0.tgz#43d4e9bf064808dd546ef1edbbcd08c2a8e0871a"
integrity sha512-3wlGMkfe0EXsiS6E6W0wCksuGapa5Z6JVFvKQMHFpXZ3XeixXKlULnemlcdMT7Yrnry+CGOtRHqkmKxLoQzhTw==
dependencies:
tslib "^2.0.0"
"@ngtools/webpack@13.2.5":
version "13.2.5"
resolved "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.5.tgz"
......@@ -2455,6 +2476,11 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0:
resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
add@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/add/-/add-2.0.6.tgz#248f0a9f6e5a528ef2295dbeec30532130ae2235"
integrity sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==
adjust-sourcemap-loader@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment