angular 5 State management using Redux and Behavior Subject - angular5

How do you do state management in angular 5 ? Between Redux and Behavior Subject which approach is better to follow and why.
Thanks.

Angular Service Layers: Redux, RxJs and Ngrx Store - When to Use a Store And Why?
ngrx sample example
Install the below packages:
npm install #ngrx/store --save
npm install #ngrx/effects --save
app.module.ts
import { appReducer } from './state/app.reducer';
import { StoreModule } from '#ngrx/store';
#NgModule({
declarations: [
...
],
imports: [
...
StoreModule.forRoot(appReducer)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
import { Component, OnInit } from '#angular/core';
import { Store } from '#ngrx/store';
import { AppState } from './state/app.state';
import { SetAppTitleAction, getAppTitle } from './state/app-title';
import { Observable } from 'rxjs';
#Component({
selector: 'app-root',
template:
`<h1>{{appTitle$ | async}}</h1>
`
})
export class AppComponent implements OnInit {
appTitle$: Observable<string>;
constructor(private store: Store<AppState>) {
this.appTitle$ = this.store.select(getAppTitle);
}
ngOnInit(): void {
this.store.dispatch(new SetAppTitleAction('Location: AppComponent'));
}
}
app-title.action.ts
import { Action } from '#ngrx/store';
export const SetAppTitle = 'Set App Title';
export class SetAppTitleAction implements Action {
readonly type = SetAppTitle;
constructor(public payload: string) {}
}
export type AppTitleActions = SetAppTitleAction;
app-title.reducer.ts
import { AppTitleActions, SetAppTitle } from './app-title.actions';
export function appTitleReducer(state: string, action: AppTitleActions) {
switch (action.type) {
case SetAppTitle:
return action.payload;
default: return state;
}
}
index.ts
export * from './app-title.actions';
import { AppState } from './app.state';
export const getState = (state: AppState) => state;
export const getAppTitle = createSelector(getState, state => state.appTitle);
app.reducer.ts
import { ActionReducerMap } from '#ngrx/store';
import { AppState } from './app.state';
import { appTitleReducer } from './app-title/app-title.reducer';
import { versionInfoReducer } from './version-info/version-info.reducer';
export const appReducer: ActionReducerMap<AppState> = {
appTitle: appTitleReducer
};
export interface AppState {
appTitle: string;
}

Related

Angular 13 running jest error "Unexpected value 'MatTimepickerModule' imported by the module 'DynamicTestModule'. Please add an #NgModule annotati."

I am getting the below error when running karma from angular using the command :
npm run test-headless
FAIL src/app/req-form-body/req-form-body.component.spec.ts (26.126 s)
● ReqFormBodyComponent › should create
**Unexpected value 'MatTimepickerModule' imported by the module 'DynamicTestModule'. Please add an #NgModule annotation.**
49 |
50 | beforeEach(() => {
> 51 | fixture = TestBed.createComponent(ReqFormBodyComponent);
| ^
52 | component = fixture.componentInstance;
53 | fixture.detectChanges();
54 | });
at verifySemanticsOfNgModuleDef (node_modules/#angular/core/fesm2015/core.mjs:23854:19)
at node_modules/#angular/core/fesm2015/core.mjs:23865:9
at Array.forEach (<anonymous>)
at verifySemanticsOfNgModuleDef (node_modules/#angular/core/fesm2015/core.mjs:23863:60)
at Function.get (node_modules/#angular/core/fesm2015/core.mjs:23825:21)
at R3TestBedCompiler.applyProviderOverridesToModule (node_modules/#angular/core/fesm2015/testing.mjs:1067:29)
at R3TestBedCompiler.compileTestModule (node_modules/#angular/core/fesm2015/testing.mjs:1315:14)
at R3TestBedCompiler.finalize (node_modules/#angular/core/fesm2015/testing.mjs:921:14)
at TestBedRender3.get testModuleRef [as testModuleRef] (node_modules/#angular/core/fesm2015/testing.mjs:1796:49)
at TestBedRender3.inject (node_modules/#angular/core/fesm2015/testing.mjs:1719:29)
at TestBedRender3.createComponent (node_modules/#angular/core/fesm2015/testing.mjs:1759:44)
at Function.createComponent (node_modules/#angular/core/fesm2015/testing.mjs:1617:37)
at src/app/req-form-body/req-form-body.component.spec.ts:51:23
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:409:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3803:43)
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:408:56)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:169:47)
at Object.wrappedFunc (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:4288:34)
//and here is my ReqFormBodyComponent.spec.ts file .
enter code hereimport { ComponentFixture, TestBed } from '#angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { MaterialModule } from './../../app/shared/material';
import { ReqFormBodyComponent } from './req-form-body.component';
import { MatTimepickerModule } from 'mat-timepicker';
import _moment from 'moment';
import { default as _rollupMoment } from 'moment';
const moment = _rollupMoment || _moment;
export const DATE_FORMATS = {
parse: {
dateInput: 'LL',
},
display: {
dateInput: 'DD-MMM-YYYY',
monthYearLabel: 'YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'YYYY',
},
};
describe('ReqFormBodyComponent', () => {
let component: ReqFormBodyComponent;
let fixture: ComponentFixture<ReqFormBodyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FormsModule, ReactiveFormsModule, MaterialModule, BrowserAnimationsModule, MatTimepickerModule],
declarations: [],
providers: [FormBuilder],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ReqFormBodyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
//corresponding component file (ReqFormBodyComponent.ts)
import { StepperSelectionEvent } from '#angular/cdk/stepper';
import { AfterViewInit, ChangeDetectionStrategy, Component,
ElementRef, OnInit, ViewChild } from '#angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from
'#angular/forms';
import { MatStepper } from '#angular/material/stepper';
import { MomentDateAdapter, MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '#angular/material-moment-adapter';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '#angular/material/core';
import _moment from 'moment';
import { default as _rollupMoment } from 'moment';
const moment = _rollupMoment || _moment;
export const DATE_FORMATS = {
parse: {
dateInput: 'LL',
},
display: {
dateInput: 'DD-MMM-YYYY',
monthYearLabel: 'YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'YYYY',
},
};
#Component({
selector: 'ereq-req-form-body',
templateUrl: './req-form-body.component.html',
styleUrls: ['./req-form-body.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
// `MomentDateAdapter` can be automatically provided by importing `MomentDateModule` in your
// application's root module. We provide it at the component level here, due to limitations of
// our example generation script.
{ provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE, MAT_MOMENT_DATE_ADAPTER_OPTIONS] },
{ provide: MAT_DATE_FORMATS, useValue: DATE_FORMATS },
],
})
export class ReqFormBodyComponent implements OnInit, AfterViewInit {
#ViewChild('stepper') public stepper: MatStepper;
public isLinear = false;
public totalStepCount: number;
public selectedStep: string;
public firstFormGroup: FormGroup;
public secondFormGroup: FormGroup;
public thirdFormGroup: FormGroup;
public fourthFormGroup: FormGroup;
public fifthFormGroup: FormGroup;
public sixthFormGroup: FormGroup;
public lastFormGroup: FormGroup;
public date = new FormControl(moment());
public defaultValue = { hour: 13, minute: 30 };
constructor(private formBuilder: FormBuilder) {}
public ngOnInit(): void {...
Your angular13 TestBed did not find an ivy compatible library to load by jest.
You can work around this by
Step: make sure you have the correct ngcc build in the package's
node_module folder.
Step: Add a moduleMapper for jest for the built
package

Unable to use Vuex Store in Nuxt layouts

I am trying to use Vuex Store in my layout but can't figure out how to make it work.
Here is what I am doing:
store/sources.ts
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
import { store } from '#/store'
import axios from 'axios'
const { sourcesEndpoint } = process.env
interface Source {
id: String
}
#Module({
name: 'sources',
namespaced: true,
stateFactory: true,
dynamic: true,
store,
})
export default class Sources extends VuexModule {
private _sources: Source[] = []
get sources(): Source[] {
return this._sources
}
#Mutation
updateSources(sources: Source[]) {
this._sources = sources
}
#Action({ commit: 'updateSources' })
async fetchSources() {
// eslint-disable-next-line no-console
console.log(`!!! ${sourcesEndpoint} !!!`)
return sourcesEndpoint ? await axios.get(sourcesEndpoint) : []
}
}
store/index.ts
import { Store } from 'vuex'
export const store = new Store({})
layouts/default.vue
<script>
import { getModule } from 'vuex-module-decorators'
import Sources from '#/store/sources'
export default {
fetch() {
const sourcesModule = getModule(Sources, this.$store)
sourcesModule.fetchSources()
},
fetchOnServer: false,
}
</script>
And the error I get:
[vuex] must call Vue.use(Vuex) before creating a store instance.
You need to add Vue.use(Vuex), also, you are not including your module in the main store
import { Store } from 'vuex'
import { Sources } from './sources'
Vue.use(Vuex)
export const store = new Store({
modules: {
Sources
}
})

Ionic5: setting a footer on all app pages with a sharedModule and controlling it (on/off) from the app.component

I've created a new app using Ionic5 with a menu. I'm trying to use a Footer on multiple pages (now only on Home page). First I've created a SharedModule and imported in the imports' array of the app.module.ts. I've added the footer component in the declarations' and exports' array of the shared.module.ts. Also I added SharedModule in the imports' array of each page.module.ts, and finally adding <app-footer> in each page.html.
It works as expected, showing the footer in all pages. But now I need to control (on/off) this footer from my app.component, in response to a specific event, for example, when internet is not available (this part is not a problem).
footer.component.ts
import { Component, OnInit } from '#angular/core';
import { FooterService } from 'src/app/footer.service';
#Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent implements OnInit {
public FooterEnabled: boolean= false;
constructor() { }
ngOnInit() {
}
}
The FooterEnabled variable control if the footer is showed or not and must be modifiable from the app.component
footer.component.html
<div class="footer-conn" *ngIf="FooterEnabled">
Alert!
</div>
sharedfooter.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FooterComponent } from '../components/footer/footer.component';
#NgModule({
declarations: [FooterComponent],
imports: [
CommonModule
],
exports: [
FooterComponent, CommonModule
]
})
export class SharedFooterModule { }
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { Network } from '#ionic-native/network/ngx';
import { SharedFooterModule } from './shared/sharedfooter.module';
#NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
SharedFooterModule
],
providers: [
StatusBar,
SplashScreen,
Network,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.ts
import { Component, OnInit } from '#angular/core';
import { Platform } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { Network } from '#ionic-native/network/ngx';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent implements OnInit {
public selectedIndex = 0;
public appPages = [
{
title: 'Página 1',
url: 'home',
icon: 'mail'
},
{
title: 'Página 2',
url: 'pagina2',
icon: 'paper-plane'
},
{
title: 'Página 3',
url: 'pagina3',
icon: 'heart'
}
];
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private network: Network
) {
this.initializeApp();
}// Fin constructor
no_internet() {
alert("No internet!")
// In this point make FooterEnabled = true (from the footer component)
}
si_internet() {
alert("Whith internet!")
//In this point make FooterEnabled = false (from the footer component)
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
ngOnInit() {
let disconnectSubscription = this.network.onDisconnect().subscribe(() => {
setTimeout(() => {
if (this.network.type !== 'none') {
this.si_internet();
}
else {
this.no_internet();
}
}, 1000);
});
// watch network for a connection
let connectSubscription = this.network.onConnect().subscribe(() => {
setTimeout(() => {
if (this.network.type !== 'none') {
this.si_internet();
}
else {
this.no_internet();
}
}, 3000);
});
const path = window.location.pathname.split('/')[1];
if (path !== undefined) {
this.selectedIndex = this.appPages.findIndex(page => page.title.toLowerCase() === path.toLowerCase());
}
}
}
home.module.ts (as an example)
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { IonicModule } from '#ionic/angular';
import { HomePageRoutingModule } from './home-routing.module';
import { HomePage } from './home.page';
import { SharedFooterModule } from '../shared/sharedfooter.module';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
HomePageRoutingModule,
SharedFooterModule
],
declarations: [HomePage]
})
export class HomePageModule {}
I've tried with a service imported in the footer.component and app.component.ts that implements observables, but it didn't work. I will appreciate your contributions!!
maybe you can do it like bellow, add input to your footer component :
footer.component.ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent implements OnInit {
#Input() FooterEnabled : string;
//public FooterEnabled: boolean= false;
constructor() { }
ngOnInit() {
}
}
footer.component.ts
<div class="footer-conn" *ngIf="FooterEnabled == 'on'">
Alert!
</div>
on your app.component.ts add new varibale status:
no_internet() {
alert("No internet!")
this.status = 'on'
}
si_internet() {
alert("Whith internet!")
this.status = 'off'
}
put it on app.component.html as :
<app-footer FooterEnabled="status" ></app-footer>

Ionic4: Phonegap-nfc: where is the content?

VERSION: Ionic 4
Plugin: phonegap-nfc
Hi everyone!
I'm trying to use this plugin (https://ionicframework.com/docs/native/nfc) and following the instructions, I should be able to read a message sent from another NFC device. The event is fired and I know something is sent, but I'm not able to understand what and where the message is stored.
This is the code:
home.page.ts
import { Component } from '#angular/core';
import {Ndef, NFC} from '#ionic-native/nfc/ngx';
import {AlertController} from '#ionic/angular';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(private nfc: NFC, private ndef: Ndef, private alertController: AlertController) { }
readNFC() {
this.nfc.addNdefListener(() => {
this.presentAlert('ok');
}, (err) => {
this.presentAlert('ko' + err);
}).subscribe((event) => {
console.log(event);
console.log(JSON.stringify(event));
this.presentAlert('Il messaggio contiene' + event.tag + ' ' + this.nfc.bytesToHexString(event.tag.id));
});
}
writeNFC() {
this.nfc.addNdefListener(() => {
console.log('successfully attached ndef listener');
const message = this.ndef.textRecord('Hello world');
this.nfc.share([message]).then(
value => {
this.presentAlert('ok');
}
).catch(
reason => {
this.presentAlert('ko');
}
);
}, (err) => {
this.presentAlert('ko' + err);
});
}
async presentAlert(mess) {
const alert = await this.alertController.create({
header: 'attenzione',
message: mess,
buttons: ['OK']
});
await alert.present();
}
}
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import {Ndef, NFC} from '#ionic-native/nfc/ngx';
#NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
providers: [
StatusBar,
SplashScreen,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
NFC,
Ndef
],
bootstrap: [AppComponent]
})
export class AppModule {}
This is the stringify obtained by printing the content of event:
{"isTrusted":false,"tag":{"id":[0],"techTypes":["android.nfc.tech.Ndef"],"type":"android.ndef.unknown","maxSize":0,"isWritable":false,"ndefMessage":[{"tnf":1,"type":[85],"id":[],"payload":[3,112,108,97,121,46,103,111,111,103,108,101,46,99,111,109,47,115,116,111,114,101,47,97,112,112,115,47,100,101,116,97,105,108,115,63,105,100,61,99,111,109,46,119,97,107,100,101,118,46,119,100,110,102,99,38,102,101,97,116,117,114,101,61,98,101,97,109]},{"tnf":4,"type":[97,110,100,114,111,105,100,46,99,111,109,58,112,107,103],"id":[],"payload":[99,111,109,46,119,97,107,100,101,118,46,119,100,110,102,99]}],"canMakeReadOnly":false}}
Thanks in advance for your help!
Your data is at
ndefMessage[0].payload
you need to use
nfc.bytesToString()
the payload to convert to string

NullInjectorError: No provider for ReducerManager on angular spec file testing

I am testing ngrx/store based component. on test getting NullInjectorError: No provider for ReducerManager
Error message:
StaticInjectorError(Platform: core)[StoreFeatureModule -> ReducerManager]:
NullInjectorError: No provider for ReducerManager!
Error: StaticInjectorError(DynamicTestModule)[StoreFeatureModule -> ReducerManager]:
StaticInjectorError(Platform: core)[StoreFeatureModule -> ReducerManager]:
NullInjectorError: No provider for ReducerManager!
How to fix this? here is my test spec file:
import { HeaderNavShellComponent } from './header-nav-shell.component';
import { HeaderComponent } from './../../header/header.component';
import { HeaderNavComponent } from './../../components/header-nav/header-nav.component';
import { StoreModule, Store } from '#ngrx/store';
import { TranslateFakeLoader,TranslateLoader,TranslateModule,TranslateService, TranslateStore } from '#ngx-translate/core';
import { RouterTestingModule } from '#angular/router/testing';
import { reducerShared } from "./../../state/reducers/shared.reducer";
import { HttpClientModule, HttpClient } from '#angular/common/http';
import { of } from 'rxjs';
import * as fromRoot from "./../../../calendar/state";
// import { reducerCalendar } from "./../../../calendar/state/calendar.reducer";
describe('HeaderNavShellComponent', () => {
let component: HeaderNavShellComponent;
let fixture: ComponentFixture<HeaderNavShellComponent>;
let dispatchSpy;
let store:Store<fromRoot.NewState>
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeaderNavShellComponent, HeaderComponent, HeaderNavComponent ],
imports:[
HttpClientModule,
StoreModule.forFeature("shared", reducerShared ),
RouterTestingModule,
TranslateModule.forChild({
          loader: {
            provide: TranslateLoader,
            useClass: TranslateFakeLoader
          }
         })
],
providers:[TranslateService, TranslateStore, HttpClient ]
})
.compileComponents();
}));
beforeEach(fakeAsync(() => {
store = TestBed.get(Store);
spyOn(store, 'dispatch').and.callThrough();
fixture = TestBed.createComponent(HeaderNavShellComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', fakeAsync(() => {
expect(component).toBeTruthy();
}));
});
here is the ts file:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { Store, select } from "#ngrx/store";
import { Observable } from "rxjs";
import { ModelEvent, EventState } from "./../../../calendar/models/model.event";
import { ModelLang, ModelLonguage } from "./../../../shared-components/models/models";
import { CalendarActions, Load, EventSelected } from "./../../../calendar/state/calendar.actions";
import * as fromRoot from "./../../../calendar/state";
import * as fromObservables from "./../../state";
import { Lang, LoadLang } from "./../../state/actions/shared.actions";
import { ShowNavi } from "./../../../shared-components/state/actions/shared.actions";
#Component({
selector: 'header-nav-shell',
templateUrl: './header-nav-shell.component.html',
styleUrls: ['./header-nav-shell.component.scss']
})
export class HeaderNavShellComponent implements OnInit {
eventList$:Observable<ModelEvent[]>;
eventListSize$:number;
currentLang$:Observable<string>;
langList$:Observable<ModelLonguage[]>;
constructor(private store:Store<fromRoot.NewState>, private router:Router) { }
ngOnInit() {
this.store.dispatch(new Load());
this.store.dispatch( new LoadLang());
this.eventList$ = this.store.pipe(select(fromRoot.getEvents));
this.currentLang$ = this.store.pipe(select(fromObservables.getCurrentLang));
this.langList$ = this.store.pipe(select(fromObservables.getLangList));
}
eventSelected(event) {
this.store.dispatch(new EventSelected(event));
this.router.navigateByUrl("iboCalendar");
}
langChanged(event) {
this.store.dispatch( new Lang(event.Name));
}
leftNaviHandler(event):void {
this.store.dispatch(new ShowNavi(event));
}
}
Thanks in advance