Why the state of my app does not change in my angular app using NGRX? - ngrx

I'm building a 'hello world' app using NGRX, but the state of my app keeps the initial value even when I'm triggering actions that should change it.
This is my app.module:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { StoreModule } from '#ngrx/store';
import { StoreDevtoolsModule } from '#ngrx/store-devtools';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { counterReducer } from './contador.reducer';
import { environment } from 'src/environments/environment';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({ counter: counterReducer }),
StoreDevtoolsModule.instrument({
maxAge: 25,
logOnly: environment.production,
})
],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts:
import { Component, OnInit } from '#angular/core';
import { Store } from '#ngrx/store';
import { decrement, increment } from './contador/contador.actions';
interface AppState {
counter: number
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
public counter!: number;
constructor(private store: Store<AppState>) {
this.store.subscribe(state => {
console.log('state', state)
this.counter = state.counter;
})
}
ngOnInit() { }
inc() {
this.store.dispatch(increment())
}
dec() {
this.store.dispatch(decrement())
}
}
app.component.html:
<div class="container-fluid text-center">
<div class="row">
<div class="col pt-5">
<h1>Counter</h1>
<h2>{{counter}}</h2>
</div>
</div>
<button (click)="inc()" class="btn btn-primary pr-5">increment</button>
<button (click)="dec()" class="btn btn-primary pr-5">decrement</button>
</div>
counter.reducer.ts:
import { decrement, increment } from './contador/contador.actions';
import { Action, createReducer, on } from "#ngrx/store";
export const initialState = 20;
const _counterReducer = createReducer(initialState,
on(increment => initialState + 1),
on(decrement => initialState - 1)
)
export function counterReducer(initialState: number | undefined, actions: Action) {
return _counterReducer(initialState, actions)
}
contador.actions.ts
import { createAction } from "#ngrx/store"
export const increment = createAction('[counter] increment')
export const decrement = createAction('[counter] decrement')
The only thing I'm seeing is that in the reducer, the increment and decrement imports are not being used in the on() methods as they are unused imports.
I always get the initialValue = 0, no matter if I click the increment or decrement buttons.

The on cases in you reducer are not defined correctly.
The first argument is the action, second argument is a function which takes the existing state, then returns a modified version of it according to the specified action.
const _counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1)
);

Related

Routing edit/id in asp.net core application with angular not working

I am learning building application using angular and asp.net core using these videos on this link. Everything works fine except the edit of a component. If I give a URL like below, it goes to the route for error in app-routing.module.ts even though there's no error in the browser console log.
http://localhost:4200/genres/edit/1
The app-routing.model.ts is as below
import { Component, NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { CreateActorComponent } from './actors/create-actor/create-actor.component';
import { EditActorComponent } from './actors/edit-actor/edit-actor.component';
import { IndexActorsComponent } from './actors/index-actors/index-actors.component';
import { CreateGenreComponent } from './genres/create-genre/create-genre.component';
import { EditGenreComponent } from './genres/edit-genre/edit-genre.component';
import { IndexGenresComponent } from './genres/index-genres/index-genres.component';
import { HomeComponent } from './home/home.component';
import { CreateMovieTheaterComponent } from './movie-theaters/create-movie-theater/create-movie-theater.component';
import { EditMovieTheaterComponent } from './movie-theaters/edit-movie-theater/edit-movie-theater.component';
import { IndexMovieTheaterComponent } from './movie-theaters/index-movie-theater/index-movie-theater.component';
import { CreateMovieComponent } from './movies/create-movie/create-movie.component';
import { EditMovieComponent } from './movies/edit-movie/edit-movie.component';
const routes: Routes = [
{path:' ', component:HomeComponent},
{path:'genres', component:IndexGenresComponent},
{path:'genres/create', component:CreateGenreComponent},
{path:'genres/edit:id', component:EditGenreComponent},
{path:'actors', component:IndexActorsComponent},
{path:'actors/create', component:CreateActorComponent},
{path:'actors/edit:id', component:EditActorComponent},
{path:'movietheaters', component:IndexMovieTheaterComponent},
{path:'movietheaters/create', component:CreateMovieTheaterComponent},
{path:'movietheaters/edit:id', component:EditMovieTheaterComponent},
{path:'movies/create', component:CreateMovieComponent},
{path:'movies/edit:id', component:EditMovieComponent},
// {path:'**',component:HomeComponent}
{path:'**',redirectTo:' '}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
edit-genre.component.ts is
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { genreCreationDTO } from '../genres.model';
#Component({
selector: 'app-edit-genre',
templateUrl: './edit-genre.component.html',
styleUrls: ['./edit-genre.component.css']
})
export class EditGenreComponent implements OnInit {
constructor(private activatedRoute:ActivatedRoute) { }
model: genreCreationDTO={name:"Drama"};
ngOnInit(): void {
this.activatedRoute.params.subscribe(params=>{
});
}
//Event emiited value passed from child form-genre-creation to parent create-genre.component
//to be displayed
saveChanges(genreCreationDTO:genreCreationDTO)
{
}
}
edit-genre.component.html is
<h2>Edit Genre</h2>
<app-form-genre [model]="model" (onSaveChanges)="saveChanges($event)"></app-form-genre>
form-genre.component.html is
<form (submit)="saveChanges()" [formGroup]="form">
<mat-form-field appearance="outline">
<mat-label>Name*</mat-label>
<input matInput formControlName="name">
<mat-error *ngIf="form.invalid">{{getErrorMessageFieldName()}}</mat-error>
</mat-form-field>
<div>
<button mat-flat-button color="primary" [disabled]="form.invalid">Save Changes</button>
<a mat-stroked-button routerLink="/genres">Cancel</a>
</div>
form-genre.component.ts is
import { Component, OnInit, Output,EventEmitter, Input } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { firstLetterUppercase } from 'src/app/validators/firstLetterUppercase';
import { genreCreationDTO } from '../genres.model';
#Component({
selector: 'app-form-genre',
templateUrl: './form-genre.component.html',
styleUrls: ['./form-genre.component.css']
})
export class FormGenreComponent implements OnInit {
constructor(private router: Router, private formBuilder:FormBuilder) { }
#Input()
model!: genreCreationDTO;
//Event Emitter
#Output()
onSaveChanges: EventEmitter<genreCreationDTO>=new EventEmitter<genreCreationDTO>();
form!: FormGroup;
ngOnInit(): void {
this.form= this.formBuilder.group({
name:['',[Validators.required, Validators.minLength(3),firstLetterUppercase()]]
});
if(this.model!==undefined){
this.form.patchValue(this.model);
}
}
getErrorMessageFieldName()
{
const field= this.form.get("name");
if(field?.hasError("required"))
{
return "The name field is required";
}
if(field?.hasError("minLength")){
return "The minimum length is 3"
}
if(field?.hasError('firstLetterUppercase')){
return field.getError('firstLetterUppercase').message;
}
return '';
}
saveChanges()
{
//Emit value from child form-genre.compnent to parent create-genre.component
this.onSaveChanges.emit(this.form.value);
// this.router.navigate(['/genres']);
}
}
I am new to .net core framework & angular and i'm using forms(chapter sharing forms) in the video tutorial by Felipe galivan. Everytime I give an edit/id url for a component, it keeps using this
{path:'**',redirectTo:' '}
in the app-routing.module.ts as if say genres/edit/id has no match. I'm doing exactly as Felipe in the video, mine keeps redirecting me to home page instead of genres/edit. Why is this happening?

Angular 8 - formControlName inside Kendo TextArea component

I am trying to use formControlName inside a Kendo Text Area, and have a outer component apply it.
Using the following code base link, its still not working.
Angular 2 - formControlName inside component
How would someone fix this?
InputText.ts
export class InputTextComponent implements AfterViewInit, ControlValueAccessor {
#Input() disabled: boolean;
#Output() saveValue = new EventEmitter();
value: string;
onChange: () => void;
onTouched: () => void;
writeValue(value: any) {
this.value = value ? value : "";
}
registerOnChange(fn: any) {this.onChange = fn}
registerOnTouched(fn: any) {this.onTouched = fn}
setDisabledState(isDisabled) {this.disabled = isDisabled}
}
InputText.html
<input kendoTextBox />
Not sure this is what you are asking but in order to use formControlName inside a custom component here is what you do
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { InputTextComponent } from './input-text';
#NgModule({
declarations: [
AppComponent,
InputTextComponent
],
imports: [
FormsModule,
ReactiveFormsModule,
BrowserModule,
],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.html
<form [formGroup]="details">
<input-text formControlName="name"></input-text>
<!-- This should show the name as you change it in your custom control -->
{{details.value | json}}
</form>
app.component.ts
import { Component } from '#angular/core';
import { FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
public details: FormGroup;
constructor(
) {
this.details = new FormGroup({
name: new FormControl("name")
});
}
}
input-text.component.html
<input kendoTextBox [(ngModel)]="value" (ngModelChange)="onChange($event)" />
input-text.component.ts
import { Component, forwardRef } from "#angular/core";
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from "#angular/forms";
#Component({
selector: "input-text",
templateUrl: "./input-text.html",
styleUrls: ["./input-text.scss"],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputTextComponent),
multi: true
}
]
})
export class InputTextComponent implements ControlValueAccessor {
public value: string;
propagateChange = (value: string) => {};
writeValue(obj: any): void {
this.value = obj;
}
registerOnTouched(fn: any): void {}
registerOnChange(fn) {
this.propagateChange = fn;
}
onChange(newValue){
this.propagateChange(newValue);
}
}

IONIC 4 - The event ionViewWillEnter only fires once

Please, I am building a Sidemenu Ionic app with firebase firestore, when it opens, the app shows my collections and data, but when browsing between pages with sidemenu and returning to the data page, there is no data return nothing, I need to refresh a browser page to see the data again. I already checked the life cycle and the ionViewWillEnter the event only fires once.
import { Component, OnInit } from '#angular/core';
import { Evento, EventoService} from 'src/app/services/evento.service';
import { Router, RouterEvent } from '#angular/router';
import { LoadingController, NavController } from '#ionic/angular';
#Component({
selector: 'app-eventos',
templateUrl: './eventos.page.html',
styleUrls: ['./eventos.page.scss'],
})
export class EventosPage{
eventos: Evento[];
pages = [
{
url: '/menu/eventos',
}
];
selectedPath = '';
constructor(private eventoService: EventoService, private router: Router,
private loadingController: LoadingController, private navCtrl: NavController) {
this.navCtrl.setDirection('root');
}
ionViewWillEnter(){
console.log(' Teste 3: ionViewWillEnter ')
this.eventoService.getAllEventos().subscribe(res => {
this.eventos = res;
let dateString = 'item.data';
let newDate = new Date(dateString);
this.loadEvento();
});
}
async loadEvento() {
const loading = await this.loadingController.create({
message: 'Carregando eventos...',
spinner: 'crescent',
duration: 300
});
return await loading.present();
}
}
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>Eventos</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-card class="card" *ngFor="let item of eventos" lines="inset" button [routerLink]="['/detailsEvento', item.id]"
routerdirection="forward">
<ion-card-header>
<ion-card-title class="texto"> {{item.nome}}</ion-card-title>
<ion-card-title class="texto"> Data: {{item.data | date:'dd/MM/yyyy'}}</ion-card-title>
</ion-card-header>
</ion-card>
</ion-content>
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { Routes, RouterModule } from '#angular/router';
import { IonicModule } from '#ionic/angular';
import { EventosPage } from './eventos.page';
import { FontAwesomeModule } from '#fortawesome/angular-fontawesome';
const routes: Routes = [
{
path: '',
component: EventosPage
}
];
#NgModule({
imports: [
FontAwesomeModule,
CommonModule,
FormsModule,
IonicModule,
RouterModule.forChild(routes)
],
declarations: [EventosPage]
})
export class EventosPageModule {}
import { Injectable } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Evento {
nome: string;
data: string;
horario: string;
local: string;
descricao: string;
}
#Injectable({
providedIn: 'root'
})
export class EventoService {
private eventosCollection: AngularFirestoreCollection<Evento>;
private eventos: Observable<Evento[]>;
constructor(db: AngularFirestore) {
this.eventosCollection = db.collection<Evento>('eventos', ref => ref.orderBy('data'));
this.eventos = this.eventosCollection.snapshotChanges().pipe(
map(actions => {
return actions.map(a => {
const data = a.payload.doc.data();
const id = a.payload.doc.id;
return{ id, ...data };
});
})
);
}
getAllEventos(){
return this.eventos;
}
getEvento(id){
return this.eventosCollection.doc<Evento>(id).valueChanges();
}
}
constructor(
private afStore: AngularFirestore,
private router: Router,
) {
this.router.events.subscribe((evt) => {
if (evt instanceof NavigationEnd && this.router.url === '/main-screen/home')
{
this.storage.get('userInfo').then((val) => {
if (val) {
// console.log(val);
this.profesion = val.Profesion ? val.Profesion : 'popular';
if ( this.profesion === 'popular') {
this.allCollectionVideo();
} else {
this.getDefaultProfesion(this.profesion);
}
}
});
}
});
}
It can resolve your issue.

I need to display all the names I have written in the table in the Angular field

I need to display all the names I have written in the table in the Angular field.
I have DB:
CREATE TABLE [dbo].[Sellers](
[Id] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[Login] [varchar](50) NOT NULL,
[Password] [varchar](50) NOT NULL,
[Name] [nchar](50) NOT NULL,
[Address] [nchar](50) NOT NULL,
[Email] [varchar](50) NOT NULL,
[RegistrationDate] [datetime] NOT NULL,
)
GO
I am requesting ASP web API to bring all the names from the table:
public class SellersController : ApiController
{
[HttpGet]
public async Task<IHttpActionResult> GetNames(string name)
{
using (var dataBaseContext = new DataBaseContext())
{
var query = from seller in dataBaseContext.Sellers
where
seller.Name == name
select seller;
return Ok(await query.ToListAsync());
}
}
}
I'll bring it to the angular. But it is not displayed and generally cries out the code:
import { Sellers } from './sellers.model';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class SellersService {
formData : Sellers;
constructor() { }
}
________________
<div class="p-3 px-md-4 mb-3 bg-white">
<input name="Sellers" #Sellers="ngModel"[(ngModel)] = "service.formData" class="form-control">
</div>
Nothing is displayed and the last line of the code issues an error
Can't bind to 'ngModel' since it isn't a known property of 'input'.Angular
UPD:
import { SellersService } from './shared/sellers.service';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { HttpModule } from '#angular/http';
import { FormsModule } from '#angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TopRegionComponent } from './components/top-region/top-region.component';
import { LeftRegionComponent } from './components/left-region/left-region.component';
import { BottomRegionComponent } from './components/bottom-region/bottom-region.component';
import { CenterRegionComponent } from './components/center-region/center-region.component';
import { UserComponent } from './components/user/user.component';
const appRoutes: Routes = [
{path: '', component: AppComponent},
{path: 'user', component: UserComponent},
{path: 'left-region', component: LeftRegionComponent},
{path: 'top-region', component: TopRegionComponent},
{path: 'bottom-region', component: BottomRegionComponent},
{path: 'center-region', component: CenterRegionComponent},
]
#NgModule({
declarations: [
AppComponent,
TopRegionComponent,
LeftRegionComponent,
BottomRegionComponent,
CenterRegionComponent,
UserComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
RouterModule.forRoot(appRoutes),
HttpModule,
FormsModule
],
providers: [SellersService],
bootstrap: [AppComponent]
})
export class AppModule { }
UPD2:
import { SellersService } from './../../shared/sellers.service';
import { Component, OnInit } from '#angular/core';
import {Http} from '#angular/http';
import { FormsModule } from '#angular/forms';
#Component({
selector: 'left-region',
templateUrl: './left-region.component.html',
styleUrls: ['./left-region.component.css']
})
export class LeftRegionComponent implements OnInit {
constructor(private service : SellersService)
{
}
ngOnInit() {
}
}
I have given an example for your query kindly look into it.
import { Sellers } from './sellers.model';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class SellersService {
formData : Sellers;
seller : Sellers = new Sellers();
constructor() { }
}
HTML
<div class="p-3 px-md-4 mb-3 bg-white">
<input name="Sellers" #Sellers="ngModel" [(ngModel)]= "seller.Name" class="form-control">
</div>

Navigating between same components using Back/Forward browser buttons does not work

Hi I'm having trouble navigating with a browsers back/forward buttons. It only occurs when navigating between routes that share the same component via route parameters.
I have an application that lists lots of animals. Every time I navigate to example.com/animal/cat or example.com/animal/dog, a Http Get request is made to a restful Api which returns the relevant data. If I navigate to /animal/cat then to /animal/dog everything seems ok. The problem starts when I click on the browsers Back Button to go back to /animal/cat. Instead of loading the data for the cat, the URL changes but the data is still listed for the dog. I'm using router 3.0.0-beta.2 with RC4.
heres my animals details.component page:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ROUTER_DIRECTIVES, ActivatedRoute, Router } from '#angular/router';
import { Response } from '#angular/http';
import { Subscription } from 'rxjs/Rx';
import { DataService } from '../../data.service';
#Component({
moduleId: module.id,
selector: 'animal-details',
templateUrl: 'animal-details.component.html',
styleUrls: ['animal-details.component.css'],
directives:[ROUTER_DIRECTIVES]
})
export class AnimalDetailsComponent implements OnInit, OnDestroy {
constructor(private activatedRoute: ActivatedRoute, private dataService: DataService, private router: Router) {
}
private subscription: Subscription;
animal = {};
link: string;
table = 'animal/';
private url = '?related=family_by_link';
ngOnInit() {
this.subscription = this.activatedRoute.params.subscribe(
(param: any) => this.link = param['link']
);
this.dataService.getData(this.table, this.link, this.url)
.map((response: Response) => response.json())
.subscribe(
(data: any) => {this.animal = data},
err => { console.log('error404') }
);
};
nextData(){
this.dataService.getData(this.table, this.link, this.url)
.map((response: Response) => response.json())
.subscribe(
(data: any) => {this.animal = data},
err => { console.log('error404') }
);
window.scroll(0,0);
};
ngOnDestroy() {
this.subscription.unsubscribe();
};
}
I use the getData() method when navigating between animals on the same component. My AnimalComponentDetails html:
<div class="row">
<div class="col-sm-6 col-md-4 col-lg-3 text-xs-center" *ngFor="let family of animal.family_by_link" (click)="nextData()">
<a [routerLink]="['/animal', family.ani_link]">
<img src="/images/animal/{{family.ani_link}}.png">
<p>{{family.name}}</p>
</a>
</div>
Heres my DataService:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Subscription, Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map'
#Injectable()
export class DataService {
constructor(private dataService: Http) {
}
urlprefix = 'http://123.123.123.123/api/v2/_table/';
api = '&api_key=7201521drgdc71';
getData(table, link, url): Observable<any> {
return this.dataService.get(this.urlprefix + table + link + url +this.api);
}
}
my app.routes:
import { provideRouter } from '#angular/router';
import { AnimalComponent } from './animal/animal.component';
import { AnimalDetailsComponent } from './animal/animal-details/animal-details.component'
{ path: '', component: HomeComponent },
{ path: 'animal', component: AnimalComponent },
{ path: 'animal/:link', component: AnimalDetailsComponent },
{ path: '**', redirectedTo: 'error404', component: Error404Component }
];
export const APP_ROUTES_PROVIDER = [
provideRouter(APP_ROUTES)
];
And my boot:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from '#angular/core';
import { AppComponent, environment } from './app/';
import { HTTP_PROVIDERS } from '#angular/http';
import { APP_ROUTES_PROVIDER } from './app/app.routes'
if (environment.production) {
enableProdMode();
}
bootstrap(AppComponent, [APP_ROUTES_PROVIDER, HTTP_PROVIDERS]);
Chrome's Network indicates when I navigate back or forward on the same component between animal/:cat and animal/:dog via browser buttons, no new Http requests are made unless I change to a different component.
This is the last bug I'm down too. Would appreciate any help, thanks!

Resources