19 July 2019
Angular (34) -- NgRx
by Jerry Zhang
LeetCode Day 14: P121. Best time to buy and sell stock I (Easy)
题目
一个数组,每个数代表每天的股票价格。只许买卖一次,求最大的利润。
Example:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
我的思路:
动态规划。然后就放弃了。。。
看答案
class Solution {
public int maxProfit(int[] prices) {
int minimumPrice = Integer.MAX_VALUE;
int maximumProfit = 0;
for(int i = 0; i < prices.length; i++){
minimumPrice = Math.min(minimumPrice, prices[i]);
maximumProfit = Math.max(maximumProfit,(prices[i] - minimumPrice));
}
return maximumProfit;
}
}
class Solution {
public int maxProfit(int[] prices) {
int b = Integer.MIN_VALUE, s = 0;
for (int p: prices) {
int tb = b, ts = s;
b = Math.max(tb, -p);
s = Math.max(ts, tb + p);
}
return s;
}
}
每次更新最小值,更新最大利润。
Angular: Using NgRx in Auth Module
Tip: A new Reducer needs to be added in the app.module.ts file
Create a Reducer
Create a new folder called store including reducer and actions
Auth Reducer: (The auth actions file will be created later)
import {User} from '../user.model';
import * as AuthActions from './auth.actions';
export interface State {
user: User;
}
const initialState: State = {
user: null
};
export function authReducer(state = initialState, action: AuthActions.AuthActions) {
switch (action.type) {
case AuthActions.LOGIN:
const user = new User(
action.payload.email,
action.payload.userId,
action.payload.token,
action.payload.expirationDate);
return {
...state,
user: user
};
case AuthActions.LOGOUT:
return {
...state,
user: null
};
default: return state;
}
}
Add the reducer in the app.module
import * as fromApp from './store/app.reducer';
StoreModule.forRoot(fromApp.appReducer),
Union Reducers
Because we have multiple reducer now, we can create a new app.reducer.ts
that merges all the reducers together.
import * as fromShoppingList from '../shopping-list/store/shopping-list.reducer';
import * as fromAuth from '../auth/store/auth.reducer';
import {ActionReducerMap} from '@ngrx/store';
export interface AppState {
shoppingList: fromShoppingList.State;
auth: fromAuth.State;
}
export const appReducer: ActionReducerMap<AppState> = {
shoppingList: fromShoppingList.shoppingListReducer,
auth: fromAuth.authReducer
};
Then we need to replace all generic type of the store injection with Store<fromApp.AppState>
.
Create an auth.actions.ts
file.
import {Action} from '@ngrx/store';
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';
export class Login implements Action {
readonly type = LOGIN;
constructor(
public payload: {
email: string;
userId: string;
token: string;
expirationDate: Date;
}) {}
}
export class Logout implements Action {
readonly type = LOGOUT;
}
export type AuthActions = Login | Logout;
Use the store in the auth service file
Previously, we use this.user.next(loadedUser);
to signal the application that we have updated the user now.
Now, we dispatch an action with NgRx store.
import * as fromApp from '../store/app.reducer';
import * as AuthActions from './store/auth.actions';
constructor(
private http: HttpClient,
private router: Router,
private store: Store<fromApp.AppState>
) {}
this.store.dispatch(new AuthActions.Login({
email: loadedUser.email,
userId: loadedUser.id,
token: loadedUser.token,
expirationDate: new Date(userData._tokenExpirationDate)
}));
logout() {
// this.user.next(null);
this.store.dispatch(new AuthActions.Logout());
this.router.navigate(['/auth']);
localStorage.removeItem('userData');
if (this.tokenExpirationTimer) {
clearTimeout(this.tokenExpirationTimer);
}
this.tokenExpirationTimer = null;
}
Update the code that we subscribe the user with authService.
this.store.select()
gives us an Observable, authState, which we can use pipe or map.
In auth-interceptor:
@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
constructor(private authService: AuthService, private store: Store<fromApp.AppState>) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.store.select('auth').pipe(
take(1),
map(authState => {
return authState.user;
}),
exhaustMap(user => {
if (!user) {
return next.handle(req);
}
const modifiedReq = req.clone({params: new HttpParams().set('auth', user.token)});
return next.handle(modifiedReq);
}));
}
}
In Auth guard:
@Injectable({providedIn: 'root'})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router,
private store: Store<fromApp.AppState>) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.store.select('auth').pipe(
take(1),
map(authState => {
return authState.user;
}),
map(user => {
const isAuth = !!user;
if (isAuth) {
return true;
}
return this.router.createUrlTree(['/auth']);
}));
}
}
In the header component:
export class HeaderComponent implements OnInit, OnDestroy {
isAuthenticated = false;
collapsed = true;
private userSub: Subscription;
constructor(
private dataStorageService: DataStorageService,
private authService: AuthService,
private store: Store<fromApp.AppState>
) { }
ngOnInit() {
this.userSub = this.store.select('auth')
.pipe(map(authState => authState.user))
.subscribe(user => {
this.isAuthenticated = !!user;
console.log(!user);
console.log(!!user);
});
}
}
Important Note: Every Actions we dispatched always reaches ALL reducers.
Important Note: Always copy the old state, and return this state in a default case.
Because if a shopping list action is dispatched, it still reaches the auth reducer.
Important Note: The ID of an action type must be unique.
A name like export const ADD_INGREDIENT = '[Shopping List] Add Ingredient';
is recommended.
export const ADD_INGREDIENT = '[Shopping List] Add Ingredient';
export const ADD_INGREDIENTS = '[Shopping List] Add Ingredients';
export const UPDATE_INGREDIENT = '[Shopping List] Update Ingredient';
export const DELETE_INGREDIENT = '[Shopping List] Delete Ingredient';
export const START_EDIT = '[Shopping List] Start Edit';
export const STOP_EDIT = '[Shopping List] Stop Edit';
export const LOGIN = '[Auth] Login';
export const LOGOUT = '[Auth] Logout';