我有angular app,它支持多个浏览器选项卡,这些选项卡具有2个绑定,其他绑定使用angular-redux @select和另一个{{property}}。该应用程序可以正常运行。但是,我可以通过使用redux-state-sync中间件将我的角度存储配置为使用广播通道而不是localstorage来打破绑定。因此,在app.component.ts中用一行下方的注释行替换一行会破坏第二个浏览器窗口中的绑定。这似乎真的很奇怪,我不知道如何找出两个绑定为何都从看似无关的变化中打破了。
编辑:参考yurzui的回答:状态也通过广播频道选项在选项卡之间同步。只有绑定不再起作用。按下按钮后,可以在不同浏览器选项卡的控制台输出中对此进行验证。
app.component.html
<div style="text-align:center">
<div *ngIf="(isLoggedIn | async)">
<p>this appears when logged in!</p>
</div>
<h1>
App status is {{ statusTxt }}!
</h1>
<button (click)="toggleLogin()">{{ buttonTxt }}</button>
<h2>You can either login 1st and then open another tab or you can 1st open another tab and then login. Either way the both tabs should be logged in and look the same</h2>
<h2>After testing multiple tabs with current 'localstorage' broadcastChannelOption type, you can change it to 'native' in app.component.ts and you'll see that bindings do not work anymore</h2>
</div>
app.component.ts
import { Component, Injectable } from '@angular/core';
import { createStateSyncMiddleware, initStateWithPrevTab, withReduxStateSync } from 'redux-state-sync';
import { combineReducers, Action, createStore, applyMiddleware } from 'redux';
import { NgRedux, select } from '@angular-redux/store';
import { Observable } from "rxjs";
export interface LoginToggle {
isLoggedIn : boolean
}
export interface LoginToggleAction extends Action {
loginToggle: LoginToggle;
}
@Injectable()
export class LoginActions {
static TOGGLE_LOGIN = 'TOGGLE_LOGIN';
toggleLogin(loginToggle: LoginToggle): LoginToggleAction {
return {
type: LoginActions.TOGGLE_LOGIN,
loginToggle
};
}
}
export interface ILoginState {
readonly isLoggedIn: boolean;
}
export interface IApplicationState {
login: ILoginState;
}
export const INITIAL_STATE : IApplicationState = {
login : { isLoggedIn: false }
}
export function loginReducer(oldState: ILoginState = { isLoggedIn : false } as any, action: Action) : ILoginState {
switch (action.type) {
case LoginActions.TOGGLE_LOGIN: {
console.log('in reducer');
const toggleAction = action as LoginToggleAction;
return {
...oldState,
isLoggedIn: toggleAction.loginToggle.isLoggedIn
} as ILoginState;
}
default: {
return oldState;
}
}
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
statusTxt = 'logged out';
subscription;
loginStatus = false;
buttonTxt = 'Login';
@select((state: IApplicationState) => state.login.isLoggedIn) isLoggedIn: Observable<boolean>;
constructor(private ngRedux: NgRedux<IApplicationState>,
private actions : LoginActions) {
const appReducer = combineReducers<IApplicationState>({
login: loginReducer
})
const rootReducer = withReduxStateSync(appReducer);
const store = createStore(rootReducer,
applyMiddleware(createStateSyncMiddleware({ broadcastChannelOption: { type: 'localstorage' } })));
/* !! Both bindings break if i replace the row above with this:
applyMiddleware(createStateSyncMiddleware())); */
initStateWithPrevTab(store);
ngRedux.provideStore(store);
this.subscription = this.ngRedux.select<ILoginState>('login')
.subscribe(newState => {
console.log('new login state = ' + newState.isLoggedIn);
this.loginStatus = newState.isLoggedIn;
if (newState.isLoggedIn) {
this.statusTxt = 'logged in!';
this.buttonTxt = 'Logout';
}
else {
this.statusTxt = 'logged out!';
this.buttonTxt = 'Login';
}
console.log('statusTxt = ' + this.statusTxt);
});
}
toggleLogin(): void {
this.ngRedux.dispatch(this.actions.toggleLogin({ isLoggedIn: !this.loginStatus }));
}
}
答案 0 :(得分:6)
使用本地存储频道时,您的应用程序已订阅浏览器storage事件。并且,一旦您的应用程序在一个选项卡中更改了值,它就会更新locastorage,从而在另一选项卡中触发storage
事件,以便对其进行更新。
在默认的redux_state_sync
频道中,选项卡之间没有这种连接,但频道现在使用{strong>未被zonejs修补的BroadcastChannel ,因此您始终会收到不正确的消息区域。
你能做什么?
在订阅时在Angular区域内手动运行代码:
constructor(...private zone: NgZone) {
this.subscription = this.ngRedux.select<ILoginState>('login')
.subscribe(newState => {
this.zone.run(() => { // enforce the code to be executed inside Angular zone
console.log('new login state = ' + newState.isLoggedIn);
this.loginStatus = newState.isLoggedIn;
...
})
});
请注意,您也可以运行cdRef.detectChanges,但是在这种情况下,您可以陷入某些Angular处理程序将在Angular区域之外注册的情况。 结果是,您将在其他地方调用detectChanges或再次使用zone.run 。
所以我建议您使用zone.run来确保您位于Angular应用程序的正确区域中
答案 1 :(得分:0)
也许您可以运行此命令以查看是否有任何错误输出到控制台。 --verbose会将所有内容记录到控制台
ng build --prod --aot --verbose
答案 2 :(得分:0)
似乎没有在角度循环挂钩中运行。
您可以使用import UIKit
enum ViewType {
case new, approved, rejected
}
class ViewController {
private var viewType: ViewType = .new
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch viewType {
case .new :
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "AddInstructorNewCell", for: indexPath) as! AddInstructorNewCell
return cell
case .approved:
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "ApprovedCell", for: indexPath) as! ApprovedCell
return cell
case .rejected:
let cell = self.addNewTblview.dequeueReusableCell(withIdentifier: "RejectedCell", for: indexPath) as! RejectedCell
return cell
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
switch viewType {
case .new:
return 170
case .approved:
return 165
case .rejected:
return 178
}
}
@IBAction func btnNewClick(_ sender:UIButton)
{
viewType = .new
tableView.reloadData()
}
@IBAction func btnapprovedClick(_ sender:UIButton)
{
viewType = .approved
tableView.reloadData()
}
@IBAction func btnrejectedClick(_ sender:UIButton)
{
viewType = .rejected
tableView.reloadData()
}
}
类中的方法detectChanges()
手动请求角度运动来进行紧缩。
您必须在构造函数中添加ChangeDetectorRef
:
ChangeDetectorRef
然后在订阅中 constructor(
private ngRedux: NgRedux<IApplicationState>,
private actions : LoginActions,
private cd: ChangeDetectorRef,
) {
const appReducer = combineReducers<IApplicationState>({
login: loginReducer
})
:
detectChanges