Imagine we have this component structure:
app1
-- app1.component.html
-- app1.component.ts
parent1
parent2
app2
-- app2.component.html
-- app2.component.ts
How could I reuse the app2 component in the app1? For example, reuse a table (both HTML and logic on typescript) instead of copy and paste code.
I have searched for solutions like ng-template, but failed. Also, call the tag didn't work as well.
If the tag didn't work inside app1 I assume that you are importing app2 component inside another module. If we want to use component over multiple modules you need to import app2 ONLY in shared module then import that module to modules where you want to have that component.
Make sure to export that component inside shared module.
Need to use componentFactoryResolver https://angular.io/guide/dynamic-component-loader
For example you want to use ThirdComponent inside FirstComponent
//HTML
<section #firstComp></section>
//TS
import { AfterViewInit, Component, ComponentFactoryResolver, OnInit, ViewChild, ViewContainerRef } from '#angular/core';
import { ThirdComponent } from '../third/third.component';
#Component({
selector: 'app-first',
templateUrl: './first.component.html',
styleUrls: ['./first.component.scss']
})
export class FirstComponent implements OnInit, AfterViewInit {
#ViewChild('firstComp',{read: ViewContainerRef}) firstComp: ViewContainerRef;
constructor(
private componentFactoryResolver: ComponentFactoryResolver
) { }
ngOnInit(): void {
}
ngAfterViewInit() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ThirdComponent);
const componentRef = this.firstComp.createComponent<ThirdComponent>(componentFactory);
}
}
Related
I have a click-outside directive on a component which is created multiple times on the page. The component has a drop down I created which on click-outside of it closes the drop down. The problem is that when I have ~100 components with this drop down the click-outside directive gets called ~100 times for each component and this causes the simple action of opening a drop down to take too long.
This is the directive:
import {Component, NgModule, VERSION, Directive, ElementRef, Output, EventEmitter, HostListener} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
#Directive({
selector: '[appClickOutside]'
})
export class ClickOutsideDirective {
#Output() clickOutside: EventEmitter<any> = new EventEmitter();
constructor(private _elementRef: ElementRef) {
}
#HostListener('document:click', ['$event.target'])
public onClick(targetElement) {
const isClickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!isClickedInside) {
this.clickOutside.emit(true);
}
}
}
Is there a way to stop the directive from running for all the drop downs?
thanks!
I have an Angular service that looks like this
#Component({})
#Inject(ChromeDataService)
#Injectable()
export class MainDataService {
}
when I run ng build --prod, I get this error
ERROR in : No template specified for component MainDataService
my only guess is that an Angular service does not need to be a component? So I removed the #Component annotation, but then I get this:
ERROR in : Unexpected value 'MainDataService in
/home/.../main.ts'
declared by the module 'SharedModule in
/home/.../src/app/shared.module.ts'.
Please add a #Pipe/#Directive/#Component annotation.
Uh, how do I create a service in Angular5?
To use an angular service properly you only need injectable()
Here's an example
#Injectable()
export class myService {
//some logic
}
then in your app.module or in a feature module you add the service in the providers array and to have the angular DI handle the service for you.
#Component({
usual stuff with template, selector, css})
export class someComponent {
constructor(private myService: MyService){}
}
The constructor will tell angular to auto-magically inject the service you need in.
your service only needs the #Injectable() decorator.like this
import {Injectable} from '#angular/core'
import {Router} from '#angular/router'
#Injectable()
export class AuthService {
constructor(private router: Router) {}
}
That's how I declare services in Angular:
#Injectable()
export class AuthService {
...
}
I think you only need #Injectable decorator. Maybe your problem comes from shared.module.ts.
I am trying to get #Input to work with Typescript in Angular 2. I am getting the following error and I don't understand why.
[ts] Cannot find name 'Input'. any
Below is the code from that component.
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-item',
templateUrl: './app-item.component.html',
styleUrls: ['./app-item.component.css']
})
export class AppItemComponent implements OnInit {
#Input item; //TypeScript complains here.
constructor() { }
ngOnInit() {}
}
The project and component were both created using the Angular CLI. Why can't TypeScript figure out the #Input decoration?
You need to add this,
import {Component, Input} from '#angular/core';
In my case I already had:
import { Component, OnInit } from '#angular/core';
So, when I tried to use:
import {Component, Input} from '#angular/core';
It didn't work.
What I had to do was import like that (Without the word Component)
import { Input } from '#angular/core';
And it worked just fine
#Marcielli, it didn't work because of the double import of Component. If you change the import statement to the following
import { Component, OnInit, Input } from '#angular/core';
You would be just fine. Optionally adding the Input module in a separate import statement is perfectly fine, but you should generally stick with either importing all components from a module in one statement, or each in separate statements.
I have a pages.service.ts
import { Injectable } from '#angular/core';
import { ApiService } from '../../apiService/api.service';
import { Playlists } from '../shared/playlists.model';
#Injectable()
export class PagesService {
private currentPlaylists: Subject<Playlists> = new BehaviorSubject<Playlists>(new Playlists());
constructor(private service: ApiService) {
}
}
This pages service needs another service called ApiService, I inject the way as shown above, it works.
I bootstrap ApiService in main.ts
import { ApiService } from './apiService/api.service';
import { AppComponent } from './app.component';
bootstrap(AppComponent,[
disableDeprecatedForms(),
provideForms(),
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
ApiService
]).catch((err: any) => console.error(err));;
But When I try to inject the PagesService to another component, it gives me error, No Provider for PagesService.
I write that component like this.
import { Component, ViewChild, ElementRef, Input, Output, EventEmitter } from '#angular/core';
import { CORE_DIRECTIVES } from '#angular/common';
import { MODAL_DIRECTVES, BS_VIEW_PROVIDERS } from 'ng2-bootstrap/ng2-bootstrap';
import { ApiService } from '../../apiService/api.service';
import { PagesService } from '../../pages/shared/pages.service';
#Component({
selector: 'assign-playlist-modal',
exportAs: 'assignModal',
providers: [ PagesService ],
directives: [MODAL_DIRECTVES, CORE_DIRECTIVES, FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES ],
viewProviders: [BS_VIEW_PROVIDERS],
styleUrls: ['app/channel/shared/assignPlaylist.css'],
templateUrl: 'app/channel/modals/assignPlaylistModal.html'
})
export class AssignPlaylistModalComponent {
constructor(private apiService: ApiService, private pageService: PagesService, fb: FormBuilder) {
}
}
Update: this is my file structure
channel/
--channel.component.ts
--shared/
----assignPlaylist.modal.ts
----addPlaylist.modal.ts
pages/
--shared/
----pages.service.ts
--pages.component.ts
Channel component is the parent of addPlaylist, addPlaylist is the parent of assignPlaylist. This structure will not work
ChannelComponent
|
AddPlaylistComponent
|
AssignPlaylistComponent ----PagesService----ApiService
I found one solution but don't know why I need to do that,
I add the provider 'PagesService' to ChannelComponent, and also the AssignPlaylistComponent, it will work, no errors.
Even this will work
ChannelComponent ----PagesService-------------
|
AddPlaylistComponent
|
AssignPlaylistComponent ----ApiService---------------
However, I just want to use PagesService in AssignPlaylistComponent, so I think it not make sense to import PagesService in channelcomponent.ts and make a providers array in it.
It is a bit strange, but only components can configure dependency injection in Angular (well, and bootstrap()). I.e., only components can specify providers.
Each component in the component tree will get an associated "injector" if the component has a providers array specified. We can think of this like an injector tree, that is (normally much) sparser than the component tree. When a dependency needs to be resolved (by a component OR a service), this injector tree is consulted. The first injector that can satisfy the dependency does so. The injector tree is walked up, toward the root component/injector.
So, in order for your PagesService to inject a ApiService dependency, that ApiService object first has to be registered with an injector. I.e., in a component's providers array. This registration must occur somewhere at or above the component where you want to use/inject the ApiService .
Your service should then be able to inject the registered ApiService object, because it will find it in the injector tree.
See also Angular 2 - Including a provider in a service.
I have a component that uses a service. The component looks something like this:
#Component({
moduleId: module.id,
selector: 'test',
providers: [HTTP_PROVIDERS, TestService]
})
export class TestComponent implements OnInit {
constructor(private _testService:TestService) {
}
As you can see, I added the HTTP_PROVIDERS provider in my component. This worked since the DI is now aware of the http classes. However, it was my TestService that was really using the Http class, not my TestComponent.
#Injectable()
export class TestService {
constructor(private _http:Http) {
}
I felt that since it is the service using the Http class, it should be the one including the providers in itself. The TestComponent wouldn't know what providers TestService would need.
Since the service class doesn't have that component decorator, I'm not sure how I can actually add providers to it. How can I add providers to a Service class?
What you can do is,
Inject HTTP_PROVIDERS into bootstrap function ,
import {HTTP_PROVIDERS} from '#angular/http';
bootstrap(AppComponent,[HTTP_PROVIDERS]);
and in your service,
import {Http} from '#angular/http';
#Injectable()
export class TestService {
constructor(private _http:Http) {}
}