1

I'm tinkering with Angular2 and have found I am unable to initially populate a html input with values from the controller when the class Constructor is called. Strangely, the input does populates with the bound default data after the user tries to make a change to the value of the input. How do I initially set the value of a html input when the view first loads so that the input is pre-populated with text ?

This is the HTML template...

<div class="row">
    <div class="col-md-3">Username:</div>
    <div class="col-md-9"><input  [(ngModel)]="CurrentUser.Username" /></div>    
</div>
<div class="row">
    <div class="col-md-3">Password:</div>
    <div class="col-md-9"><input  [(ngModel)]="CurrentUser.Password" /></div>    
</div>
<div class="row">
    <div class="col-md-3"></div>
    <div class="col-md-9"><button class="btn btn-sm btn-primary"     (click)="signin()">Sign in</button></div>    
</div>

Here is the Component (Controller)...

import {Component} from 'angular2/core'
import {UserService} from '../services/user-current.service';

@Component({
    templateUrl: './app/templates/signin.template.html'
})


export class SigninComponent { 
    private CurrentUser: UserService;    
    constructor(private userService:UserService) {
        this.CurrentUser = userService
    }  
}

for the sake of reference, I have included the service that loads the data I want displayed in the html input...

import {Injectable} from "angular2/core";

@Injectable()

export class UserService {

    private Username = "TEST";
    private Password = "Password";
    private UserID = 1;
    private LoggedIn = true;

    constructor() {}

    setUser(userObject) {
        this.Username = userObject.Username;
        this.Password = userObject.Password;
        this.UserID = userObject.UserID;
        this.LoggedIn = userObject.LoggedIn;
    }

}

also for complete clarity, here is the navigation component which is loading the view (including the SigninComponent)...

import {Component} from 'angular2/core'
import {RouteConfig,ROUTER_DIRECTIVES} from 'angular2/router'
import {UsersListComponent} from './users-list.component'
import {DashboardComponent} from './dashboard.component'
import {SigninComponent} from './signin.component'
import {UserService} from '../services/user-current.service';

@Component({
    selector: 'nav-top',
    templateUrl: './app/templates/nav-top.template.html',
    directives: [ROUTER_DIRECTIVES]
})


@RouteConfig([
    {path: '/users', name:'UsersList', component: UsersListComponent},
    {path: '/signin', name:'Signin', component: SigninComponent},
    {path: '/', name:'Dashboard', component: DashboardComponent, useAsDefault: true}
])

export class NavTopComponent{    
    private CurrentUser;    
    constructor(private userService:UserService) {
        this.CurrentUser = userService
    }       
}

I believe the issue is related to the use of ngOnInit which never seems to fire as the console.log() within the function does not fire. Here is an updated look at the code I'm now using for the SigninComponent...The ngOnInit does not fire.

import {Component,OnInit} from 'angular2/core'
import {UserService} from '../services/user-current.service';

@Component({
    selector: 'SigninComponent',
    templateUrl: './app/templates/signin.template.html',
    providers: [UserService]
})

export class SigninComponent implements OnInit{ 

    public CurrentUser;

    constructor(public userService:UserService) {   
        console.log("constructor");   
    } 

    ngOnInit() {
        this.CurrentUser = this.userService;
        console.log("ngOnInit");
    }

}
Craig
  • 387
  • 4
  • 16
  • Nope. No errors...I've been copying my code from various tutorials online. I added the selector and providers properties, but that has made no difference. – Craig Apr 29 '16 at 01:49
  • SigninComponent is being loaded as a view using @RouteConfig. What I find most strange is that the html input values are updated, but only when the user enters a value into either of the inputs or clicks the button. – Craig Apr 29 '16 at 02:06
  • Strange it works on plunker but not on my localhost – Craig Apr 29 '16 at 02:07
  • Thanks for your suggestions. I changed the private to public in the UserService and also in the SigninComponent for good measure, but it's still not initializing when the view is loaded. – Craig Apr 29 '16 at 02:26
  • OK, I removed the private declaration from the constructor, but that made no difference. I also removed the line this.CurrentUser = userService but that then throws an error that Username is undefined. It's like the constructor is not running until after a key stroke is detected on the input element. – Craig Apr 29 '16 at 02:53
  • Where do you set `UserService` as a provider? It should be in a `providers` somewhere. Or at least in the bootstrap, such as `bootstrap(MainComponent, [UserService])`. – acdcjunior Apr 29 '16 at 03:09
  • My recommendation: do that initializing in `ngOnInit()`. Something like: `ngOnInit() { this.CurrentUser = this.userService; }` – Richard Pressler Apr 29 '16 at 03:14
  • I've tried adding the UserService to the providers property of the Component. No change in the behaviour. I also have the UserService added to the bootstrap array already. As the input successfully populates with the value from the SigninComponent AFTER the user initiates an event on the input by either losing focus or typing a character, I can only assume that everything is wired up correctly and the data is available. The problem appears to be that the constructor does not happen until AFTER the user initiates an event on the view. – Craig Apr 29 '16 at 03:18
  • Look, I did the best I could to reproduce your example in a plunker: http://plnkr.co/edit/trpkl8WaX6gFcP8YLy9J?p=preview And it works... Notice the main difference between mine and yours is that you have no `providers` in the `@Component` decorator of `NavTopComponent` and I do. If I remove it, I get an exception (that's why I asked where did you provide/bootstrap your `UserService`, it could be related to the issue). – acdcjunior Apr 29 '16 at 03:18
  • Try adding some `console.log()`s to the constructor to see when it actually is executed. – acdcjunior Apr 29 '16 at 03:19
  • I promise this is happening: everything is fine except the view doesn't detect the change and update. It happened to me, and the issue was that the async action was happening in a place there the angular2 dirty checker just isn't paying attention (your constructor). Put your code in `ngOnInit`. – Richard Pressler Apr 29 '16 at 03:21
  • Removing the constructor setting of this.CurrentUser = userService from within the Constructor and adding the ngOnInit function made no change to the behaviour. But thanks anyway. – Craig Apr 29 '16 at 03:23
  • Console.log() shows that the Constructor is indeed firing when the view is loaded, but the input is not being initiated with the value. – Craig Apr 29 '16 at 03:24
  • Adding a console.log() to the ngOnInit() function shows that the ngOnInit never fires. I have imported the OnInit and added 'implements OnInit' to the export class. – Craig Apr 29 '16 at 03:35
  • Show your `` tags. It may have something to do with their order. See https://github.com/angular/angular/issues/4809 – acdcjunior Apr 29 '16 at 04:09
  • See also: https://github.com/angular/angular/issues/8012#issuecomment-208940860 – acdcjunior Apr 29 '16 at 04:15
  • 1
    @acdcjunior has found the final piece of the puzzle. The – Craig Apr 29 '16 at 04:15

1 Answers1

1

There were two parts to my problem.

Firstly, the constructor happens before some sort of Angular2 life cycle stuff that is way above my pay grade. Essentially, you need to import OnInit and then Implement OnInit in your exported class. Once you do this, you can use the ngOnInit function.

Secondly, this ngOnInit will not fire unless your tags are in the correct order. Firstly, here is the final Component code...

import {Component,OnInit} from 'angular2/core'
import {UserService} from '../services/user-current.service';

@Component({
    selector: 'SigninComponent',
    templateUrl: './app/templates/signin.template.html',
    providers: [UserService]
})



export class SigninComponent implements OnInit{ 

    public CurrentUser;

    constructor(public userService:UserService) {   
        console.log("constructor");   
    } 

    ngOnInit() {
        this.CurrentUser = this.userService;
        console.log("ngOnInit");
    }

}

Lastly, here is my scripts from the index.html in the order I needed to make it all work...

<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>     
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>

Thanks to everyone who helped to find this answer. Credit should go to you...

Craig
  • 387
  • 4
  • 16