Angular 6 - 如果导入 BrowserAnimationsModule (Openlayers) 会出现渲染问题
Angular 6 - Rendering problems if BrowserAnimationsModule imported (Openlayers)
我基本上有一个呈现小地图的应用程序。此页面上还有一个 "toggle" 按钮,它在父组件中隐藏带有 *ngIf 的小地图,并以大地图显示。
如果我现在在 app.module.ts 中导入 BrowserAnimationsModule,那么如果我单击切换按钮(仅当我在 map-base.component.ts 的 ngOnInit 中创建地图之前设置了 1 毫秒的超时),则不会渲染大地图。
抱歉..有点难以解释,这是代码:
App.module.ts
...
@NgModule({
declarations: [
AppComponent,
MapSmallComponent,
MapLargeComponent,
MapBaseComponent
],
imports: [
BrowserModule,
FormsModule,
BrowserAnimationsModule, // comment this line, to make the app work
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
@Component({
selector: 'app-root',
template: `
<app-map-small *ngIf="small"></app-map-small>
<app-map-large *ngIf="!small"></app-map-large>
<button (click)="small = !small">Toggle</button>`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
small = true;
}
地图-base.component.ts
@Component({
selector: 'app-map-base',
template: `<div id="map" class="map"></div>`,
styleUrls: ['./map-base.component.scss']
})
export class MapBaseComponent implements OnInit {
map: OlMap;
constructor() { }
ngOnInit() {
this.map = new OlMap({
target: 'map',
layers: [new OlTileLayer({ source: new OlOSM() })],
view: new OlView({ zoom: 13, center: fromLonLat([13.376935, 52.516181]) }),
controls: []
});
}
}
地图-small.component.ts
@Component({
selector: 'app-map-small',
template: `
<p>other stuff</p>
<div style="max-width:500px">
<app-map-base></app-map-base>
</div>
<p>other stuff2</p>
`,
styleUrls: ['./map-small.component.scss']
})
export class MapSmallComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
地图-large.component.ts
@Component({
selector: 'app-map-large',
template: `
<p>Some other stuff</p>
<app-map-base></app-map-base>`,
styleUrls: ['./map-large.component.scss']
})
export class MapLargeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
依赖项
"dependencies": {
"@angular/animations": "^6.0.9",
"@angular/cdk": "^6.4.0",
"@angular/common": "^6.0.2",
"@angular/compiler": "^6.0.2",
"@angular/core": "^6.0.2",
"@angular/forms": "^6.0.2",
"@angular/http": "^6.0.2",
"@angular/material": "^6.4.0",
"@angular/platform-browser": "^6.0.2",
"@angular/platform-browser-dynamic": "^6.0.2",
"@angular/router": "^6.0.2",
"core-js": "^2.5.4",
"ol": "^5.1.3",
"rxjs": "^6.0.0",
"zone.js": "^0.8.26"
},
那么,为什么我不导入 BrowserAnimationsModule 就一切正常,如果我导入它就会中断?我想使用 Angular Material,我需要它...
您必须使用 ngAfterViewInit
,然后您可以 'sure' 您的组件模板中的元素出现在 DOM。
ngAfterViewInit() {
this.map = new OlMap({
target: 'map',
layers: [new OlTileLayer({ source: new OlOSM() })],
view: new OlView({ zoom: 13, center: fromLonLat([13.376935, 52.516181]) }),
controls: []
});
}
这是一个解决方案,
组件模板
<div #mapElementRef class="map"></div>
组件 class 实现 OnInit
并具有下一个 属性
@ViewChild('mapElementRef', { static: true }) mapElementRef: ElementRef;
最后在ngOnInit
方法上初始化地图,可以使用
this.map.setTarget(this.mapElementRef.nativeElement);
希望对您有所帮助。
注意脏修复
我确实尝试了@cabesuon 的版本,但它仍然无法正常工作。
就像@cabueson 说的那样
<div #mapElementRef class="map"></div>
然后在你的component.ts
,但是
- 如@PoulKrujit 所说
ngAfterViewInit()
- 添加
setTimeout(() => {})
这是脏修复
这里是结果
export class MapBaseComponent implements AfterViewInit {
@ViewChild('mapElementRef', { static: true }) mapElementRef: ElementRef
map: OlMap
constructor() {}
ngAfterViewInit() {
setTimeout(() => {
this.map = new Map({
layers: [new OlTileLayer({ source: new OlOSM() })],
view: new OlView({ zoom: 13, center: fromLonLat([13.376935, 52.516181]) }),
controls: [],
})
this.map.setTarget(this.mapElementRef.nativeElement)
}, 0)
}
}
我不是 100% 确定为什么,但这是我的猜测。
ion-content
实例化后不会刷新自己。但是,将代码置于超时强制 ion-content 下进行刷新。这也是为什么你不用放任何毫秒的原因。
强制 ionic 应用程序刷新的其他方法确实存在,例如 tick
,但是超时对于我们在这里所做的事情来说已经足够了。
我遇到了类似的问题,但解决方法不同。让我和你分享我学到的东西。
我有一个 Angular (13) 应用程序,由于 router-outlet...所以我的应用程序看起来像这样:
// App.component.html
<router-outlet>
// App.route.module.ts
const routes: Routes = [
{ path: 'map-component', component: MapComponent },
{ path: 'settings', component: SettingsComponent },
];
map-component 是我的默认路线。所以当我启动我的应用程序时,一切都很好。
但是当我启用“设置”路由时,我的 MapComponent 被销毁,然后当我返回“map-component”时,在某些帧中我可以看到两个组件都出现在 DOM(如下面的屏幕截图所示):
所以我在地图组件中为解决这个问题所做的是以下代码:
// Map.component.ts
export class MapComponent implements AfterViewInit, DoCheck {
private previousHeight = 0;
@ViewChild('map') map!: ElementRef;
ngAfterViewInit(): void {
this.previousHeight = this.map.nativeElement.offsetHeight;
this.mapService.map.setTarget(this.map.nativeElement);
}
ngDoCheck(): void {
if (this.map) {
if(this.previousHeight !== this.map.nativeElement.offsetHeight) {
this.previousHeight = this.map.nativeElement.offsetHeight;
this.mapService.map.updateSize();
}
}
}
}
因为Angular在动画过程中调用DoCheck方法,所以我在整个动画过程中更新了地图大小。
我对那个方法不是很满意。我真正想要的是禁用路由器动画(并保留所有其他动画)。 Buf 即使查看了 angular 文档,我也找不到关于这个“默认动画”的任何信息...
我基本上有一个呈现小地图的应用程序。此页面上还有一个 "toggle" 按钮,它在父组件中隐藏带有 *ngIf 的小地图,并以大地图显示。 如果我现在在 app.module.ts 中导入 BrowserAnimationsModule,那么如果我单击切换按钮(仅当我在 map-base.component.ts 的 ngOnInit 中创建地图之前设置了 1 毫秒的超时),则不会渲染大地图。
抱歉..有点难以解释,这是代码:
App.module.ts
...
@NgModule({
declarations: [
AppComponent,
MapSmallComponent,
MapLargeComponent,
MapBaseComponent
],
imports: [
BrowserModule,
FormsModule,
BrowserAnimationsModule, // comment this line, to make the app work
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.ts
@Component({
selector: 'app-root',
template: `
<app-map-small *ngIf="small"></app-map-small>
<app-map-large *ngIf="!small"></app-map-large>
<button (click)="small = !small">Toggle</button>`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
small = true;
}
地图-base.component.ts
@Component({
selector: 'app-map-base',
template: `<div id="map" class="map"></div>`,
styleUrls: ['./map-base.component.scss']
})
export class MapBaseComponent implements OnInit {
map: OlMap;
constructor() { }
ngOnInit() {
this.map = new OlMap({
target: 'map',
layers: [new OlTileLayer({ source: new OlOSM() })],
view: new OlView({ zoom: 13, center: fromLonLat([13.376935, 52.516181]) }),
controls: []
});
}
}
地图-small.component.ts
@Component({
selector: 'app-map-small',
template: `
<p>other stuff</p>
<div style="max-width:500px">
<app-map-base></app-map-base>
</div>
<p>other stuff2</p>
`,
styleUrls: ['./map-small.component.scss']
})
export class MapSmallComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
地图-large.component.ts
@Component({
selector: 'app-map-large',
template: `
<p>Some other stuff</p>
<app-map-base></app-map-base>`,
styleUrls: ['./map-large.component.scss']
})
export class MapLargeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
依赖项
"dependencies": {
"@angular/animations": "^6.0.9",
"@angular/cdk": "^6.4.0",
"@angular/common": "^6.0.2",
"@angular/compiler": "^6.0.2",
"@angular/core": "^6.0.2",
"@angular/forms": "^6.0.2",
"@angular/http": "^6.0.2",
"@angular/material": "^6.4.0",
"@angular/platform-browser": "^6.0.2",
"@angular/platform-browser-dynamic": "^6.0.2",
"@angular/router": "^6.0.2",
"core-js": "^2.5.4",
"ol": "^5.1.3",
"rxjs": "^6.0.0",
"zone.js": "^0.8.26"
},
那么,为什么我不导入 BrowserAnimationsModule 就一切正常,如果我导入它就会中断?我想使用 Angular Material,我需要它...
您必须使用 ngAfterViewInit
,然后您可以 'sure' 您的组件模板中的元素出现在 DOM。
ngAfterViewInit() {
this.map = new OlMap({
target: 'map',
layers: [new OlTileLayer({ source: new OlOSM() })],
view: new OlView({ zoom: 13, center: fromLonLat([13.376935, 52.516181]) }),
controls: []
});
}
这是一个解决方案,
组件模板
<div #mapElementRef class="map"></div>
组件 class 实现 OnInit
并具有下一个 属性
@ViewChild('mapElementRef', { static: true }) mapElementRef: ElementRef;
最后在ngOnInit
方法上初始化地图,可以使用
this.map.setTarget(this.mapElementRef.nativeElement);
希望对您有所帮助。
注意脏修复
我确实尝试了@cabesuon 的版本,但它仍然无法正常工作。
就像@cabueson 说的那样
<div #mapElementRef class="map"></div>
然后在你的component.ts
,但是
- 如@PoulKrujit 所说
- 添加
setTimeout(() => {})
这是脏修复
ngAfterViewInit()
这里是结果
export class MapBaseComponent implements AfterViewInit {
@ViewChild('mapElementRef', { static: true }) mapElementRef: ElementRef
map: OlMap
constructor() {}
ngAfterViewInit() {
setTimeout(() => {
this.map = new Map({
layers: [new OlTileLayer({ source: new OlOSM() })],
view: new OlView({ zoom: 13, center: fromLonLat([13.376935, 52.516181]) }),
controls: [],
})
this.map.setTarget(this.mapElementRef.nativeElement)
}, 0)
}
}
我不是 100% 确定为什么,但这是我的猜测。
ion-content
实例化后不会刷新自己。但是,将代码置于超时强制 ion-content 下进行刷新。这也是为什么你不用放任何毫秒的原因。
强制 ionic 应用程序刷新的其他方法确实存在,例如 tick
,但是超时对于我们在这里所做的事情来说已经足够了。
我遇到了类似的问题,但解决方法不同。让我和你分享我学到的东西。
我有一个 Angular (13) 应用程序,由于 router-outlet...所以我的应用程序看起来像这样:
// App.component.html
<router-outlet>
// App.route.module.ts
const routes: Routes = [
{ path: 'map-component', component: MapComponent },
{ path: 'settings', component: SettingsComponent },
];
map-component 是我的默认路线。所以当我启动我的应用程序时,一切都很好。
但是当我启用“设置”路由时,我的 MapComponent 被销毁,然后当我返回“map-component”时,在某些帧中我可以看到两个组件都出现在 DOM(如下面的屏幕截图所示):
所以我在地图组件中为解决这个问题所做的是以下代码:
// Map.component.ts
export class MapComponent implements AfterViewInit, DoCheck {
private previousHeight = 0;
@ViewChild('map') map!: ElementRef;
ngAfterViewInit(): void {
this.previousHeight = this.map.nativeElement.offsetHeight;
this.mapService.map.setTarget(this.map.nativeElement);
}
ngDoCheck(): void {
if (this.map) {
if(this.previousHeight !== this.map.nativeElement.offsetHeight) {
this.previousHeight = this.map.nativeElement.offsetHeight;
this.mapService.map.updateSize();
}
}
}
}
因为Angular在动画过程中调用DoCheck方法,所以我在整个动画过程中更新了地图大小。
我对那个方法不是很满意。我真正想要的是禁用路由器动画(并保留所有其他动画)。 Buf 即使查看了 angular 文档,我也找不到关于这个“默认动画”的任何信息...