1

I'm learning Angular and I was trying to display a submitted form data in a different component than the one the form actually is.

So that's how I set up my app: I built the form in a component, created a user model, created a service that should provide cross component interaction and created an output component. The structure looks like this:

app-component folder

app.component.html,ts,css; app.module etc. files

home-form.component folder

home-form.component.hmtl,ts,css files

output-component folder

output.component.html,ts,css files

shared folder

user-module.ts, user-data.service.ts

That's my code so far:

that's my user model

export class User {
    public name: string;
    public surname: string;
    public gender: string;
    public email: string;

    constructor(name: string, surname: string, gender: string, email: string) {
      this.name = name;
      this.surname = surname;
      this.gender = gender;
      this.email = email;
    }
  }

HTML of form component

<div class="container">
  <form class="mt-5" (ngSubmit)="onSubmit()" #myForm="ngForm">
    <div class="row d-flex justify-content-center">

      <div class="col-md-6">
        <label for="name">Name</label>
        <input 
          type="text" 
          id="name" 
          class="form-control" 
          placeholder="start typing here..." 
          ngModel 
          name="name"
          required
          #name="ngModel">
        <span class="help-block text-danger" *ngIf="!name.valid && name.touched">*Please, enter a valid name</span>
      </div>

      <div class="col-md-6">
        <label for="surname">Surname</label>
        <input 
          type="text" 
          id="surname" 
          class="form-control" 
          placeholder="start typing here..." 
          ngModel 
          name="surname"
          required
          #surname="ngModel">
          <span class="help-block text-danger" *ngIf="!surname.valid && surname.touched">*Please, enter a valid surname</span>
      </div>

      <div class="col-md-6 mt-2">
        <label for="gender">Gender</label>
        <div class="form-check form-check-inline d-block">
          <input 
              class="form-check-input" 
              type="radio" 
              name="gender" 
              id="maleGender" 
              value="male" 
              ngModel
              required>
          <label class="form-check-label" for="inlineRadio1">Male</label>
        </div>
        <div class="form-check form-check-inline">
          <input 
            class="form-check-input" 
            type="radio" 
            name="gender" 
            id="femaleGender" 
            value="female" 
            ngModel
            required>
          <label class="form-check-label" for="inlineRadio2">Female</label>
        </div>
      </div>

      <div class="col-md-6 mt-2">
        <label for="email">E-mail</label>
        <input 
          type="email" 
          id="email" 
          class="form-control" 
          placeholder="start typing here..." 
          ngModel 
          name="email"
          required
          email
          #email="ngModel">
          <span class="help-block text-danger" *ngIf="!email.valid && email.touched">*Please, enter a valid e-mail</span>
      </div>

      <div class="mt-5">
        <button 
          type="submit" 
          class="btn btn-primary"
          [disabled]="!myForm.valid">Submit</button>
      </div>

    </div>
  </form>
</div>

TS file of that component

import { Component, OnInit, ViewChild, } from '@angular/core';
import { userDataService } from '../shared/user-data.service';
import { NgForm } from '@angular/forms';
import { User } from '../shared/user.model';

@Component({
  selector: 'app-home-form',
  templateUrl: './home-form.component.html',
  styleUrls: ['./home-form.component.css'],
  providers: [userDataService]
})

export class HomeFormComponent implements OnInit {


 @ViewChild('myForm', { static: false }) userForm: NgForm;    
  constructor(private userData: userDataService) {}    
  ngOnInit() {}

  onSubmit(){
    const newUser = new User(this.userForm.value['name'], this.userForm.value['surname'], this.userForm.value['gender'], this.userForm.value['email']);
    this.userData.addUser(newUser);
  }
}

then the user-data service

import { User } from './user.model';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable()

export class userDataService {

    private myUser = new Subject<User>();
    myUserObservable = this.myUser.asObservable();

    addUser(user: User){
        this.myUser.next(user);
        //console.log(user);      
    }
}

and until here I still can log the object user with all the data submitted in the form.

that's the ts file of the comonent that should output the user. What am I doing wrong here?

import { Component, OnInit, OnDestroy } from '@angular/core';
import { userDataService } from '../shared/user-data.service';
import { User } from '../shared/user.model';

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css'],
  providers: [userDataService]
})
export class UserProfileComponent implements OnInit, OnDestroy {
  user: User;


  constructor(private userData: userDataService) {}

  ngOnInit() {
    this.userData.myUserObservable.subscribe(
      (user: User) => {
        this.user = user;
        console.log(user);
      }
    );
  }

  ngOnDestroy(){

  }
}

ngOnDestroy is implemented to unsubscribe. the console.log is just a way to test if the data arrives, I wanted to display the data via string interpolation. Something like that

<div class="container">
<div class="row">
    <div class="col-md-12">
        <h3>You've created your profile!</h3>
        <p>Name: {{ user.name }}</p>
        <p>Surname: {{ user.surname }}</p>
        <p>Gender: {{ user.gender }}</p>
        <p>E-mail: {{ user.email }}</p>
    </div>
</div>

I would really appreciate any help and suggestion cause I'm really stuck here. Thank you all!

1 Answer 1

1

The problem is that you inject two different instances of userDataService - one for each component.

  1. Remove providers: [userDataService] from your components decorators. You can read more about it at Official Angular documentation: Limiting provider scope with components

So, your Component decorators should look like that:

@Component({
  selector: 'app-home-form',
  templateUrl: './componentname.html',
  styleUrls: ['./componentname.css']
})
  1. The Injactable decorator of your userDataService should look like this

    @Injectable({ providedIn: 'root' }) export class userDataService { ....

In this case your service will be injected as a singleton to any of your components (this injectable should be provided in the 'root' injector, which will be the application-level injector in most apps.)

Sign up to request clarification or add additional context in comments.

2 Comments

Exactly this. You need to keep just a single instance of service in order to use it as a shared service.
So basically providing the service in every component creates different istances of the same service. I didn't know that, thank you guys. Now works perfectly. I didn't use providedIn, I just provided the services in the the app.module.ts file and it autromatically was provided to all components. Thanks expecially for the useful links ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.