Angular2 - No provider for service found - exception

I have defined 3 components in my application. AppComponent, MainComponent and TextComponent.
AppComponent contains MainComponent as follows. Note that I have two services I have included in the providers attribute, CommonEventsService, CommonProductEventsService
#Component({
selector: 'my-app',
template: `
<my-main></my-main>
`,
providers: [CommonEventsService, CommonProductEventsService],
directives: [ROUTER_DIRECTIVES, MainComponent]
})
export class AppComponent implements OnInit, OnDestroy {...}
Main component is included in the directives attribute in the AppComponent. The Main component is defined as follows:
#Component({
template: "<my-add-text></my-add-text>",
selector: "my-main",
directives: [TextComponent]
})
export class MainComponent implements OnInit, OnDestroy {...}
Note that I have included the TextComponent in the directives attribute above. The TextComponent is defined as follows:
#Component({
selector: "my-add-text",
templateUrl: "/app/scripts/components/add/text.component.html",
})
export class TextComponent {
constructor(private commonProductEvents: CommonProductEventsService){}
}
The problem is that when the application is loaded, I get the following error.
EXCEPTION: No provider for CommonProductEventsService! (TextComponent -> CommonProductEventsService) in [addText in MainComponent#41:13]
But I have registered the CommonProductEventsService in the AppComponent. So why am I getting this error?

Can't reproduce. See Working Plunker.
Do you have
CommonEventsService
CommonProductEventsService
in the same file as the components but further below in the file?
Then you either need to move them upwards, use forwardRef or move them into as separate file and import them.
See also
- https://angular.io/docs/ts/latest/api/core/forwardRef-function.html
- http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html

Related

Can't see content of reusable angular form on home page

I'm trying to create a reusable Angular form in an Ionic app. After following several tutorials and Stack posts I no longer have any errors but the content isn't showing on the parent page.
My reusable component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-profile-form',
templateUrl: './profile-form.component.html',
styleUrls: ['./profile-form.component.scss'],
})
export class ProfileFormComponent implements OnInit {
constructor() { }
ngOnInit() {}
}
<p>
profile-form works! BLAH!!!!!
</p>
After reading a stack post I manually created a module for the component because Ionic doesn't generate modules when you create a component via the CLI.
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { IonicModule } from '#ionic/angular';
import { ProfileFormComponent } from './profile-form.component';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
],
exports: [ProfileFormComponent],
declarations: [ProfileFormComponent]
})
export class ProfileFormModule {}
Then I added it to the module of the parent page:
import { ProfileFormComponent } from 'src/app/forms/profile-form/profile-form.component';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
ProfilePageRoutingModule
],
declarations: [ProfilePage, ProfileFormComponent]
})
<div>
<app-profile-form></app-profile-form>
</div>
I'm not getting any errors but I'm unable to see the content of the reusable component on the parent page.
Angular CLI: 14.0.2
Node: 16.13.2
Package Manager: npm 8.1.2
OS: darwin x64
Angular: 14.0.3
... common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Package Version
#angular-devkit/architect 0.1400.2
#angular-devkit/build-angular 14.0.2
#angular-devkit/core 14.0.2
#angular-devkit/schematics 14.0.2
#angular/fire 7.4.1
#schematics/angular 14.0.2
rxjs 6.6.7
typescript 4.7.4
Any help would be greatly appreciated.
What's weird is if I inspect the page I can see the element:
You do not need to create a module for ProfileFormComponent you just need to declare ProfileFormComponent in a specific module for example in app.module.ts
But if you want to use this component or any reusable component in other modules you should put it on the shared module or create your own module and import it to the specific module you want
Open app-profile-form more in the html inspector, what's the contents? Anyway ProfileFormComponent should be in declarations of #NgModule no need to export it. Remove ProfileFormModule entirely and add ProfileFormComponent to declarations of AppModule

Putting anything in the constructor of Angular component results in failure of entire page

I am trying to add something to the constructor of one of my Angular components, however, anytime I put something in the constructor, it renders the entire page blank - getting rid of all of the other components and displaying just the background.
For example - this will work.
TS
#Component({
selector: 'app-cardboxes',
templateUrl: './cardboxes.component.html',
styleUrls: ['./cardboxes.component.scss']
})
export class CardboxesComponent implements OnInit {
constructor() { }
ngOnInit(): void {}
}
And this will render completely blank - removing everything, even the other components.
TS
#Component({
selector: 'app-cardboxes',
templateUrl: './cardboxes.component.html',
styleUrls: ['./cardboxes.component.scss']
})
export class CardboxesComponent implements OnInit {
constructor(private dialog: MatDialog) { }
ngOnInit(): void {}
}
For the record, it doesn't matter what is put in the constructor - it is the same result every time. The Chrome terminal says that there is a NullInjectorError - No provider for MatDialog
Is there a reason for this, or an easy solution? I do not understand why this is happening and I really need to be able to use the constructors. Do I have to make another import somewhere? Is there a configuration I am missing?
You are trying to use angular materials. Please use this comand in console.
npm i #angular/material
Open app.module and
NgModule ({....
imports: [...,
MatSliderModule,
…]
Items adsed in constructor should be imported in module

Angular - What's the best way to include html in multiple components?

I need to put a loading in multiple components of my project. So instead of putting the same HTML over and over again across the components, I need to know what's the best way to not repeat code, but I don't know if this is correct thing to do. I have created a component called loading-graphic, which I bind it in every HTML file of the respective components. I read about ngTemplateOutlet and ngContent, but to be honest it doesn't make sense in my head to use it for this case (and I don't get it too... I'm a beginner on it). So, on what should I bet? Thanks.
Base on your question, I think creating Reusable Components with NgTemplateOutlet would be the best solution to avoid repeating HTML in different component Templates. It allows you to pass parameters base on your host component and makes your Angular app easier to test and develop since it sllows easily modified reusable component for various use cases without having to modify individual components itself.
Since you are a begginer I am going to Illustrate simple way of using NgTemplateOutlet, however dive deep later on Templates and Stamps.
Imaging you have a reusable Search component where you want to hide a check box base on the parent component. Your Template will look like below.
we pass data from the parent component to the child/Search component using #Input and property binding, so we define which checkboxes to hide base on Parent component.
here is the code sample for Search Component
search.component.ts
======================
import { Component, OnInit, Output, EventEmitter, Input } from '#angular/core';
#Component({
selector: 'app-search',
templateUrl: './app-search.component.html',
styleUrls: ['./app-search.component.css']
})
export class AppSearchComponent implements OnInit {
accountName: string = '';
#Output() accountSearchChange = new EventEmitter<string>(); //1. Event Binding to pass data from Child to Parent Component ->Int
#Input() searchMode: 'account' | 'holder' | 'distribution' = 'account'; //Use NgTemplateOutlet for reusable componenet
constructor() { }
ngOnInit() {
}
//2. Event Binding to pass data from Child to Parent Component ->Invoke Emit
doSearchFilter(searchText: string) {
console.log('Search Child: doSearchFilter -> ' + searchText);
this.accountSearchChange.emit(searchText);
}
clearFilters() {
console.log('Account Search: Clear Filter is called');
this.accountName = '';
}
}
search.component.html
=====================
<ng-container [ngSwitch]="searchMode">
<div class="input-full-width" *ngSwitchCase="'account'">
<mat-checkbox class="example-container check-full-width">Show active and pending only</mat-checkbox>
</div>
<div class="input-full-width" *ngSwitchCase="'holder'">
<mat-checkbox class="example-container check-full-width">View only holders with missing requirements</mat-checkbox>
</div>
<div class="input-full-width" *ngSwitchCase="'holder'">
<mat-checkbox class="example-container check-full-width">View only active Holders</mat-checkbox>
</div>
</ng-container>
I am using Search component inside Account component and below is the code sample.
in HTML file i am referring app-search css selector and pass the search Mode defined in ts.
import { Component, OnInit, ViewChild, AfterViewInit } from '#angular/core';
import { MatSort, MatTableDataSource, MatPaginator } from '#angular/material';
import { Router } from "#angular/router";
import { Observable } from 'rxjs';
import { AccountService } from 'src/app/core/services/account.service';
import { Deal } from 'src/app/core/models/deal';
#Component({
selector: 'app-account',
templateUrl: './account.component.html',
styleUrls: ['./account.component.css']
})
export class AccountsComponent implements OnInit, AfterViewInit {
displayedColumns: string[] = ['accountId', 'accountShortName', 'accountType'];
public dealDataSource = new MatTableDataSource<Deal>();
dealsObservable: Observable<Deal[]>;
searchMode = 'distribution';
isLoadingResults = true;
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
constructor(private router: Router, private api: AccountService) { }
......................................................
<app-search (accountSearchChange)='doFilter($event)' [searchMode]="searchMode"></app-search>
Hope this is clear.
i think loading component is not a bad Idee. Use loading component in your app-root component. You can use a loading.service and interceptor to display or hide the component. You will get automatically a Loading Indicator for each API call.
sample: https://medium.com/#zeljkoradic/loader-bar-on-every-http-request-in-angular-6-60d8572a21a9

How to load jsp page as template in component in angular 6

I want to load the external jsp page content as template in my component in angular 6 application.
#Component({
selector: 'app-conrequest',
templateUrl:'mydomain.com:port/utils/registerUser.jsp',
styleUrls: ['./conrequest.component.css']
})
In the above code, I have mentioned the jsp page url, which I want to load as a template.
Please help me on this.
Thanks,
Suresh
First of all whichever external link you are using it should "return" something.
If there is no information in return it will always gives you an error.
Lets begin your answer...
Step 1- First you need to import HttpClient and Map
import { HttpClient } from '#angular/common/http';
import 'rxjs/add/operator/map';
Step 2- and your #Component decorator should look like this
#Component({
selector: 'my-template',
template: `<div [innerHtml]="myTemplate">
</div> `})
Step 3- in your class you can import your external template like...
export class TestComponent {
private myTemplate: any = '';
constructor(http: HttpClient) {
http.get('www.abc.com/index.html', {responseType: 'text'}).subscribe(data => this.myTemplate = data);
}
}
Step 4 - Also import httpClient in RootModule
imports: [
...
...
HttpClientModule
],
That's it. Try with yours.
Thanks
Sunil Sain

Angular 2+: Concatenate String Interpolation? [duplicate]

I am working on a tutorial involving the setting of an iframe src attribute:
<iframe width="100%" height="300" src="{{video.url}}"></iframe>
This throws an exception:
Error: unsafe value used in a resource URL context
at DomSanitizationServiceImpl.sanitize...
I have already tried using bindings with [src] with no success.
Update v8
Below answers work but exposes your application to XSS security risks!.
Instead of using this.domSanitizer.bypassSecurityTrustResourceUrl(url), it is recommended to use this.domSanitizer.sanitize(SecurityContext.URL, url)
Update
For RC.6^ version use DomSanitizer
Plunker
And a good option is using pure pipe for that:
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer} from '#angular/platform-browser';
#Pipe({ name: 'safe' })
export class SafePipe implements PipeTransform {
constructor(private domSanitizer: DomSanitizer) {}
transform(url) {
return this.domSanitizer.bypassSecurityTrustResourceUrl(url);
}
}
remember to add your new SafePipe to the declarations array of the AppModule. (as seen on documentation)
#NgModule({
declarations : [
...
SafePipe
],
})
html
<iframe width="100%" height="300" [src]="url | safe"></iframe>
Plunker
If you use embed tag this might be interesting for you:
how with angular2 rc.6 disable sanitize on embed html tag which display pdf
Old version RC.5
You can leverage DomSanitizationService like this:
export class YourComponent {
url: SafeResourceUrl;
constructor(domSanitizationService: DomSanitizationService) {
this.url = domSanitizer.bypassSecurityTrustResourceUrl('your url');
}
}
And then bind to url in your template:
<iframe width="100%" height="300" [src]="url"></iframe>
Don't forget to add the following imports:
import { SafeResourceUrl, DomSanitizationService } from '#angular/platform-browser';
Plunker sample
This one works for me.
import { Component,Input,OnInit} from '#angular/core';
import {DomSanitizer,SafeResourceUrl,} from '#angular/platform-browser';
#Component({
moduleId: module.id,
selector: 'player',
templateUrl: './player.component.html',
styleUrls:['./player.component.scss'],
})
export class PlayerComponent implements OnInit{
#Input()
id:string;
url: SafeResourceUrl;
constructor (public sanitizer:DomSanitizer) {
}
ngOnInit() {
this.url = this.sanitizer.bypassSecurityTrustResourceUrl(this.id);
}
}
This works me to Angular 5.2.0
fileName.Component.ts
import { Component, OnInit, Input } from '#angular/core';
import { DomSanitizer, SafeResourceUrl } from '#angular/platform-browser';
#Component({
selector: 'app-sample',
templateUrl: './fileName.component.html',
styleUrls: ['./fileName.component.scss']
})
export class FileName implements OnInit {
#Input()
url: string = "https://www.mmlpqtpkasjdashdjahd.com";
urlSafe: SafeResourceUrl;
constructor(public sanitizer: DomSanitizer) { }
ngOnInit() {
this.urlSafe= this.sanitizer.bypassSecurityTrustResourceUrl(this.url);
}
}
fileName.Component.html
<iframe width="100%" height="100%" frameBorder="0" [src]="urlSafe"></iframe>
thats all folks!!!
constructor(
public sanitizer: DomSanitizer, ) {
}
I had been struggling for 4 hours. the problem was in img tag. When you use square bracket to 'src' ex: [src]. you can not use this angular expression {{}}. you just give directly from an object example below. if you give angular expression {{}}. you will get interpolation error.
first i used ngFor to iterate the countries
*ngFor="let country of countries"
second you put this in the img tag. this is it.
<img [src]="sanitizer.bypassSecurityTrustResourceUrl(country.flag)"
height="20" width="20" alt=""/>
All answers seem wrong to be honest. Using this.sanitizer.bypassSecurityTrustResourceUrl(url) only bypasses the security and treats the url as a SafeResourceUrl. However, given url may still be malicious resulting in security violations. Docs say so too: https://angular.io/api/platform-browser/DomSanitizer#bypassSecurityTrustResourceUrl
A solution would be to call sanitizer first and the pass the sanitizer return value to the bypassSecurityTrustResourceUrl like this:
this.sanitizer.bypassSecurityTrustResourceUrl(this.sanitizer.sanitize(SecurityContext.URL, url))
This way you sanitize any malicious code and then bypass the security denoting this is indeed a safe url.
I ran into this issue as well, but in order to use a safe pipe in my angular module, I installed the safe-pipe npm package, which you can find here. FYI, this worked in Angular 9.1.3, I haven't tried this in any other versions of Angular. Here's how you add it step by step:
Install the package via npm install safe-pipe or yarn add safe-pipe. This will store a reference to it in your dependencies in the package.json file, which you should already have from starting a new Angular project.
Add SafePipeModule module to NgModule.imports in your Angular module file like so:
import { SafePipeModule } from 'safe-pipe';
#NgModule({
imports: [ SafePipeModule ]
})
export class AppModule { }
Add the safe pipe to an element in the template for the Angular component you are importing into your NgModule this way:
<element [property]="value | safe: sanitizationType"></element>
Here are some specific examples of the safePipe in an html element:
<div [style.background-image]="'url(' + pictureUrl + ')' | safe: 'style'" class="pic bg-pic"></div>
<img [src]="pictureUrl | safe: 'url'" class="pic" alt="Logo">
<iframe [src]="catVideoEmbed | safe: 'resourceUrl'" width="640" height="390"></iframe>
<pre [innerHTML]="htmlContent | safe: 'html'"></pre>
This works for me
I defined an id in the iframe
<iframe id="embeddedPage"></iframe>
and in the component I used this code
export class YourComponent implements OnInit {
constructor() {}
ngOnInit(): void {
const iframe = document.getElementById('embeddedPage') as HTMLIFrameElement;
iframe.contentWindow.location.replace('your url');
}
}
I'll share this solution even if this is NOT best practice, but it happened to me once that we were not allowed to use the this.domSanitizer.bypassSecurityTrustResourceUrl(url) solution because of an automatic security warning that stopped the CI/CD pipelines.
#Component({
template: '<iframe #iframeRef></iframe>'
})
export class UnsafeUrlBypassIframeSampleComponent implements AfterViewInit {
#ViewChild('iframeRef') iframe: ElementRef<HTMLIFrameElement>;
constructor(private renderer: Renderer2) {}
ngAfterViewInit() {
const MY_UNSAFE_URL = '/path/to/something';
this.renderer.setProperty(this.iframe.nativeElement, 'src', MY_UNSAFE_URL);
}
}
If it is the case that you need to bypass Angular security systems, and this will inevitably lead to vulnerabilities, it is best to do it explicitly.
I usually add separate safe pipe reusable component as following
# Add Safe Pipe
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({name: 'mySafe'})
export class SafePipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {
}
public transform(url) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}
# then create shared pipe module as following
import { NgModule } from '#angular/core';
import { SafePipe } from './safe.pipe';
#NgModule({
declarations: [
SafePipe
],
exports: [
SafePipe
]
})
export class SharedPipesModule {
}
# import shared pipe module in your native module
#NgModule({
declarations: [],
imports: [
SharedPipesModule,
],
})
export class SupportModule {
}
<!-------------------
call your url (`trustedUrl` for me) and add `mySafe` as defined in Safe Pipe
---------------->
<div class="container-fluid" *ngIf="trustedUrl">
<iframe [src]="trustedUrl | mySafe" align="middle" width="100%" height="800" frameborder="0"></iframe>
</div>
Congratulation ! ¨^^
I have an easy & efficient solution for you, yes!
<iframe width="100%" height="300" [attr.src]="video.url"></iframe
[attr.src] instead of src
"video.url" and not {{video.url}}
Great ;)