3

I would like to use a service in my class (model)

Use Service in my Class

movie-list.component.ts

import { Component, OnInit } from '@angular/core';
import {Movie} from "../../models/movie";
import {MovieService} from "../../services/movie.service";

@Component({
  selector: 'movie-list',
  templateUrl: './movie-list.component.html',
})

export class MovieListComponent implements OnInit {
  public movies = [];
  public movie: Movie;

  constructor(private movieService: MovieService) {
      this.movie = new Movie();
  }

  ngOnInit() {
    console.log(this.movie.getMovies());
    this.movies = movie.getMovies();
  }
}

movie-list.component.html

<div *ngFor="let movie of movies">
    {{ movie.title }}

    <div *ngFor="let actor of movie.actors">
        {{ actor.name }} - {{ actor.getOscar() }}
    </div>
</div>

movie.ts (movie model)

import { Actor } from "./actor";

export class Movie {

    constructor(title: string = '', actors: any = []) {
        this.title = title;
        this.actors = [];
    }

    title: string = '';
    actors: any = [];
    movieService;

    setModel(obj) {
        Object.assign(this, obj);
    }

    addActor(actor) {
        this.actors.push(actor);
    }

    build(data) {
        let movie = new Movie(
            data.title,
            data.actors
        );

        movie.setModel({ actors: []});

        data.actors.forEach((actor) => {
            let new_actor = new Actor(
                actor.firstname,
                actor.lastname
            );

            movie.addActor(new_actor);
        });

        return movie;
    }

    getMovies() {
        let movies: any[];

        this.movieService.getMovies().subscribe(
            data => {
                data.map((result) => {
                    movies.push(this.build(result));
                });

                return movies;
            },
            error => {
                console.error(error);
            }
        )
    }
}

actor.ts (actor model)

export class Actor {

    constructor(firstname: string, lastname: string) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    firstname: string;
    lastname: string;

    setModel(obj) {
        Object.assign(this, obj);
    }

    getOscar() {
        return '10 oscars';
    };
}

movie.service.ts

import { Injectable } from '@angular/core';
import {Http} from "@angular/http";
import 'rxjs/add/operator/map';

@Injectable()
export class MovieService {
  constructor(private http:Http) { }

    getMovies() {
    return this.http
        .get('data/movies.json')
        .map(res => res.json());
  }

}

movies.json

[
  {
    "title": "title1",
    "actors": [
      {
        "firstname": "firstname10",
        "lastname": "lastname10"
      },
      {
        "firstname": "firstname11",
        "lastname": "lastname11"
      }
    ]
  },
  {
    "title": "title2",
    "actors": [
      {
        "firstname": "firstname20",
        "lastname": "lastname20"
      },
      {
        "firstname": "firstname21",
        "lastname": "lastname21"
      }
    ]
  }
]

I would like to "inject/use" my service in my Class to use this workflow:

  1. Component (call getMovies in my Class)
  2. Class (call getMovies in my service, with response build my object (Movie, Actor) with this.build
  3. Service (call my API, return JSON, without type object (Movie, Actor)

I tried to use @inject in my Class but it doesn't work from this post How to inject Service into class(not component)

I would like to know the best practice to do this.

With this code, I have an error :

enter image description here

Modification

Added this in my component :

constructor(private movieService: MovieService) {
  this.movie = new Movie();
  this.movie.movieService = movieService; // add this line
}

I have an error on this line in my component :

ngOnInit() {
    console.log(this.movie.getMovies());
    this.movies = movie.getMovies(); // error Type 'void' is not assignable to type 'any[]'.
}

I would like to conserve the subscribe in the model to have a clean code in my component

enter image description here

SOLVED

movie-list.component.ts

import { Component, OnInit } from '@angular/core';
import { Movie } from "../../models/movie";
import { MovieService } from "../../services/movie.service";

@Component({
  selector: 'movie-list',
  templateUrl: './movie-list.component.html',
})

export class MovieListComponent implements OnInit {
    public movie: Movie;
    public movies = [];

  constructor(private movieService: MovieService) {
      this.movie = new Movie('', []);
      this.movie.movieService = movieService;
  }

  ngOnInit() {
      this.movies = this.movie.getMovies();
  }
}

movie.ts

import { MovieService } from "../services/movie.service";
import { Actor } from "./actor";

export class Movie {

    constructor(
        private title: string = '',
        private actors: any[] = [],
    ) { }

    public movieService : MovieService;

    setModel(obj) {
        Object.assign(this, obj);
    }

    addActor(actor) {
        this.actors.push(actor);
    }

    build(data) {
        const movie = new Movie(
            data.title,
            data.actors,
        );

        movie.setModel({ actors: [] });

        data.actors.forEach((actor) => {
            const new_actor = new Actor(
                actor.firstname,
                actor.lastname
            );

            movie.addActor(new_actor);
        });

        return movie;
    }

    getMovies() {
        let movies = [];

         this.movieService.getMovies().subscribe(
            data => {
                data.map((result) => {
                    movies.push(this.build(result));
                });
            },
            error => {
                console.error(error);
            }
        );

        return movies;
    }
}
6
  • amy i ask the need for injecting service in class, as it would just do fine in component. you should use class as model Commented Aug 30, 2017 at 9:25
  • thx for your comment ! I would like a clean code in my component, for example : without subscribe in my component Commented Aug 30, 2017 at 9:28
  • Your last error is about the getMovies() method that returns nothing. You are trying to return the movies, but you it is in the Subscription, so it doesn't return anything, and thus you cannot affect nothing to this.movies. Commented Aug 30, 2017 at 11:05
  • 1
    Well the best thing would be to build your Movie model from your first call, and then return an Observable of that result. You can then subscribe to that observable from your component. There is not much other choice, since the data is asynchronous Commented Aug 30, 2017 at 11:55
  • 1
    thx @AlexBeugnet ! I updated my post and validated your precious help ! Commented Aug 30, 2017 at 12:22

2 Answers 2

2

You should read this about Smart and Presentation (Dumb) components. This is by far the best practice there is, as the whole angular data management and detection changes revolves around it.

http://blog.angular-university.io/angular-2-smart-components-vs-presentation-components-whats-the-difference-when-to-use-each-and-why/


Your error comes from the two parameters you set in the constructor, as these should be sent either by the @Input decorator or fetched from a service. You don't do things like let foo = new MovieComponent(.., ...); so this can't work.

Read this from the official documentation. It will also help you for setting up the smart / dumb component structure :

https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding

EDIT : To make your service work, simply pass it as a dependency into your MovieComponent constructor like to :

constructor(
  private movieService: MovieService
) {}

Do not forget to provide your MovieService in your (I expect) MovieModule or wherever the MovieComponent is declared (in the same module)


EDIT2 : Here are the code parts that worked for me. The model class would need to be reworked, with a method to send back the movies built asynchronously, but even then, I'd find that model not very useful. You are better off using only interfaces and a service. Also, this is obviously not a smart/dumb component interaction, so some upgrades will be needed to achieve what you want.

MovieListComponent

export class MovieListComponent implements OnInit {
  public movies = [];
  public movie: Movie;

  constructor(private movieService: MovieService) {
    this.movie = new Movie('', []);
  }

  ngOnInit() {
    this.movie.movieService = this.movieService;
    this.movie.getMovies().subscribe(
      data => {
        data.map((result) => {
          this.movies.push(this.movie.build(result));
        });
      }
    );
  }
}

Movie (model)

export class Movie {
  movies: any[];
  movieService: MovieService

  constructor(
    private title: string = '',
    private actors: any[] = [],
  ) { }

  setModel(obj) {
    Object.assign(this, obj);
  }

  addActor(actor) {
    this.actors.push(actor);
  }

  build(data) {
    const movie = new Movie(
      data.title,
      data.actors,
    );

    movie.setModel({ actors: [] });

    data.actors.forEach((actor) => {
      const new_actor = new Actor(
        actor.firstname,
        actor.lastname
      );

      movie.addActor(new_actor);
    });

    return movie;
  }

  getMovies() {
    return this.movieService.getMovies();
  }
}

Module

@NgModule({
  imports: [
    HttpModule,
    CommonModule
  ],
  declarations: [
    MovieListComponent
  ],
  providers: [
    MovieService
  ]
})
export class MainModule { }
Sign up to request clarification or add additional context in comments.

7 Comments

Thx @Alex Beugnet for your comment ! Do you have an exemple to the constructor please ? I removed my constructor in my class to replace with your constructor, but in my component, the instance doesn't work because I don't have the parameter
Since you did everything around your model, you should just change it so that you can create a new instance of your movie class (in that case you will use new). See this post which shows a good example : stackoverflow.com/questions/38398877/…. Be aware that even though your MovieListComponent is still considered a smart component in that case since basically it does (through the link to your Movie Model) the call to the service.
Thx for your help, I updated my post (component) : I applied your recommandation from your link ! But I always confused to use my service in my class..
Since you want to make your service call in the model, you need to pass the MovieService as a parameter when you do new Movie(), and your Movie model class needs to get the reference from the constructor
Thx @Alex Beugnet ! I updated my post (modification part, bottom post). Can you confirm this syntax please ? Because I can't set my result in an array
|
-2

pass a parent injector that provides them

constructor( parentInjector:Injector ){

  let injector = ReflectiveInjector.resolveAndCreate ( [ ProductService ] );

  this.productService = injector.get(ProductService, parentInjector);
}

1 Comment

Please edit your answer and format it to clearly show the code. When you are in the editor, you will see on the right side of the screen the help information on how to edit it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.