I tried to search but I didn't get the answer.
I can't realize when I must define "providers" section in #Component definition.
I looked over examples.
half of them use
1)
import { Component, blah-blah} from '#angular/core';
import { SomeService } from './some.service';
#Component({
selector: 'example-selector',
providers: [SomeService ],
.....
constructor(public someService : SomeService ,
2) but half of them WITHOUT "providers" section!
import { Component, blah-blah} from '#angular/core';
import { SomeService } from './some.service';
#Component({
selector: 'example-selector',
.....
constructor(public someService : SomeService ,
===
so, I am confused: when I need that section and when I don't?
I believe Maxouhell gave the right answer in another question:
If you provide your service inside your component, it will be local to it. So if you have two instance of your component, you will have two instance of your service.
Now if you provide your service inside your module, it will be global and if you have two instance of your component, they will share the same instance of the service.
I believe it has to do with at what level you declare your providers. You must declare a provider for each component or service that you use
For example in my app, I have a service that I provide in main.ts which is then available to any component without having to indicate the provider there.
bootstrap(AppComponent,
[
APP_ROUTER_PROVIDER,
HTTP_PROVIDERS,
SettingsService,
disableDeprecatedForms(),
provideForms()
]).catch((err: any) => console.error(err));
#Component({
selector: 'kg-Settings ',
templateUrl: './app/components/settings/settings.component.html',
styleUrls: ['./app/components/settings/settings.component.css'],
directives: [ROUTER_DIRECTIVES, REACTIVE_FORM_DIRECTIVES]
})
However, if I want to inject a component into another component and it has not been previously provided for at a higher level in the app tree,
#Component({
selector: 'k-Calculator ',
templateUrl: './app/components/calculator/calculator.component.html',
styleUrls: ['./app/components/calculator/calculator.component.css'],
directives: [ROUTER_DIRECTIVES, REACTIVE_FORM_DIRECTIVES, Panel, Dropdown, SelectButton, Button],
providers: [CalculationService]
})
You need provider section whenever you need to use a service in that component.
Item #1, defined the provider within the component.
Item #2, (might) defined the provider in app.component.ts (Please check the main file)
Related
I have created a mat-dialog component to fire for http response. When I include a ngIf statement inside the html for the mat-dialog,it is not handled. On the console it shows a warning as follows.
Can't bind to 'ngIf' since it isn't a known property of 'div'.
NgIf works fine in all other components in the project.
Calling for mat-dialog inside typescript file.
this.matDialog.open(PaymentModelComponent, {
width: "630px",
data: { message: response.comment },
autoFocus: false,
height: "630px",
});
html code
<div *ngIf="true"><p>Show this only if "show" is true</p></div>
Why do ng-if don't work inside a mat-dialog ?
All I needed to do was to add the DialogComponent to the module's declarations:
declarations: [..., MyDialogComponent]
No need to have the component code in the same file.
All you need to do is add the dialogs to your module imports & declarations. In the same module where the component your dialogs are declared in is declared.
I am posting this hoping that someone may find it useful. This is not the exact answer for this problem, but this is how I overcome my scenario. In my case I was using mat-dialog component as an entry component inside the app.module.ts
entryComponents: [ErrorComponent, UserAccountInfoReloadPopupDialogComponent],
For some reason, ng-if it is not working inside the dialog. It is not only for ng-if, all others like ng-for were not available.
I fixed the issue by defining both components in the same ts file.
#Component({
selector: "app-account-info",
templateUrl: "./account-info.component.html",
styleUrls: ["./account-info.component.css"],
})
export class AccountInfoComponent implements OnInit {
//code
}
#Component({
selector: "user-account-info-reload-popup-dialog",
templateUrl: "./payment-modal.component.html",
styleUrls: ["./payment-modal.component.css"],
})
export class UserAccountInfoReloadPopupDialogComponent implements OnInit {
//code
}
Then I defined the newly created mat-dialog component inside angular module declaration.
#NgModule({
declarations:
[
UserAccountInfoReloadPopupDialogComponent
],
This fixed my issue. You can check the angular mat-dialog documentation.
https://material.angular.io/components/dialog/overview
I have a hybrid angular-cli that roughly follows Victor Savkin's Lazy Loaded AngularJS guide. AngularJS is bootstraped in the constructor of a LazyLoaded Angular module. The main difference between my app and the guide is that I am trying to wrap the <ui-view> directive inside of some Angular components. Because of how my layout is structured the <ui-view> element will not be available when AngularJS is bootstrapped and may be added or removed at any time.
import { Component, Directive, ElementRef, Injector } from '#angular/core';
import { UpgradeComponent } from '#angular/upgrade/static';
import * as angular from 'angular';
#Component({
template: `
<layout-wrapper>
<my-toolbar></my-toolbar>
<layout-contents>
<ng2-ui-view>
<h3 class="text-center">AngularJS page not loaded</h3>
</ng2-ui-view>
</layout-contents>
</layout-wrapper>
`,
})
export class LegacyOutputComponent { }
#Directive({selector: 'ng2-ui-view'})
export class UpgradedUiViewComponent extends UpgradeComponent {
constructor(ref: ElementRef, inj: Injector) {
super('uiViewWrapper', ref, inj);
}
}
export const routerPatchModule = 'arcs.router.patch';
// We need to define a wrapper for ui-view because we can only upgrade
// components with only one definition. uiView cannot be automatically
// upgraded because its definition is too complex
angular.module(routerPatchModule, ['ui.router'])
.component('uiViewWrapper', { template: '<ui-view></ui-view>'})
When I run the code a Error: No provider for $scope! error is thrown. Checking the stack trace I can see that it is thrown in the UpgradeComponent super class. The injector tries to get $scope and
Alternative is to let Angular know that it needs to provide the $scope.
import { Injector } from '#angular/core';
// allow $scope to be provided to ng1
export const ScopeProvider = {
deps: ['$injector'],
provide: '$scope',
useFactory: (injector: Injector) => injector.get('$rootScope').$new(),
};
#Directive({
providers: [ ScopeProvider ],
selector: 'ng2-ui-view',
})
export class UpgradedUiViewComponent extends UpgradeComponent {
constructor(ref: ElementRef, inj: Injector) {
super('uiViewWrapper', ref, inj);
}
}
This setup will not work. AngularJS needs to be able to load in the root of your application in order for the scope to be defined correctly.
A better way to approach this problem is to use the <div ui-view> directive in the root of your application (as in the upgrade guide) and then to downgrade a layout component from Angular into AngularJS to wrap your content.
I'm working on a very large project right now, and we're trying to make our code as modular as possible. We have multiple Angular applications, and we've created a separate library of common UI components and an API kit with common services used across these applications.
We're running into problems though when trying to build common components that need to work with services. For example, I'm working on an autocomplete component right now. That component should be able to fetch data from a remote source and filter the results based on what's been typed into the component's input field.
That implementation is easy enough for one instance. I inject the autocomplete service in my autocomplete component's constructor, then I provide it on the parent. That gives me the flexibility to change to the implementation details of the service when I used it while still being able to create a reusable component that works with a defined interface.
For example:
The service where we want to define the interface used by our Autocomplete Component:
#Injectable()
export class AutocompleteService {
public url: string = 'my-api-default';
constructor(http: Http) {}
fetch(): any {
return this.http.get(this.url);
}
}
The autocomplete component implementation:
#Component({
selector: 'my-autocomplete',
templateUrl: 'my-autocomplete.component.html'
})
export class MyAutocompleteComponent {
constructor(private autocompleteService: AutocompleteService) {}
getData() {
return this.autocompleteService.fetch();
}
...autocomplete logic...
}
Now I can define a bear service that implements the Autocomplete service. I can hook up the bear service to my autocomplete component, so I can choose bear species in my form.
#Injectable()
export class BearService {
public url: string = 'bear-url';
constructor(http: Http){}
fetch() {
return this.http.get(this.url);
}
}
Next, I define the parent that consumes my autocomplete component and provide the bear service to get my bear species data.
#Component({
selector: 'my-form-component',
template: `
<form>
<my-autocomplete [(ngModel)]="bear"></my-autocomplete>
<button type="submit">Submit</button>
</form>
`,
providers: [
{provide: AutocompleteService, useClass: BearService}
]
})
export class MyFormComponent {
...component logic...
}
So far, so good.
My question arises when I need to build a large form that uses multiple autocomplete components. My boss tells me I need three autocomplete fields on this form, one for bear species, one for beet species, and one for a Battlestar Gallactica character. My first thought is to do this:
I define the service instances:
#Injectable()
export class BeetsService {
public url: string = 'beets-url';
constructor(http: Http){}
fetch() {
return this.http.get(this.url);
}
}
#Injectable()
export class BattleStarGallacticaService {
public url: string = 'battlestar-gallactica';
constructor(http: Http){}
fetch() {
return this.http.get(this.url);
}
}
Then I update the parent template and providers:
#Component({
selector: 'my-form-component',
template: `
<form>
<my-autocomplete [(ngModel)]="bear"></my-autocomplete>
<my-autocomplete [(ngModel)]="beet"></my-autocomplete>
<my-autocomplete [(ngModel)]="battleStarGallactica"></my-autocomplete>
<button type="submit">Submit</button>
</form>
`,
providers: [
{provide: AutocompleteService, useClass: BearService},
{provide: AutocompleteService, useClass: BeetService},
{provide: AutocompleteService, useClass: BattlestarGalacticaService},
]
})
export class MyFormComponent {
...component logic...
}
Now how can I tell which autocomplete component to use which service?
I know what I have now will always use the last provider given for AutocompleteService, because that's how the Angular DI framework works.
I also know I can't use multi-providers on this since Angular only defines them for NG_VALIDATORS and NG_ASYNC_VALIDATORS.
So, does anyone have any idea how I can solve my problem? I don't care how the problem gets solve per se, but I still need to be able to:
Define a service interface
User that service interface in the reusable component
Create a new service instance for my own needs that implements the original interface
Be able to use multiple components that implement the same service interface using different service implementations within a single parent component
I would move the providers to a directive that are applied selectively
#Directive({
selector: 'my-autocomplete[bear]',
providers: [{ provide: AutoCompleteService, useClass: BearService}]
})
export class BearDirective {}
#Directive({
selector: 'my-autocomplete[battlestarGalactica]',
providers: [{ provide: AutoCompleteService, useClass: BattlestarGalacticaService}]
})
export class BattelstarGalacticaDirective{}
and then use it like
<form>
<my-autocomplete bear [(ngModel)]="bear"></my-autocomplete>
<my-autocomplete bear [(ngModel)]="beet"></my-autocomplete>
<my-autocomplete battlestarGalactica [(ngModel)]="battleStarGallactica"></my-autocomplete>
<button type="submit">Submit</button>
</form>
and remove the providers from the MyFormComponent
Problem:
I am unable to get a function to pass successfully to a downgraded Angular 2 component. I've tried just about every way I can think to pass things in but I am only able to get strings passed in via string interpolation -> some-attribute={{controller.property}}. Here are some of the things I've tried (yes, I know some of them don't make sense...)
some-function="controller.function";
some-function="{{controller.function}}"; //<-- works for data, not functions
[some-function]="{{controller.function}}";
[someFunction]="controller.function";
[(someFunction)]="controller.function";
Setup:
Here is my existing setup which is working for data but not for functions:
Angular 1 usage
<my-component data-input-name="{{controller.name}}"></my-component>
Upgrade/Downgrade adapter
angular.module('myModule').directive('myComponent',
downgradeComponent({
component: MyComponent,
inputs: ['inputName']
}) as angular.IDirectiveFactory);
Angular 2 definition
#Component({
selector: 'my-component',
templateUrl: './my.component.html',
styleUrls: ['./my.component.css']
})
export class MyComponent implements OnInit {
#Input() inputName: any;
constructor() {}
}
The Angular docs even show how to set it up but they don't give any examples of actual usage.
Am I missing something or can this just not be done?
Use AngularJS kebab case like this, seems to work for me:
<my-component [data-input-name]="controller.name">
</my-component>
My web application has multiple filters that should be represented in the current Url so that users can simply copy/bookmark the current page to get it back later.
In angular1 I used $location.search(name, value) to simply set seach params on change. Now I want to get something similar in angular2.
Or is it wrong?
I think you must use the router inside Angular2. code example:
import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {ProfileComponent} from './profile.component';
#Component({
selector: 'my-app',
template: `
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
#RouteConfig([
{path: '/profile/:id', name: 'Profile', component: ProfileComponent}
])
export class AppComponent {
}
The :id is a route parameter and you can do an URL like that:
/profile/2 and 2 is the value of the id parameter.
You could find more detail in the Angular2 doc : router doc