我有一个页面 SearchPage,嵌套在项目根目录 HomePage 的主页面中,并且 SearchComponent 中有两个子组件:PromptComponent 和 ResultsComponent。这是
tree
输出:
src/app/home/
├── home.module.ts
├── home.page.html
├── home.page.scss
├── home.page.spec.ts
├── home.page.ts
├── home-routing.module.ts
└── search
├── prompt
│ ├── prompt.component.html
│ ├── prompt.component.scss
│ ├── prompt.component.spec.ts
│ └── prompt.component.ts
├── results
│ ├── results.component.html
│ ├── results.component.scss
│ ├── results.component.spec.ts
│ └── results.component.ts
├── search.module.ts
├── search.page.html
├── search.page.scss
├── search.page.spec.ts
├── search.page.ts
└── search-routing.module.ts
如果我在主页中创建另一个带有路由(或组件,无关紧要)的页面,在地址栏中输入该路由,然后导航到
/search
,我会看到正确的内容,并且工作正常,没有错误。但是,如果我从那里刷新页面,或者在地址栏中输入 /search
,我会收到两个控制台错误:
NG0303: Can't bind to 'ngIf' since it isn't a known property of 'app-prompt'. core.js:10105
NG0303: Can't bind to 'ngIf' since it isn't a known property of 'app-results'. core.js:10105
这指的是 search.page.html 中的几个标签:
<app-prompt
(queryEmitter)="update($event)"
*ngIf="query === ''"
></app-prompt>
<app-results
[query]="query"
(queryEmitter)="update($event)"
*ngIf="query !== ''"
></app-results>
当我看到错误时,这些组件不会被渲染。
当他们工作时,以下是他们如何工作。首先,
<app-prompt>
中会显示一个空提示。稍后,当用户在提示中键入某些内容时,query
的值会从事件发射器传递到 SearchPage,然后再次传递到 ResultsComponent。
其余代码如下。我怎样才能让它在每种情况下都起作用?
搜索.page.ts
import { Location } from '@angular/common';
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
/**
* Main component for the search page and parent to `PromptComponent` and
* `ResultsComponent`.
*
* @decorator `@Component({ selector: 'app-search', templateUrl: './search.page.html', styleUrls: ['./search.page.scss'], })`
*
* @alpha
*/
@Component({
selector: 'app-search',
templateUrl: './search.page.html',
styleUrls: ['./search.page.scss'],
})
export class SearchPage implements OnInit {
/**
* Holds the value of `ion-searchbar` within each of the child components,
* and allows for communication across components.
*
* @alpha
*/
query!: string;
/**
* Constructor for the search page.
*
* @param _route `ActivatedRoute`
* @param _location `Location`
*
* @alpha
*/
constructor(private _route: ActivatedRoute, private _location: Location) {}
/**
* A lifecycle hook that is called after Angular has initialized all
* data-bound properties of a directive. Takes query parameter from URL and
* stores it locally.
*
* @alpha
*/
ngOnInit(): void {
this._route.paramMap.subscribe((paramMap: ParamMap) => {
const query: null | string = paramMap.get('query');
this.query = query ? query : '';
});
}
/**
* Method to update the `query` property and the URL.
*
* @param query String from search bar to be used in child components.
*
* @alpha
*/
update(query: string): void {
this.query = query;
this._location.replaceState(`search${query ? '/' + query : ''}`);
}
}
提示.组件.ts
import {
AfterViewChecked,
Component,
EventEmitter,
Output,
ViewChild,
} from '@angular/core';
import { SearchbarChangeEventDetail } from '@ionic/core/dist/types/components/searchbar/searchbar-interface.d';
/**
* Prompt component. Child component of Search page responsible for prompting
* the user and indirectly passing data to its sibling component, Results, with
* the help of Word page.
*
* @selector `@Component({ selector: 'app-prompt', templateUrl: './prompt.component.html', styleUrls: ['./prompt.component.scss'], })`
*
* @alpha
*/
@Component({
selector: 'app-prompt',
templateUrl: './prompt.component.html',
styleUrls: ['./prompt.component.scss'],
})
export class PromptComponent implements AfterViewChecked {
/**
* Emitter object for passing query (text) value to the parent component for
* its use and use by ResultsComponent.
*
* @alpha
*/
@Output() queryEmitter = new EventEmitter<string>();
/**
* Reference to `ion-searchbar` element to be set to focus.
*
* @alpha
*/
@ViewChild('searchbar') searchbar: HTMLIonInputElement;
query:string;
/**
* Constructor that takes no arguments and does nothing.
*
* @alpha
*/
constructor() {}
/**
* An Angular lifecycle hook that is called after the default change detector
* has completed checking a component's view for changes. This method simply
* calls setFocus() on the searchbar element.
*
* @alpha
*/
ngAfterViewChecked(): void {
this.searchbar.setFocus();
}
/**
* Takes `ion-searchbar` value from template and emits it to the parent
* component.
*
* @param $event The `ionChange` event object passed in via template.
*
* @alpha
*/
onChange($event: CustomEvent<SearchbarChangeEventDetail>): void {
this.queryEmitter.emit($event.detail.value);
}
}
结果.component.ts
import {
AfterViewChecked,
Component,
EventEmitter,
Input,
Output,
ViewChild,
} from '@angular/core';
import { IonSearchbar } from '@ionic/angular';
import { SearchbarChangeEventDetail } from '@ionic/core/dist/types/components/searchbar/searchbar-interface.d';
/**
* Results component. Child component of Search page responsible for taking in
* user input and displaying search results. Currently, though, all it does is
* take in input and switch to its sibling component, Prompt, when the input is
* cleared.
*
* @selector `@Component({ selector: 'app-results', templateUrl: './results.component.html', styleUrls: ['./results.component.scss'], })`
*
* @alpha
*/
@Component({
selector: 'app-results',
templateUrl: './results.component.html',
styleUrls: ['./results.component.scss'],
})
export class ResultsComponent implements AfterViewChecked {
/**
* Query text passed in from parent component, which receives it either from
* the `:query` URL parameter or from `PromptComponent`.
*
* @alpha
*/
@Input() query: string;
/**
* Emitter object for passing query (text) value to the parent component for
* its use.
*
* @alpha
*/
@Output() queryEmitter = new EventEmitter<string>();
/**
* Reference to `ion-searchbar` element to be set to focus.
*
* @alpha
*/
@ViewChild('searchbar') searchbar: IonSearchbar;
/**
* Constructor that takes no arguments and does nothing apart from returning
* the component instance.
*
* @alpha
*/
constructor() {}
/**
* Ionic lifecycle hook that gets called when the component is about to enter
* view. Here, the search results are updated.
*
* @alpha
*/
ionViewWillEnter() {
this.search();
}
/**
* Angular lifecycle hook called after calls to ngAfterContentChecked() have
* finished. This hook simply calls setFocus() on the searchbar element.
*
* @alpha
*/
ngAfterViewChecked(): void {
this.searchbar.setFocus();
}
/**
* Takes `ion-searchbar` value from template and stores it locally. Then it
* checks for an non-empty string, and if that passes, the search results are
* updated. Finally, it emits the query value to the parent component, empty
* or not.
*
* @param $event The `ionChange` event object passed in via template.
*
* @alpha
*/
onChange($event: CustomEvent<SearchbarChangeEventDetail>): void {
const query = $event.detail.value;
this.query = query;
if (query) {
this.search();
}
this.queryEmitter.emit(query);
}
/**
* Emits a blank query value to the parent component so focus is returned to
* PromptComponent.
*
* @alpha
*/
clear(): void {
this.queryEmitter.emit('');
}
/**
* Skeleton code for search functionality. Currently, it is used to log the
* local value of `query` for debugging purposes.
*
* @alpha
*/
private search(): void {
console.log(this.query);
}
}
编辑
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 { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
CommonModule,
],
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent],
})
export class AppModule {}
home.module.ts
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';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
HomePageRoutingModule
],
declarations: [HomePage]
})
export class HomePageModule {}
search.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { SearchPageRoutingModule } from './search-routing.module';
import { SearchPage } from './search.page';
import { PromptComponent } from './prompt/prompt.component';
import { ResultsComponent } from './results/results.component';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
SearchPageRoutingModule,
],
declarations: [SearchPage, PromptComponent, ResultsComponent]
})
export class SearchPageModule {}
其他问题可以通过将
CommonModule
添加到 *.module.ts 来解决,但正如您在上面看到的,它已经包含在内。
请转到您项目的正确 module.ts 文件并检查您是否已导入
CommonModule
.
否则,将其导入为
import { CommonModule } from '@angular/common';
然后,将其添加到导入数组中。
@NgModule({
imports: [CommonModule], // Import here
...
})
class YourModule {}
在你的 component.ts 文件中
从'@angular/common'导入{CommonModule};
在@Component({ 选择器... 导入:[CommonModule], })