I have go through with following questions but didn't found any solution.
Angular 4 directive error: Can't resolve all parameters for directive
Can't resolve all parameters for custom directive
I have made a custom validation directive to validate unique permalink. this code working fine but when i try to create a build for production then it gives me following error:-
ERROR in : Can't resolve all parameters for
UniquePermalinkValidatorDirective in
E:/Manish/Projects/ampleAdmin/src/app/shared/permalink-validation.directive.ts:
([object Object], ?).
permalink-validation.directive.ts
import { Directive } from '#angular/core';
import { AsyncValidator, AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn } from '#angular/forms';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import * as qs from 'qs';
import { PageService } from '../services/page.service';
import { IPage } from '../client-schema';
export function UniquePermalinkValidator(pageService: PageService, page: IPage): AsyncValidatorFn {
return (ctrl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
if (!(ctrl && ctrl.value)) { return null; }
const cond: any = {
where: {
permalink: ctrl.value
}
};
if (page && page.id) {
cond.where.id = { nin: [page.id]};
}
const query = qs.stringify(cond, { addQueryPrefix: true });
return pageService.getPageCount(query).pipe(
map(res => {
return res && res.count ? { uniquePermalink: true } : null;
})
);
};
}
#Directive({
selector: '[appUniquePermalink]',
providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: UniquePermalinkValidatorDirective, multi: true }]
})
export class UniquePermalinkValidatorDirective implements AsyncValidator {
constructor(private pageService: PageService, private page: IPage) { }
validate(ctrl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return UniquePermalinkValidator(this.pageService, this.page)(ctrl);
}
}
page.component.ts
import { Component, OnInit, TemplateRef } from '#angular/core';
import * as _ from 'lodash';
import { NotifierService } from 'angular-notifier';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { IPage } from 'src/app/client-schema';
import { Utils } from 'src/app/shared/utils';
import { PageService } from 'src/app/services/page.service';
import { UniquePermalinkValidator } from 'src/app/shared/permalink-validation.directive';
#Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.css']
})
export class PageComponent implements OnInit {
private notifier: NotifierService;
pageForm: FormGroup;
pageDetail: IPage;
isAddFold = false;
isEditFold = false;
editFoldIndex = -1;
constructor(
private pageService: PageService,
private notifierService: NotifierService,
private modalService: BsModalService,
private formBuilder: FormBuilder,
) {
this.notifier = notifierService;
}
initPageForm() {
this.pageForm = this.formBuilder.group({
name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(250)]],
permalink: ['', [Validators.required], UniquePermalinkValidator(this.pageService, this.pageDetail)],
folds: [
[]
],
data: null,
status: true
});
}
}
I am using single form for Add/Edit page so i have to require records details to allow permalink on editing a page.
is there any way to pass current page details to directive?
Given
export function UniquePermalinkValidator(pageService: PageService, page: IPage): AsyncValidatorFn {
// ...
}
And given
#Directive({
selector: '[appUniquePermalink]',
providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: UniquePermalinkValidatorDirective, multi: true }]
})
export class UniquePermalinkValidatorDirective implements AsyncValidator {
constructor(private pageService: PageService, private page: IPage) {}
// ...
}
And given that IPage is defined solely by
export interface IPage {
id: number;
// ...
}
Then UniquePermalinkValidatorDirective will not work by definition, failing in the manner described.
An interface defines something only in type space, not in the value space and therefore has no runtime manifestation whatsoever. This means it cannot be used in a value position.
In essence, Angular's dependency injection system reads the type of constructor parameters and, when there is a correspondingly named declaration in the value space, it will use that correspondingly named value as the injection token.
For example, the following
import {Injectable} from '#angular/core';
#Injectable() export class Service {
constructor(http: Http) {}
}
can also be written
import {Inject} from '#angular/core';
export class Service {
constructor(#Inject(Http) http: ThisTypeIsArbitraryWithRespectToInjection) {}
}
which means the same thing
Notice how Http is pass as an argument to Inject. But Inject(IPage), where IPage is an interface, is malformed.
A primary purpose of #Inject(ProviderToken) is to allow one to inject a provider orthogonally to the type of the decorated paramater in cases such as yours.
Therefore, you need something like
constructor(#Inject(pageToken) page) {}
Which means one needs to define a token and use it to register a provider that can be injected.
One can, and should, still write
constructor(#Inject(pageToken) page: IPage) {}
In order to give the parameter a type but the type is unrelated to the value injected for the parameter.
For example
import {InjectionToken, NgModule} from '#angular/core';
export const pageToken = new InjectionToken<IPage>('Page');
#NgModule({
providers: [
{
provide: pageToken,
useFactory: functionReturningAPage
}
]
}) export class // ...
Related
Below are the files of a library named posts-lib which makes http call inside posts.services.ts file and receives a list of posts and display them onto screen. It also consists a component named title.component.ts which is dependent on posts.services.ts and is responsible for displaying content on screen.
All of this works fine, but incase I want to move posts.service.ts folder out of the library and put it inside the app then how can I transfer the data from file which is outside of the library to the file title.component.ts which is dependent on it.
title.component.html
<h1>Testing titles api call</h1>
<ul>
<li *ngFor="let item of data">{{item.title}}</li>
</ul>
title.component.ts
import { Component, OnInit } from '#angular/core';
import { PostsService } from '../posts.service';
#Component({
selector: 'lib-tilte',
templateUrl: './tilte.component.html',
styleUrls: ['./tilte.component.css']
})
export class TilteComponent implements OnInit {
data: any;
constructor(private postData: PostsService) { }
ngOnInit() {
this.postData.getPosts().subscribe((result) => {
console.warn("reult",result);
this.data = result;
})
}
}
posts-lib.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'lib-posts-lib',
template: `
<p>
posts-lib works!
</p>
`,
styles: [
]
})
export class PostsLibComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
posts-lib.module.ts
import { NgModule } from '#angular/core';
import { PostsLibComponent } from './posts-lib.component';
import { TilteComponent } from './tilte/tilte.component';
import { HttpClientModule } from "#angular/common/http";
import { CommonModule } from '#angular/common'
#NgModule({
declarations: [
PostsLibComponent,
TilteComponent
],
imports: [
HttpClientModule,
CommonModule
],
exports: [
PostsLibComponent,
TilteComponent
]
})
export class PostsLibModule { }
posts.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from "#angular/common/http";
#Injectable({
providedIn: 'root'
})
export class PostsService {
url = "https://jsonplaceholder.typicode.com/posts";
constructor(private http: HttpClient) { }
getPosts() {
return this.http.get(this.url);
}
}
public-api.ts
export * from './lib/tilte/tilte.component';
export * from './lib/posts-lib.service';
export * from './lib/posts-lib.component';
export * from './lib/posts-lib.module';
export * from './lib/posts.service';
Ignoring all the issues the commenters are making - all valid - it sounds like you want to just remove the dependency on the service.
Or not, actually.
Yay, options!
Remove usage of the service
Just turn the component around from getting its own data, to being given its data. I.e. #Input.
Still with #Input, but instead, input the service itself rather than the values.
So either:
#Input() public data: any;
or
#Input() public set service(value: PostsService) {
this.postsService = value;
this.getData();
}
private getData(): void {
this.postsService.getPosts().subscribe(...);
}
Either way if you're moving the service out and no longer expecting the service and component to work as a functional pair within a system, you have to extract the component and feed it information instead with #Inputs.
Whether that's just feeding it the data from [a wrapper] service, or feeding it the service itself from wherever it now lives, you still need to give it to it.
I am working on an e-commerce app who's front-end is made in Angular 13.
I use a service to handle the products coming from an API. I run into a problem while ring to display the product details.
See Stackblitz demo HERE.
In app\services\product.service.ts I have:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
import { Product, ProductResponse } from '../models/product';
#Injectable({
providedIn: 'root'
})
export class ProductService {
products: Product[] = [];
apiURL: string = 'https://dummyjson.com';
constructor(private http: HttpClient) {}
// Product List
public getProducts(): Observable<ProductResponse>{
return this.http.get<ProductResponse>(`${this.apiURL}/products`);
}
// Product Details (single product)
public getProductDetails(id: Number): Observable<ProductResponse>{
return this.http.get<ProductResponse>(`${this.apiURL}/products/${id}`);
}
}
In app\app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { CommonModule } from '#angular/common';
import { HttpClientModule } from '#angular/common/http';
import { AppComponent } from './app.component';
import { Routes, RouterModule } from '#angular/router';
import { NavbarComponent } from './components/navbar/navbar.component';
import { FooterComponent } from './components/footer/footer.component';
import { SidebarComponent } from './components/sidebar/sidebar.component';
import { HomeComponent } from './components/home/home.component';
import { ProductItemComponent } from './components/product-item/product-item.component';
import { ProductsListComponent } from './components/products-list/products-list.component';
import { ProductsDetailsComponent } from './components/products-details/products-details.component';
const routes: Routes = [
{ path: '', component: HomeComponent},
{ path: 'products', component: ProductsListComponent},
{ path: 'products/show/:id', component: ProductsDetailsComponent},
];
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
FooterComponent,
SidebarComponent,
ProductsListComponent,
ProductItemComponent ,
ProductsDetailsComponent
],
imports: [
CommonModule,
BrowserModule,
HttpClientModule,
RouterModule.forRoot(routes),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In app\models\product.ts:
export class Product {
id?: number;
title?: string;
description?: string;
price?: number;
discountPercentage?: number;
rating?: number;
stock?: number;
brand?: string;
category?: string;
thumbnail?: string;
}
export interface ProductResponse {
products: Product[];
total: number;
skip: number;
limit: number;
}
In app\components\products-details\products-details.component.ts I have:
import { Component, OnInit, InputDecorator, Input } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { Product, ProductResponse } from '../../models/product';
import { ProductService } from '../../services/product.service';
#Component({
selector: 'app-products-details',
templateUrl: './products-details.component.html',
styleUrls: ['./products-details.component.css']
})
export class ProductsDetailsComponent implements OnInit {
#Input() product!: Product;
productResponse: any;
constructor(private ProductService: ProductService, private Router: Router, private ActivatedRoute:ActivatedRoute) { }
ngOnInit(): void {
const id = Number(this.ActivatedRoute.snapshot.paramMap.get('id'));
this.ProductService.getProductDetails(id).subscribe((response) => (this.productResponse = response));
}
}
In app\components\products-details\products-details.component.html I have:
<h1>{{ product.title }}</h1>
The problem
When I access a product details route (for instance, http://localhost:4200/products/show/1), the page displays an empty <h1> tag and the Chrome console shows Cannot read properties of undefined (reading 'title').
Where is my mistake?
Unlike the https://dummyjson.com/products endpoint, the https://dummyjson.com/products/{id} returns a plain Product object, so:
// product.service.ts
public getProductDetails(id: Number): Observable<Product>{
return this.http.get<Product>(`${this.apiURL}/products/${id}`);
}
// products-details.component.ts
// the #Input decorator is wrong — the data is not passed to the component from outside
// but instead fetched inside of the component
#Input() product!: Product;
// productResponse: any — this field is unused and should be removed
ngOnInit(): void {
...
this.ProductService.getProductDetails(id).subscribe((product) => (this.product = product));
}
The #Input() property only works for external assignment of values.
The following will probably do what you want:
product: Product | undefined;
this.ProductService.getProductDetails(id).subscribe((response) => (this.product = response));
<h1>{{ product?.title }}</h1>
Furthermore I believe handling raw responses should happen in the service instead of the component, but that may be a matter of personal preference.
I need to load components dynamically to view on some button click.
I have created on directive and some components in my custom module.
But when I try to create new instance of component it says No component factory found.
Here is my code structure.
dashboard module
#NgModule({
declarations: [MainPageComponent, WidgetComponent, ChartsComponent, GraphsComponent, InfographicsComponent, InsertionDirective],
imports: [
CommonModule,
GridsterModule,
ButtonsModule,
ChartsModule,
DropDownsModule
],
entryComponents: [MainPageComponent, WidgetComponent, ChartsComponent, GraphsComponent, InfographicsComponent],
exports: [MainPageComponent, WidgetComponent, ChartsComponent, GraphsComponent, InfographicsComponent]
})
export class DashboardModule {
static customization(config: any): ModuleWithProviders {
return {
ngModule: DashboardModule,
providers: [
{ provide: Service, useClass: config.service }
]
}
}
}
dashboard/insert directive
import { Directive, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[appInsertion]'
})
export class InsertionDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
1. dashboard/mainMainPageComponent.ts
import { Component, OnInit, ViewChild, ComponentFactoryResolver, ComponentRef, Type } from '#angular/core';
import { Service } from 'src/app/services/service';
import { Widget } from 'src/app/interface/widget';
import { GridsterConfig, GridsterItem } from 'angular-gridster2';
import { SETTINGS } from '../settings'
import { InsertionDirective } from '../insertion.directive';
import { ChartComponent } from '#progress/kendo-angular-charts';
#Component({
selector: 'dasbhoard-main-page',
templateUrl: './main-page.component.html',
styleUrls: ['./main-page.component.css']
})
export class MainPageComponent implements OnInit {
componentRef: ComponentRef<any>;
childComponentType: Type<any>;
#ViewChild(InsertionDirective)
insertionPoint: InsertionDirective;
public title: string;
public widgets: Array<{ widget: Widget, grid: GridsterItem, type: string }> = [];
public options: GridsterConfig;
constructor(private service: Service, private componentFactoryResolver: ComponentFactoryResolver) {
this.title = 'Dashboard';
}
ngOnInit() {
this.options = {
itemChangeCallback: MainPageComponent.itemChange,
itemResizeCallback: MainPageComponent.itemResize,
};
}
addNewWidget(type: string) {
let widgetType = SETTINGS.widgetSetting[type];
let totalWidgets = this.widgets ? this.widgets.length : 0;
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(widgetType.useClass);
//widgetType.useClass = ChartsComponent
// when I pass it statically its ok
//error here
let viewContainerRef = this.insertionPoint.viewContainerRef;
viewContainerRef.clear();
this.componentRef = viewContainerRef.createComponent(componentFactory);
this.widgets.push({ widget: { id: totalWidgets, header: `Widget ${totalWidgets} Header`, content: `<h1>Widget ${totalWidgets} Body</h4>` }, grid: { cols: 1, rows: 1, x: 0, y: 0 }, type: type });
}
}
dashboard/main-component/main-component.html
<ng-template appInsertion> </ng-template>
<button kendoButton(click) = "addNewWidget('charts')"[primary] = "true" class="pull-right" > Add Chart Widget </button>
I have each and every posts and all says you need to insert componets into entry points but I've already included all components to entry points. And all components are inside the same module but still it says no No component factory found for ChartsComponent. Did you add it to #NgModule.entryComponents?.
Any one please can you find out the where I'm doing wrong?
Thanks in advance.
I think it's just a typo:
In entryComponents you've written ChartsComponent and in the resolveComponentFactory method you've written ChartComponent
Could that be it?
I'm trying to pass a variable that is set on a component, to the parent component via a getter/setter in a service. The setter is applied correctly, but the getter returns undefined.
The below code was pulled out of another project I work on that does just fine with this code so I'm not sure why it isn't working here.
I simply just need to pass the pageTitle that is set on the child component, pass it to the parent component to display in its HTML.
Parent Component
TS: styleguide.component.ts
import { Component } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { StyleguideService } from './styleguide.service';
#Component({
selector: 'styleguide',
templateUrl: './styleguide.component.html',
host: {'class': 'route'},
})
export class StyleguideComponent {
constructor( private ss: StyleguideService ) {}
}
Relevant HTML: styleguide.component.html
<a [routerLink]="[]" aria-current="page" class="crumbs__link crumbs__link--active" [title]="ss.pageTitle">{{ss.pageTitle}}</a>
Parent Module: styleguide.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { StyleguideService } from './styleguide.service';
import { StyleguideComponent } from './styleguide.component';
import { TemplatesComponent } from './templates/templates.component';
...
#NgModule({
imports: [
CommonModule,
FormsModule,
...
],
declarations: [
StyleguideComponent,
TemplatesComponent,
...
],
exports: [
...
],
providers: [
StyleguideService
]
})
export class StyleguideModule {}
Service: styleguide.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class StyleguideService {
pageTitleS: string;
get pageTitle(): string {
console.log('get title: ', this.pageTitleS); // <-- Returns undefined
return this.pageTitleS;
}
set pageTitle(s: string) {
console.log('set title: ', s);
this.pageTitleS= s;
}
}
Child Component: templates.component.ts
import { Component } from '#angular/core';
import { StyleguideService } from '../styleguide.service';
#Component({
selector: 'templates',
templateUrl: './templates.component.html',
host: {'class': 'route__content'}
})
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle = "Templates";
}
}
You should implement the Service with Observables. A quick example would be something like this:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Injectable} from '#angular/core'
#Injectable()
export class Service {
private value: BehaviorSubject<string>;
constructor() {
this.value = <BehaviorSubject<string>>new BehaviorSubject();
}
setValue(value=""){
this.value.next(value);
}
getValue() {
return this.value.asObservable();
}
}
The Parent Component would subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'parent-component',
template: `
<div>
<h2>Value {{value}}</h2>
<child-component></child-component>
</div>
`,
})
export class ParentComponent implements OnInit {
value:string;
constructor(private service: Service) {
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
And the Child Component would set the value and also subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'child-component',
template: `
<div>
<h2>Child Value {{value}}</h2>
</div>
`,
})
export class ChildComponent implements OnInit {
value:string;
constructor(private service: Service) {
this.service.setValue('New Value');
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
Your setter is never called. You instantiate the service using StyleguideComponent, not the TemplatesComponent which does call the setter, and the constructor of StyleguideComponent does not call the setter on the service which is why the value remains undefined.
The TemplatesComponent has an element selector templates which I do not see in the styleguide.component.html you have in the question which is why I believe TemplatesComponent is never being created.
You are not calling the setter function in your child.component.ts instead you are setting the value of variable but I think you are accessing it wrongly as you are missing the last S in the variable name. You should be doing
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle("Templates");
// Now to get it you should call
this.ss.pageTitle(); // Should console.log the value
}
}
Okay so it was related to my routing setup, I didn't have my child routes setup correctly so this really had nothing to do with the getter/setter after all.
I have two components that are not parent and child components but i need to pass value from component A to component B.
example:
src/abc/cde/uij/componentA.ts has variable CustomerId = "ssss"
need to pas that variable customerID to src/abc/xyz/componentB.ts
Simple example:
Component A:
#Component({})
export class ComponentA {
constructor(private sharedService : SharedService) {}
sendMessage(msg : string) {
this.sharedService.send(msg);
}
}
Component B:
#Component({})
export class ComponentB {
constructor(private sharedService : SharedService) {
this.sharedService.stream$.subscribe(this.receiveMessage.bind(this));
}
receiveMessage(msg : string) {
console.log(msg); // your message from component A
}
}
Shared service:
#Injectable()
export class SharedService {
private _stream$ = new Rx.BehaviorSubject("");
public stream$ = this._stream$.asObservable();
send(msg : string) {
this._stream$.next(msg);
}
}
Shared service have to be placed in the same NgModule.
On your service define a setMyProperty() and a getMyProperty(). Then setMyProperty with a value from Component A. ComponentB then getMyProperty where you return the value...
You have to inject the service into both components.
You could give this a shot. Its pretty simple and straight forward.
I just followed THIS example and made some changes so that it could be siblings talking instead of parent/child.
my-service.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MyService {
// Observable string sources
private myAnnouncedSource = new Subject<string>();
// Observable string streams
myAnnounced$ = this.myAnnouncedSource.asObservable();
// Service message commands
announceItem(item: string) {
this.myAnnouncedSource.next(item);
}
}
my-comp1.component.ts
import { Component } from '#angular/core';
import { MyService } from './my-service.service';
#Component({
selector: 'my-compA',
template: `...`,
providers: [MyService]
})
export class MyComponentA {
constructor(private myService: MyService) {
}
announceToOtherComps() {
let sharedItem = "shibby";
this.myService.announceItem(sharedItem);
}
}
my-comp2.component.ts
import { Component, Input, OnDestroy } from '#angular/core';
import { MyService } from './my-service.service';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'my-compB',
template: `...`,
providers: [MyService]
})
export class MyComponentB implements OnDestroy {
sharedItem = '<no data>';
subscription: Subscription;
constructor(private myService: MyService) {
this.subscription = myService.myAnnounced$.subscribe(
item => {
this.sharedItem = item;
});
}
ngOnDestroy() {
// prevent memory leak when component destroyed
this.subscription.unsubscribe();
}
}
<component-a [id]="product.id"></component-a>
In the component-a ts file .Use it like below
export class ComponentA implements OnInit {
#Input() // <------------
id: number;
(...)
}