angular2 ngoninit怎么解决ngoninit执行一次

问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
我知道ngOnInit和constructor都能在页面初始化的时候执行,constructor比ngOnInit快,并且看到官网说尽量将复杂的内容放在ngOnInit里,我也一直是这么做的。
然而最近遇到了一个问题,跳路由之后,页面只执行constructor没有执行ngOnInit,当我再次点击路由或者输入页面中的Input框时,才执行ngOnInit。这导致了一个问题,跳路由时,我的页面加载不完全。
然后又滚去看官网,有这样一句话:Remember also that a directive's data-bound input properties are not set until after construction. That's a problem if we need to initialize the directive based on those properties. They'll have been set when our ngOninit runs.中文翻译:另外还要记住,在指令的 构造函数完成之前 ,那些被绑定的输入属性还都没有值。 如果我们需要基于这些属性的值来初始化这个指令,这种情况就会出问题。 而当 ngOnInit 执行的时候,这些属性都已经被正确的赋值过了。
我表示没有看懂,另外看到说ngOnInit会在第一次执行ngOnChanges之后执行,那么我遇到的这个问题是因为我没有触发ngOnChanges吗?
求大神解答。
以下是我的代码:
import {Component,OnInit,OnDestroy} from '@angular/core';
import {Router} from '@angular/router';
import {Http} from '@angular/http';
import {SettingService,LocalHostParams} from '../../services/setting.service';
import {Pagination} from '../pagination-modal/pagination';
@Component({
selector: 'setting-network',
template: require('./setting-network.html'),
styles: [require('./setting.css').toString()],
directives: [Pagination]
export class SettingNetwork implements OnInit,OnDestroy {
private service:SettingS
private local_host_params:LocalHostP
constructor(private http:Http, private _router:Router) {
this.service = new SettingService(http);
console.log('constructor')
ngOnInit() {
console.log('ngOnInit')
this.getLocalHostParams();
getLocalHostParams() {
this.service.getLocalHost().subscribe(data=& {
var results = data.json();
this.local_host_params = {
'subnet_mask': results.subnet_mask,
'default_gateway': results.default_gateway,
'ip_address': results.ip_address,
'dns_server': results.dns_server,
console.log(this.local_host_params)
ngOnDestroy() {
&div class="setting-network"&
&form class="form-horizontal setting-config-form" #localhost="ngForm"&
&div class="form-group"&本机参数&/div&
&div class="form-group"&
&label class="col-sm-3 control-label"&IP地址:&/label&
&div class="col-sm-4"&
&input type="text" class="form-control"
ngControl="ip_address" [(ngModel)]="local_host_params.ip_address"&
&div class="form-group"&
&label class="col-sm-3 control-label"&子网掩码:&/label&
&div class="col-sm-4"&
&input type="text" class="form-control"
ngControl="subnet_mask" [(ngModel)]="local_host_params.subnet_mask"&
&div class="form-group"&
&label class="col-sm-3 control-label"&默认网关:&/label&
&div class="col-sm-4"&
&input type="text" class="form-control"
ngControl="default_gateway" [(ngModel)]="local_host_params.default_gateway"&
&div class="form-group"&
&label class="col-sm-3 control-label"&DNS服务器:&/label&
&div class="col-sm-4"&
&input type="text" class="form-control"
ngControl="dns_server" [(ngModel)]="local_host_params.dns_server"&
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
我遇到的情况和你的一样,最后发现是没有引入一个模块导致的import 'core-js/es6';楼主可以试一下,我的已经解决用的是"core-js": "^2.4.1"
同步到新浪微博
分享到微博?
你好!看起来你挺喜欢这个内容,但是你还没有注册帐号。 当你创建了帐号,我们能准确地追踪你关注的问题,在有新答案或内容的时候收到网页和邮件通知。还能直接向作者咨询更多细节。如果上面的内容有帮助,记得点赞 (????)? 表示感谢。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
扫扫下载 App& & &为了应对未来的趋势,及时赶上下一趟互联网技术,我最近也在通过具体项目研究angular2,首先必须要吐槽的是,学习angular2的成本本身不高,但是一堆的工具、配置实在让人 很是焦灼,就像asp.net core一样,所有的东西都在向同样的方向迈进:尽量使用已经造好的轮子,而不是自己再弄一个。
& & &当然,统一是好的,但是对于对前端不是太敏感的我来说,还是挑战不小,这就要求我要学好angular2,必须要熟悉一系列的工具链。加油。
今天要说的是一个可以说我遇到的很诡异的问题,我在angular1上进行了相同的尝试,没有发现这个问题,但是在angular2上发现了,我将使用场景还原一下,当然我只是抽取其中的一部分,希望有人遇到或知道如何解决回复一下,当然如果我找到答案,会注明。
& & 背景:我通过angular2 下叫ng2,的路由来实现视图的导航,我使用官方文档英雄列表来说明。我从英雄列表页面通过路由导航到具体的详情页面,但是我准备实现从详情页面导航到列表页面时出现的问题。
& 我自己尝试了几种实现方式:
&window.history.back():通过浏览器的回退实现
通过router.navigate(['/heros']).其中router通过构造函数进行注入
我要说明的问题就是通过第二种实现。
&步骤1:新建index.html页面,没有什么特别的地方,只是设置了ng2的启动页面
1 &!DOCTYPE html&
&html lang="en"&
&meta charset="UTF-8"&
&title&guozhiqi&/title&
&meta name="viewport" content="width=device-width,initial-scale=1"&
&link rel="stylesheet" type="text/css" href="node_modules/primeng/resources/themes/delta/theme.css" /&
&link rel="stylesheet" type="text/css" href="node_modules/font-awesome/css/font-awesome.css" /&
&link rel="stylesheet" type="text/css" href="node_modules/primeng/resources/primeng.min.css" /&
&base href="/"&
Loading...
&script src="node_modules/core-js/client/shim.js"&&/script&
&script src="node_modules/reflect-metadata/Reflect.js"&&/script&
&script src="node_modules/zone.js/dist/zone.js"&&/script&
&script src="node_modules/systemjs/dist/system.js"&&/script&
&script src="systemjs.config.js"&&/script&
System.import('app').catch(function (err) {
console.error(err);
步骤2:新建app文件夹,并且添加main.ts页面
* Created by guozhiqi on .
4 import {platformBrowserDynamic}from '@angular/platform-browser-dynamic';
5 import {AppModule}from './app.module';
6 import {RouterModule}from '@angular/router';
platform=platformBrowserDynamic();
9 platform.bootstrapModule(AppModule);
添加ng2的启动模块AppModule
在app文件夹添加app.module.ts文件  
1 import './rxjs-extensions';
3 import { NgModule }
from '@angular/core';
4 import { BrowserModule } from '@angular/platform-browser';
5 import { FormsModule }
from '@angular/forms';
6 import { HttpModule }
from '@angular/http';
8 // Imports for loading & configuring the in-memory web api
9 import { InMemoryWebApiModule } from 'angular2-in-memory-web-api';
10 import { InMemoryDataService }
from './in-memory-data.service';
12 import { AppComponent }
from './ponent';
13 import { DashboardComponent }
from './ponent';
14 import { HerosComponent }
from './ponent';
15 import { HeroDetailComponent }
from './ponent';
16 import { HeroService }
from './hero.service';
17 import { HeroSearchComponent }
from './ponent';
18 import { routing }
from './app.routing';
19 import {RouterModule,Router}from '@angular/router';
21 @NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
InMemoryWebApiModule.forRoot(InMemoryDataService),RouterModule,
declarations: [
AppComponent,
DashboardComponent,
HeroDetailComponent,
HerosComponent,
HeroSearchComponent
providers: [
HeroService
bootstrap: [ AppComponent ]
41 export class AppModule {
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
重点是我们导入了RoutingModule模块
添加路由配置app.routing.ts
1 import {ModuleWithProviders}from '@angular/core';
2 import {Routes, RouterModule}from '@angular/router';
3 import {HerosComponent}from './ponent';
4 import {DashboardComponent}from './ponent';
5 import {HeroDetailComponent}from './ponent';
6 const appRoutes:Routes = [
path: 'heros',
component: HerosComponent
path:'dashboard',
component:DashboardComponent
path:'detail/:id',
component:HeroDetailComponent
redirectTo: '/dashboard',
pathMatch: 'full'
path:'**',
component:DashboardComponent
31 export const
routing:ModuleWithProviders=RouterModule.forRoot(appRoutes);
添加英雄列表页面ponent.ts
* Created by guozhiqi on .
4 import {Component, OnInit}from '@angular/core';
5 import {Title}from '@angular/platform-browser';
6 import {ButtonModule}from 'primeng/primeng';
7 import {Hero}from './hero';
8 import {HeroService} from "./hero.service";
9 import {HeroDetailComponent}from './ponent';
10 import {Router}from '@angular/router';
12 @Component({
selector: "my-heros",
templateUrl: 'ponent.html',
styleUrls: ['ponent.css']
18 export class HerosComponent implements OnInit {
heros:Hero[];
selectedHero:H
ngOnInit():void {
this.getHeros();
getHeros():void {
this.heroService.getHeroes().then(heros=&this.heros = heros);
onSelect(hero:Hero) {
this.selectedHero =
gotoDetail() {
this.router.navigate(['/detail', this.selectedHero.id]);
add(name:string):void {
name = name.trim();
if (!name) {
this.heroService.create(name)
.then(hero=&{
this.heros.push(hero);
this.selectedHero=null;
delete(hero:Hero):void
this.heroService.delete(hero.id)
.then(()=&{
this.heros=this.heros.filter(h=&h!==hero);
if(this.selectedHero===hero)
this.selectedHero=null;
constructor(private
router:Router, private titleService:Title, private heroService:HeroService) {
this.titleService.setTitle("HeroList");
重点就出现在我们通过构造函数注入的Router上,我们的英雄列表通过router.navigate(
['/detail', this.selectedHero.id]
)来导航到了详情页面,请注意,router是通过构造函数注入
详情页面组件代码
* Created by guozhiqi on .
4 import {Component, Input, OnInit}from'@angular/core';
5 import {ActivatedRoute, Params, Router}from'@angular/router';
6 import {HeroService}from './hero.service';
7 import {Title}from '@angular/platform-browser';
8 import {Hero} from "./Hero";
9 import from = require("core-js/fn/array/from");
11 @Component({
selector: 'my-hero-detail',
templateUrl: 'app/ponent.html',
styleUrls: ['app/ponent.css'],
17 export class HeroDetailComponent implements OnInit {
currentDate:D
private router:R
constructor(private heroService:HeroService,
route:ActivatedRoute,
router:Router,
title:Title) {
this.currentDate = new Date();
this.title.setTitle("hero-detail");
ngOnInit():void {
this.route.params.forEach((params:Params)=& {
let id = +params['id'];
this.heroService.getHero(id).then(hero=&this.hero = hero);
goBack():void {
window.history.back();
this.router.navigate(['/heros']);
save():void {
this.heroService.update(this.hero).then(this.goBack);
重点是详情页面的goBack()方法,我本来准备通过路由的navigate方法 来实现导航,并且router是通过构造函数注入,但是我在使用时this.router会为null,导致我无法通过这种方式实现页面跳转。
其实我想说的这个倒不是ng2的bug,可能有些地方没有设置成功,之所以说是bug,而是因为官方没有提供具体的详细信息。
有知晓的麻烦回复一下,我相信如果要使用页面跳转,肯定会用到。
&更详细的内容可以参考官方的英雄列表。
阅读(...) 评论()转载自GitHub JTangming&:&
Angular应用程序通过组件实例和模板之间进行数据交互,也就是将组件的数据和页面DOM元素关连起来,当数据有变化后,NG2能够监测到这些变化并更新视图,反之亦然,它的数据流向是单项的,通过属性绑定和事件绑定来实现数据的流入和流出,数据从属性绑定流入组件,从事件流出组件,数据的双向绑定就是通过这样来实现的。那么它是如何实现变化检测的呢?
需要进行变化监测的情形
试想一下,在什么样的场景下,angular才需要去更新视图:
event,在view中绑定事件来监听用户的操作,如果数据有变更则更新视图;
xmlHTTPRequest/webSocket,例如从远端服务拉取对应的数据,这是一个异步的过程;
timeout,例如:setTimeout,&setInterval,&requestAnimationFrame都是在某个延时后触发。
以上的共同特征是什么?很明显共同点是它们都是异步的处理,即需要使用异步回调函数,这带给我们的结论就是,不管任何时候的一个异步操作,我们应用程序状态可能已经被改变,这就需要告诉Angular去更新视图。
我们创建一个组件来呈现一个Todo例子,我们可以在模板中这样使用这个组件:
&todo-cmp [model]="myTodo" (complete)="onCompletingTodo(todo)"&&/todo-cmp&
这将告诉Angular不管任何时候myTodo发生改变,Angular必须通过调用视图模型设置的myTodo数据(model setter)来自动的更新todo模板组件。
同样的,数据的流出是通过事件绑定来实现的,如果一个complete事件被触发,它将调用这个onCompletingTodo方法,该方法可能是一个获取后台最新数据的操作,这将需要用后台返回的异步数据与之前的数据参考进行对比来确定是否需要更新视图。
正如上面的例子,Angular2的属性和事件绑定的核心语法是很简单的,我们通过属性绑定实现了数据从父传递给了子,而事件绑定则实现了数据由子到父的传递,这也就是Angular2用来实现数据双向绑定的方法。它实现的是单向流的数据传递,也就是说,你的数据流只能向下流入组件,如果你需要进行数据变化,你可以发射导致变化的事件到顶部,待数据变化处理完成,然后再往下流入组件。那么问题来了,Angular2如何知道数据是否已经处理处理完成,这份新的数据是否有变化,如果数据有变化,那是怎么来通知数据往下流入组件通知组件来改变视图呢?这里我们先理解zone。
Zone实际上是Dart的一种语言特性,其是对Javascript某些设计缺陷的一些补充,简单的可以概述成Zone是一个异步事件拦截器,也就是说Zone能够hook到异步任务的执行上下文,以此来处理一些操作,比如说,在我们每次启动或者完成一个异步的操作、进行堆栈的跟踪处理、某段功能代码进入或者离开zone,我们可以在这些关键的节点重写我们所需处理的方法。
Zone中提供了各类hooks,允许在每一个回调函数的开始和结束时,去执行统一的自定义逻辑,其本身是不做任何事的,相反它是依赖其它的代码,获取到这些代码片段的执行上下文,通过hooks来完成相关的功能。Zone的另一个值得一提的是它必须依赖异步操作,当一个异步操作在执行时,它是有必要去捕获的这个异步操作并在该异步功能开始或者完成时建立对应的callback,然后存储到当前的zone,举个例子,如果一个代码片段在fork的zone中执行,并且这段代码中包含一个setTimeout的异步任务,那么执行到和完成这个setTimeout方法需要包裹一个异步的回调函数,存储到当前zone。
这样是确保每个异步操作之间的相互不受影响,也就是受保护的状态,例如一个页面由业务代码和一些第三方广告代码组成,这两份代码之间是相互独立的,我们需要的是业务代码的异常捕获数据提交到我们自己的后台服务器上,第三方广告代码的异常捕获提交到他们自己的服务器上。当fork了多个zone之后,异步操作将会精准的执行其所在的子zone上面方法。
Zone的一个重要意义在于,我们的功能或者业务代码运行在了fork的一个zone中,我们zone有了对该代码块执行上下文的控制权。其中也提供了一些钩子(hook)来处理我们基本的业务情景需求,大致有:
Zone.onZoneCreated:在zone被fork时运行
Zone.beforeTask:在执行zone.run包裹的函数之前调用
Zone.afterTask:在执行zone.run包裹的函数之后调用
Zone.onError:zone.run方法中的Task任务抛出异常时的钩子函数
下面我们通过这样的一个例子来帮助你理解Zone,简单的代码如下:
zone.fork({
beforeTask: () =& {
console.log('hi, beforeTask in.');
afterTask: () =& {
console.log('hi, afterTask in.');
}).run(function () {
zone.inTheZone = true;
setTimeout(function () {
console.log('in the zone: ' + !!zone.inTheZone);
}, <span style="color: #);
console.log('in the zone: ' + !!zone.inTheZone);
这段代码按照执行上下文顺序的执行,我们在zone的run函数执行的开始和结束会有对应的hooks,例如要统计这段代码执行所消耗的时间,然而通常情况下,这里的异步处理,比如说是服务端异步返回给我们所需要的数据,或者是一些异步事件更改视图模型的数据等。这样通过beforeTask和afterTask统计到整个代码的耗时。这种情形在zone得到了很好的解决,Zone能够hook到异步任务的执行上下文,在异步事件发生或者结束的时候,允许我们在这样的异步任务节点执行一些分析代码。zone使用也很简单,一旦我们引入zone.js,那我们在全局作用域中可以获取到zone对象。
但是这远远不够的,很多时候我们的应用场景要比这个复杂的多,现在是时候体现zone的暴力美了,zone.js采用猴子补丁(Monkey-patched)的方式将Js中的异步任务都进行了包裹,同样的这使得这些异步任务都将运行在zone的执行上下文中,每一个异步的任务在zone.js都是一个task,除了提供了一些供开发者使用的勾子(hook)函数外,默认情况下zone.js重写了并提供了如下的方法:
Zone.setInterval() / Zone.setTimeout()
Zone.alert()
Zone.prompt()
Zone.requestAnimationFrame()
Zone.addEventListener()
Zone.removeEventListener()
综上所述,我们应该能理解zone.js的应用场景了,即实现了异步task的跟踪分析和错误记录以便更好的进行开发debug等。接下来将回到主题来探讨一下Angular2的数据绑定和zone的关系。
Angular2数据绑定和Zone
在Angular1.x中,默认的选择是双向的数据绑定,你的控制器数据发生变化,或者表单直接操作数据变动等,最终体现在视图中显示数据。
Angular1.x双向数据绑定的问题是,随着你的项目增长,它往往会导致整个应用的级联效应,并很难跟踪你的数据流。除非你使用Angular1.x框架的内置服务和指令,否则我们在model上做数据修改或者数据输出,Angular是无法预知的,当然就不会去更新视图模板中的数据来展示给UI。
好在Angular2框架把zone.js作为依赖,因为zone.js是一个独立的库,可以不依赖于其他库或者框架而单独被使用,因此在Angular2开发的应用中,zone拥有angular应用运行环境的执行上下文,事实证明,zone是能够解决在我们在angular应用中变化监测的问题的。
下面我们来介绍ngZone。实际上,ngZone是基于Zone.js来实现的,Angular2 fork了zone.js,它是zone派生出来的一个子zone,在Angular环境内注册的异步事件都运行在这个子zone上(因为ngZone拥有整个Angular运行环境的执行上下文),并且onTurnStart和onTurnDone事件也会在该子zone的run方法中触发。
在Angular2源码中,有一个ApplicationRef类,其作用是用来监听ngZone中的onTurnDone事件,不论何时只要触发这个事件,那么将会执行一个tick()方法用来告诉Angular去执行变化监测。
// very simplified version of actual source
class ApplicationRef {
changeDetectorRefs:ChangeDetectorRef[] = [];
constructor(private zone: NgZone) {
this.zone.onTurnDone
.subscribe(() =& this.zone.run(() =& this.tick());
this.changeDetectorRefs
.forEach((ref) =& ref.detectChanges());
Angular2的变化监测
现在我们已经知道了Angular2的变化监测在何时被触发,那它是怎么去做变化监测的呢?实际上在Angular2中,任何的一个Angular2应用都是由大大小小的组件组成的,可以把它看成是一颗线性的组件树,重要的是,每一个组件都有自己的变化检测器。这样的一个图可以帮助你理解这些概念,具体也可以参考关于的文章。
正是因为每个组件都拥有它的变化检测器,组成了Angular2应用的一颗组件树,同样的我们也有变化监测树,它也是线性的,数据的流向也是从上到下,因为变化监测在每个组件中的执行也是从根组件开始,从上往下的执行。单向的数据流相对angular1.x的环形数据流来说要更好预测的多,其实我们清楚视图中数据的来源,也就是说这些数据的变化是来自于哪个组件数据变化的结果。我们来举个例子吧:
@Component({
template: '&v-card [vData]="vData"&&/v-card&'
class VCardApp {
constructor() {
this.vData = {
name: '***',
email: '****@**.com'
changeData() {
this.vData.name = '*****';
Angular2在整个运行期间都会为每一个组件创建监测类,用来监测每个组件在每个运行周期是否有异步操作发生。当变化监测被执行时会发生什么呢?假象一下changeData()方法在一个异步的操作之后被执行,那么vData.name被改变,然后被传递到&v-card [vData]="vData"&&/v-card&的变化检测器来和之前的数据对比是否有改变,如果和参照数据对比有变动的话,Angular将更新视图。
因为在JavaScript语言中不提供给我们对象的变化通知,所以Angular必须保守的要对每一个组件的每一次运行结果执行变化检测,但其实很多组件的输入属性是没有变化的,没必要对这样的组件来一次变化监测,如何减少不必要的监测,我们有两种方式去实现。
Immutable Objects
不可变对象(Immutable Objects)给我们提供的保障是对象不会改变,即当其内部的属性发生变化时,相对旧有的对象,我们将会保存另一份新的参照。它仅仅依赖输入的属性,也就是当输入属性没有变动(没有变动即没有产生一份新的参照),Angular将跳过对该组件的全部变化监测,直到有属性变化为止。如果需要在Angular2中使用不可变对象,我们需要做的就是设置changeDetection: ChangeDetectionStrategy.OnPush,如下的例子:
@Component({
template: `
&h2&{{vData.name}}&/h2&
&span&{{vData.email}}&/span&
changeDetection: ChangeDetectionStrategy.OnPush
class VCardCmp {
@Input() vD
例子中,VCardCmp仅仅依赖它的输入属性,同时我们也设定了变化监测策略为OnPush来告诉Angular如果属性属性没有任何变化的话,则跳过该组件的变化监测。
Observables
和不可变对象类似,但却又和不可变对象不同,它们有相关变化的时候不会提供一份新的参照,可观测对象在输入属性发生变化的时候来触发一个事件来更新组件视图,同样的,我们也是添加OnPush来跳过子组件树的监测器,我们给这样的一个例子来帮你加深理解:
@Component({
template: '{{counter}}',
changeDetection: ChangeDetectionStrategy.OnPush
class CartBadgeCmp {
@Input() addItemStream:Observable&any&;
counter = <span style="color: #;
ngOnInit() {
this.addItemStream.subscribe(() =& {
this.counter++; // application state changed
该组件是模拟的当用户触发一个事件后增加counter这样一个场景,确切的讲,CartBadgeCmp设置了一个插值counter和一个输入属性addItemStream,当有异步操作需要更新counter的时候,将会触发一个事件流,但是输入属性addItemStream作为参考对象将不会更改,意味着该组件树的变化监测将不会发生。那怎么办?我们将怎么来通知Angular某区块有改变呢?Angular2的变化监测总是从组件树的头到尾来执行,我们其实需要的就是在整个组件树的某个发生改变的地方来做出相应即可,Angular是不知道那一块目录有改变的,但是我们知道,我们可以通过依赖注入给组件来引入一个ChangeDetectorRef,这个方法正是我们所需要的,它能标记整颗组件树的目录直到下一次变化监测的执行,代码示例如下:
class CartBadgeCmp {
constructor(private cd: ChangeDetectorRef) {}
@Input() addItemStream:Observable&any&;
counter = <span style="color: #;
ngOnInit() {
this.addItemStream.subscribe(() =& {
this.counter++; // application state changed
this.cd.markForCheck(); // marks path
当这个可监测的addItemStream触发一个事件,该事件处理句柄将会从根路径到这个已经改变的addItemStream组件来处理监测,一旦变化监测跑遍整个监测路径,它将会存储OnPush状态到整个组件树。这样做的好处是,变化监测系统将会走遍整棵树,你可以利用他们来监测树在局部是否有真正的改变,以此来做出相应的改变。
阅读(...) 评论()

我要回帖

更多关于 angular2 ngoninit 的文章

 

随机推荐