1

My goal is to use dependency injection in a child component to access its properties in the parent component. I would like to get it working like this. As you can see 'second child' in the console and template is coming from the second child and is displayed in the parent. It does NOT require the second child's component selector to be in the parent's template.

The goal for my actual application (code below) is that I will be able to toggle between true and false in the PARENT by clicking the "change valid state" button in the CHILD... despite only referencing the child through a router outlet.

I chose this solution because I did not have to use the component selector in the parent to access child data.... but I'm getting the error:

ERROR Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[NewUserComponent -> NewUserInputComponent]: 
  StaticInjectorError(Platform: core)[NewUserComponent -> NewUserInputComponent]: 
    NullInjectorError: No provider for NewUserInputComponent!
Error: StaticInjectorError(AppModule)[NewUserComponent -> NewUserInputComponent]: 
  StaticInjectorError(Platform: core)[NewUserComponent -> NewUserInputComponent]: 
    NullInjectorError: No provider for NewUserInputComponent!
    at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:979)

...when I implement this solution into my actual application (when I uncomment the code in the PARENT COMPONENT below).

Thank you for any help you can provide.

Here is the code of my actual application: PARENT COMPONENT(I have commented out the code which fixed my data sharing problem but is causing the error):

import { UserManagementModule } from './../user-management.module';
import { Component, OnInit } from '@angular/core';
import { routerTransition, slideToRight, slideToLeft } from '../../../router.animations';
import { Router, NavigationEnd } from '@angular/router';

// import { NewUserInputComponent } from './new-user-input/new-user-input.component';  //bringing accessability/form validation state from child


@Component({
  selector: 'app-new-user',
  templateUrl: './new-user.component.html',
  styleUrls: ['./new-user.component.css'],
  host: {
    'class': 'blade-container'
  },
  animations: [slideToRight()]
})
export class NewUserComponent implements OnInit {
  isRestored = true;
  minimizeVar = false;

  test = false;

  newUserInfoValidState = false;




  // constructor(private router: Router, public newuserinput: NewUserInputComponent) {
  constructor(private router: Router) {
    // this.newUserInfoValidState = newuserinput.newUserInfoValidState;
    this.test = false;
  }

  ngOnInit() {
    console.log(this.newUserInfoValidState);
  }


  getVState(componentRef) {
    this.test = componentRef.test;
  }


  private scrollToSectionHook() {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        const tree = this.router.parseUrl(this.router.url);
        if (tree.fragment) {
          const element = document.querySelector('#' + tree.fragment);
          if (element) {
            setTimeout(() => {
              element.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' });
            }, 500);
          }
        }
      }
    });
  }
}

CHILD COMPONENT:

pertinent:

  public newUserInfoValidState = true;

  changeTest() {
    this.newUserInfoValidState = !this.newUserInfoValidState;
    console.log(this.newUserInfoValidState);
  }

_

    import { EventEmitter } from '@angular/core';
import { Component, OnInit, Output } from '@angular/core';
import { slideToRight } from '../../../../router.animations';
import { Router, ActivatedRoute, UrlSegment, NavigationEnd } from '@angular/router';

@Component({
  selector: 'app-new-user-input',
  templateUrl: './new-user-input.component.html',
  styleUrls: ['./new-user-input.component.css'],
  animations: [slideToRight()]
})
export class NewUserInputComponent implements OnInit {

  test = true;


  public newUserInfoValidState = true;

  // newUserInfoComplete = false;

  // @Output() newUserInfoCompleteEvent = new EventEmitter<boolean>();

  constructor(private router: Router, r: ActivatedRoute) {
    r.url.subscribe((s: UrlSegment[]) => {
      console.log("url", s); //https://vsavkin.com/angular-router-understanding-router-state-7b5b95a12eab
    });
  }

  ngOnInit() {
  }


  changeTest() {
    this.newUserInfoValidState = !this.newUserInfoValidState;
    console.log(this.newUserInfoValidState);
  }


  // sendNewUserInfoComplete() {
  //   this.newUserInfoCompleteEvent.emit(this.newUserInfoComplete);
  // }


  displaySibling() {
    console.log(this.router);
    this.router.navigate(['../', { outlets: { newuserorginfo: ['newuserorginfo'] } }])
  }

  closeBlade() {
    this.router.navigate([{ outlets: { newuserinput: null } }]);
  }


  private scrollToSectionHook() {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        const tree = this.router.parseUrl(this.router.url);
        if (tree.fragment) {
          const element = document.querySelector('#' + tree.fragment);
          if (element) {
            setTimeout(() => {
              element.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' });
            }, 500);
          }
        }
      }
    });
  }

}

PARENT TEMPLATE:

pertinent:

  <br> injected: {{newUserInfoValidState}}

_

<!-- <app-page-header [icon]="'fa fa-users'"></app-page-header> -->
<!-- <app-page-header [heading]="'New User Request'"></app-page-header> -->



<!--BLADE LAYER 2-->
<div class="blade" [@routerTransition] [ngClass]="{'is-minimized-blade':minimizeVar, 'is-restored-blade':isRestored}">
  <div class="blade-header" [ngClass]="{'is-minimized-header':minimizeVar}">
    <h3 [ngClass]="{'is-minimized-headerText':minimizeVar}">New User Request</h3>
    <!-- BLADE Window Operations -->
    <div class="window-functions">
      <!-- Minimize -->
      <div class="inline" *ngIf="minimizeVar; else minimizeElseBlock">
        <a (click)='minimizeVar=!minimizeVar'>
          <i class="fa fa-plus-circle"></i>
        </a>
      </div>
      <ng-template #minimizeElseBlock>
        <a (click)='minimizeVar=!minimizeVar'>
          <i class="fa fa-window-minimize"></i>
        </a>
      </ng-template>
      <!-- Maximize -->
      <div class="inline" *ngIf="isRestored; else elseBlock">
        <a (click)='isRestored=!isRestored'>
          <i class="fa fa-window-restore"></i>
        </a>
      </div>
      <ng-template #elseBlock>
        <a (click)='isRestored=!isRestored'>
          <i class="fa fa-window-maximize"></i>
        </a>
      </ng-template>
      <!-- Close -->
      <a routerLink='/layout/usermanagement' routerLinkActive='router-link-active'>
        <i class="fa fa-window-close"></i>
      </a>
    </div>
  </div>

  <!-- BLADE Contents -->
  <ul>
    <li>
      <li [routerLink]="['./', { outlets: { newuserinput: ['newuserinput'] } } ]" routerLinkActive='active'>
        <h4 class="font-weight-light">1 User Information &nbsp;
          <i class="fa fa-chevron-right"></i>
          <br>test: {{test}}
          <br> injected: {{newUserInfoValidState}}
        </h4>
      </li>
      <br>
      <li>
        <!-- <li *ngIf='newUserInfoValidState'>
          <h4 class="font-weight-light">2 Organization &nbsp;
            <i class="fa fa-chevron-right"></i>
          </h4>
        </li> -->
        <li [routerLink]="[{ outlets: { newuserorginfo: ['newuserorginfo'] } } ]" routerLinkActive='active'>
          <h4 class="font-weight-light">2 Organization &nbsp;
            <i class="fa fa-chevron-right"></i>
          </h4>
        </li>
        <br>
        <li>
          <li [routerLink]="[{ outlets: { newusersupervisorinfo: ['newusersupervisorinfo'] } } ]" routerLinkActive='active'>
            <h4 class="font-weight-light">3 Supervisor &nbsp;
              <i class="fa fa-chevron-right"></i>
            </h4>
          </li>
          <br>
          <li>
            <li [routerLink]="[{ outlets: { newusersecurityinfo: ['newusersecurityinfo'] } } ]" routerLinkActive='active'>
              <h4 class="font-weight-light">4 Security Profiles &nbsp;
                <i class="fa fa-chevron-right"></i>
              </h4>
            </li>
  </ul>
</div>

<!-- To BLADE LAYER 3 -->
<router-outlet></router-outlet>
<!-- <router-outlet name="newuserinput" (newUserInfoCompleteEvent)="receiveNewUserInfoComplete($event)"></router-outlet> -->
<router-outlet (activate)='getVState($event)' name="newuserinput"></router-outlet>
<router-outlet name="newuserorginfo"></router-outlet>
<router-outlet name="newusersupervisorinfo"></router-outlet>
<router-outlet name="newusersecurityinfo"></router-outlet>
<!-- end -->



<!-- For ViewChild: bringing form validity state from child -->
<!-- <app-new-user-input style="display:none;"></app-new-user-input> -->
<!-- can't do this because putting the child component selector in the parent disables the childs router outlet -->

CHILD TEMPLATE:

pertinent:

    <button (click)="changeTest()">Change Valid State</button>

_

   <div class="blade" [@routerTransition]>
      <div class="blade-header">
        <h3>User Information</h3>
        <div class="window-functions">
          <i class="fa fa-window-minimize"></i>
          <i class="fa fa-window-restore"></i>
          <i class="fa fa-window-maximize"></i>
          <a [routerLink]="['../',{ outlets: { newuserorginfo: ['newuserorginfo'] } } ]" routerLinkActive='router-link-active'>
            <!-- <a routerLink='/layout/usermanagement/(newuser:newuser)' routerLinkActive='router-link-active'> -->
            <i class="fa fa-window-close"></i>
            <!-- <i (click)='closeBlade()' class="fa fa-window-close"></i> -->
          </a>
        </div>
      </div>
      <form action="submit">
        <!-- <label for="firstname">First Name:</label> -->
        User type:
        <br>
        <select required>
          <option value="" hidden disabled selected data-default></option>
          <option value="Customer">Customer</option>
          <option value="Organization Administrator">Organization Administrator</option>
          <option value="Customer Service Representative">Customer Service Representative</option>
          <option value="Customer Service Administrator">Customer Service Administrator</option>
        </select>
        <br>
        <br> First name:
        <br>
        <input required type="text" name="firstname" value="Richard">
        <br>
        <br> Last name:
        <br>
        <input required type="text" name="lastname" value="Dawkins">
        <br>
        <br> Cell phone:
        <br>
        <input required type="tel" name="cellphone" value="(585) 271-8888">
        <br>
        <br> Office phone:
        <br>
        <input required type="tel" name="officephone" value="(585) 271-8887">
        <br>
        <br> Fax:
        <br>
        <input type="tel" name="fax" value="(585) 271-8886">
        <br>
        <br> City:
        <br>
        <input required type="text" name="city" value="City">
        <br>
        <br> State:
        <br>
        <input required type="text" name="state" value="New York">
        <br>
        <br> Requester title:
        <br>
        <input required type="text" name="requester" value="Requester title">
        <br>
        <br>
        <br>
        <button (click)="changeTest()">Change Valid State</button>

        <div *ngIf="newUserInfoValidState; else allowOrgInput">
          <!-- (click)='ngSubmit'  -->
          <!-- <a (click)='displaySibling()' routerLinkActive='router-link-active'>
            <button autofocus class="next-button">Next</button>
          </a> -->
          <a [routerLink]="['../',{ outlets: { newuserorginfo: ['newuserorginfo'] } } ]" routerLinkActive='router-link-active'>
            <button autofocus class="next-button">Next</button>
          </a>
        </div>
        <ng-template #allowOrgInput>
          <a>
            <button autofocus href="#" class="next-button" disabled>Next</button>
          </a>
        </ng-template>


      </form>
    </div>
    <router-outlet></router-outlet>
    <router-outlet name="newuserorginfo"></router-outlet>

This is my other related stackoverflow question.

imnickvaughn
  • 2,774
  • 9
  • 25
  • 42

0 Answers0