I'm using ngx-translate with no-problems in views, using pipes. What i need to do is to use it in a component, for example to show an error message, or define a datatable column default content.
I'm trying to do something like:
translate.instant("AREA.NEW");
or
translate.get("AREA.NEW").subscribe((res: string) => {
console.log(res);
});
I've tried calling it in ngOnInit() and ngAfterViewInit()
But in both cases i just get "AREA.NEW", not the translated word. I asume the json dictionary is loaded after my call, so i don't realize how make it work.
Import the TranslateService and use it wherever you want.
import { TranslateService } from '#ngx-translate/core';
export class YourComponent {
constructor(private translateService: TranslateService) {
console.log('translation', this.translateService.instant('my_i18n_json_defined_key'));
}
}
It's work.
constructor(private readonly translateService: TranslateService) { }
ngOnInit() {
this.translateService.get(['']).subscribe(translations => {
console.info(this.translateService.instant('key1'));
console.info(this.translateService.instant('key2'));
console.info(this.translateService.instant('key3'));
});
}
Another cause for "instant" translation not working can be the translations not being available when required. This might happen when translations are loaded using the TranslateHttpLoader (example here) which loads from translation files in an async way.
One trick to overcome this is shown here (just tested and it works in Angular 13+) and forces the component to wait until the translation is ready by adding a special resolver on its route:
The resolver
#Injectable()
export class TranslationLoaderResolver {
constructor(private translate: TranslateService){ }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any>{
return this.translate.get("does.not.matter");
}
}
Routing usage
{path: "segment", component: YourComponent, resolve: {model: TranslationLoaderResolver}
Related
I'm having the following directive that adds dynamic component to ng-container
#Directive({
selector: '[appAddingDirective]'
})
export class AddingDirective {
constructor(protected vc: ViewContainerRef) { }
public addComponent(factory: ComponentFactory<any>, inputs: any): void {
this.vc.clear();
const ref: ComponentRef<any> = this.vc.createComponent(factory);
Object.assign(ref.instance, inputs); // can't find more elegant way to assign inputs((
ref.instance.ngOnInit(); // IMPORTANT: if I remove this call ngOnInit will not be called
}
}
The directive is used in an obvious way.
#Component({
selector: 'app-wrapper',
template: `<ng-container appAddingDirective></ng-container>`
})
export class WrapperComponent implements AfterViewInit{
#ViewChild(DynamicItemDirective)
private dynamicItem: DynamicItemDirective;
constructor() { }
ngAfterViewInit(): void {
// hope it doesn't matter how we get componentFactory
this.dynamicItem.addComponent(componentFactory, {a: '123'});
}
}
Finally in a component that is loaded dynamically I have
#Component({
selector: 'app-dynamic',
template: '<p>Dynamic load works {{ a }}</p>'
})
export class DynamicComponent implements OnInit {
#Input() a: string;
constructor() {}
ngOnInit(): void {
console.log(this.a);
debugger;
}
}
Here are my questions.
If I remove ref.instance.ngOnInit() call in AddingDirective, I do not get in ngOnInit of DynamicComponent (debugger and console.log do not fire up). Do component lifecycle hooks work in a component that is created and attached dynamically? What is the best way to make these hooks work?
I don't see rendered string Dynamic load works 123 still if I remove {{ a }} in template (template: '<p>Dynamic load works</p>'), Dynamic load works is rendered as it should. What is the reason and how can I fix that?
Is there a better way to assing inputs than doing Object.assign(ref.instance, inputs) as above?
PS. I'm using Angular 11
What is the appropriate place for adding a call to initialize a global listener in Angular app?
Here is the code:
export class AuthService {
constructor(
private store: Store<fromAuth.State>,
private afAuth: AngularFireAuth
) {
this.afAuth.auth.onAuthStateChanged(payload => {
if (payload) {
const user: UserBeta = {
uid: payload.uid,
displayName: payload.displayName,
email: payload.email,
emailVerified: payload.emailVerified
};
this.store.dispatch(AuthActions.authenticated({ user }));
} else {
this.store.dispatch(AuthActions.notAuthenticated());
}
});
}
As you could see I've added it to the constructor of the AuthService but it doesn't seem right for me.
What I'm also concerning about is that the following code has two dependencies: Ngrx and AngularFireAuth.
In this case, would it be correct to move somewhere to the FirebaseModule (i.e. firebase.module.ts) and if yes, how is the call will look like?
You can add it inside ngOnInit(), from the docs:
A callback method that is invoked immediately after the default change detector has checked the directive's data-bound properties for the first time, and before any of the view or content children have been checked. It is invoked only once when the directive is instantiated.
Check here for more info:
https://angular.io/api/core/OnInit
Thank all of you for replies.
I've finally decided to introduce a new initialize() method inside the AuthService and call it inside the ngOnInit() method of the AppComponent.
auth.service.ts:
#Injectable({ providedIn: 'root' })
export class AuthService {
constructor(
private http: HttpClient,
private store: Store<fromAuth.State>,
private afAuth: AngularFireAuth
) { }
initialize() {
this.afAuth.auth.onAuthStateChanged(payload => {
if (payload) {
const user: UserBeta = {
uid: payload.uid,
displayName: payload.displayName,
email: payload.email,
emailVerified: payload.emailVerified
};
this.store.dispatch(AuthActions.authenticated({ user }));
} else {
this.store.dispatch(AuthActions.notAuthenticated());
}
});
}
}
app.component.ts:
export class AppComponent implements OnInit {
constructor(private authService: AuthService) { }
ngOnInit(): void {
this.authService.initialize();
}
}
Update: At my project I'm using ngrx for state management. Since AngularFireAuth also manages user information I've faced difficulty in managing the same state in multiple places, which increased the complexity, so the final solution became quite complicated. In the end, I've decided to stop using the onAuthStateChanged listener and start persisting the ngrx state locally.
I am very new in Angular 2 and I have a doubt about how exactly works this use of the cross component comunication using services.
In my application I have this RecipeService service class:
#Injectable()
export class RecipeService {
// Hold a Recipe object to be emitted to another component to implement cross component comunication:
recipeSelected = new EventEmitter<Recipe>();
// List of all recipes (maybe later can be obtained by a web service)
private recipes: Recipe[] = [
new Recipe(
'Tasty Schnitzel',
'A super-tasty Schnitzel - just awesome!',
'https://upload.wikimedia.org/wikipedia/commons/7/72/Schnitzel.JPG',
[
new Ingredient('Meat', 1),
new Ingredient('French Fries', 20)
]),
new Recipe('Big Fat Burger',
'What else you need to say?',
'https://upload.wikimedia.org/wikipedia/commons/b/be/Burger_King_Angus_Bacon_%26_Cheese_Steak_Burger.jpg',
[
new Ingredient('Buns', 2),
new Ingredient('Meat', 1)
])
];
// Inject a sub service:
constructor(private slService: ShoppingListService) {}
/**
* Return a copy of the reipes array.
* #returns {Recipe[]}
*/
getRecipes() {
return this.recipes.slice();
}
addIngredientsToShoppingList(ingredients: Ingredient[]) {
this.slService.addIngredients(ingredients);
}
}
This class is used by 2 different components to implement the cross component comunication by this emitter:
recipeSelected = new EventEmitter<Recipe>();
From what I have understood (correct me if I am doing wrong assertion) this recipeSelected emit event that holds the information contained into a Recipe object (it contains some string fields).
Then I have this RecipeItemComponent component (it represents a recipe and it views show the information related a specific recipe):
#Component({
selector: 'app-recipe-item',
templateUrl: './recipe-item.component.html',
styleUrls: ['./recipe-item.component.css']
})
export class RecipeItemComponent implements OnInit {
#Input() recipe: Recipe;
// Inkect the RecipeService to use it in this component:
constructor(private recipeService: RecipeService) { }
ngOnInit() {
}
/**
* When a specific recipe is selected in the page it emit the selected recipe to comunicate
* with another component
*/
onSelected() {
this.recipeService.recipeSelected.emit(this.recipe);
}
}
When the user click on a link into the view related to this RecipeItemComponent the onSelected() method of this class is performed.
From what I know it simply emit an event related to this Recipe object. So I think that it is shooting to someone else the content of this object, where someone else should be another component (so it is implemented the cross components comunication concept).
Then I have this other RecipesComponent component class:
#Component({
selector: 'app-recipes',
templateUrl: './recipes.component.html',
styleUrls: ['./recipes.component.css'],
providers: [RecipeService]
})
export class RecipesComponent implements OnInit {
selectedRecipe: Recipe;
/**
* Inject the RecupeService to use it in this component
* #param recipeService
*/
constructor(private recipeService: RecipeService) { }
/**
* Subscribe on the event emitted when a recipe is selected:
*/
ngOnInit() {
this.recipeService.recipeSelected
.subscribe(
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
);
}
}
From what I can understand I am registering the "listener" (is it a listerner?) for this kind of events into the ngOnInit() method, by:
ngOnInit() {
this.recipeService.recipeSelected
.subscribe(
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
);
}
So, in practice, every time that the RecipeItemComponent component emit an event containing a Recipe object, this information is received by the RecipesComponent component that use it. Is it?
Then I have a doubt about this sintax:
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
What exactly means? I think that recipe: Recipe is the content of the received event. It is something like an implicit way to declare a function? (I came from Java and I am not so into this kind of syntax).
Another doubt is: why this code is declared into the ngOnInit()? My idea is that so it declare a listener when this component is created and then this listener react to events that could come in a second time. Is it?
An EventEmitter should not be used in a service.
See this post: What is the proper use of an EventEmitter?
From that post:
Use by directives and components to emit custom Events.
Not for use in services. As #Pablo mentioned, even for components it is recommended that you use #Output to expose your event.
For a service, normally Angular's change detection will handle changes to the service data. So all you need to do is expose that data. I have an example here:
https://blogs.msmvps.com/deborahk/build-a-simple-angular-service-to-share-data/
And a corresponding plunker here: https://plnkr.co/edit/KT4JLmpcwGBM2xdZQeI9?p=preview
import { Injectable } from '#angular/core';
#Injectable()
export class DataService {
serviceData: string;
}
So this:
#Injectable()
export class RecipeService {
recipeSelected = new EventEmitter<Recipe>();
Becomes this:
#Injectable()
export class RecipeService {
recipeSelected: Recipe;
And this:
onSelected() {
this.recipeService.recipeSelected.emit(this.recipe);
}
Becomes this:
onSelected() {
this.recipeService.recipeSelected=this.recipe;
}
And this:
export class RecipesComponent implements OnInit {
selectedRecipe: Recipe;
ngOnInit() {
this.recipeService.recipeSelected
.subscribe(
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
);
}
}
Becomes this:
export class RecipesComponent implements OnInit {
get selectedRecipe(): Recipe {
return this.recipeService.recipeSelected;
};
}
Every time the recipeSelected changes, the Angular change detection will be notified and the UI will rebind to the current value of selectedRecipe.
I think you have nailed the description of that piece of code. I think I would not use a service to emit a recipe, but just an #Output attribute, but anyway your analysis is correct.
About the arrow notation, I recommend you to read the MDN documentation.
And about the ngOnInit(): Usually in Angular the constructor is used to inject dependencies only, because the main initialization logic is set in the ngOnInit method just because all the attributes decorated with #Input are initialized just before calling this method, so the visual "construction" of the component won't be done before this method is called.
How can I create a global object in angular2. I am collecting data from one page to another means page by page(may be for 4-5 pages) using navParams in ionic2. but want to store it in global object & finally submit it. I have tried it using global provider but haven't got most of it & not getting any clue also. Please any suggestion.
As with my question ionic community suggest to use global provider. i did use global provider to hold my 7 step form data and final submit on last step.
i used with ionic 3.
provider code:
import { Injectable } from '#angular/core';
#Injectable()
export class NcConnectionProvider {
public statecode:any;
public districtcode:any;
constructor() {
//console.log('Hello NcConnectionProvider Provider');
}
}
Page.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { NcConnectionProvider } from '../../providers/nc-connection/nc-connection';
#IonicPage()
#Component({
selector: 'page-nc-connection',
templateUrl: 'nc-connection.html',
})
export class NcConnectionPage {
public distbform:FormGroup;
public statedata:any;
public districtdata:any;
constructor(public navCtrl: NavController,
public navParams: NavParams,
public ncdatashare: NcConnectionProvider) {
}
locateDistb(form: NgForm){
this.ncdatashare.statecode = this.distbform.value.state;
this.ncdatashare.districtcode = this.distbform.value.district;
}
}
you can inject provider to multiple pages as you want and access your global variable/object. like in example value set to this.ncdatashare.statecode = this.distbform.value.state; now you can access this.ncdatashare.statecode to other page but provider must inject there.
Make sure to provide some sample code to elaborate the problem better. Moreover, following is the way you can follow to collect the data in single object.
Create a service with a variable and its getter setter. Inject this service in your component and set the data wherever needed and later use its get method to collect it back again.
#Injectable()
export class DataService{
private _data: any;
get data() {
return this._data;
}
set data(data: any) {
this._data = data;
}
}
and in side your component
#Component({
// configurations
})
export class LoginComponent {
constructor(public dataService: DataService) {
this.dataService.data = { userAge: 37 }
}
}
and where you need to submit this data, just use the getter to collect it back.
let finalData = this.dataService.data;
Not sure about your use case. But I hope it might help you.
I've got a small Plunk I'm using for playing around with the new Router 3.0 alpha currently available in Angular 2. It works well in general, but the issue is that once I click on a link that routes to the 'detail' component with a particular ID, it never changes when I click on a different link with a different ID. The component is never being reinstantiated, so it only ever shows what it was passed the very first time it is loaded.
Here's the component in question:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ContactsService } from './contacts.service';
#Component({
selector: 'contacts-detail',
template: `
<h2>{{contact.name}}</h2>
`
})
export class ContactsDetailComponent implements OnInit {
constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
}
ngOnInit() {
this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
console.log('Fetching user', this.route.snapshot.params.id);
}
}
Here is the Plunk demonstrating the problem. Click on one author name and then another to see it not change.
In your ContactsDetailComponent, change the OnInit to this:
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.contact = this.contactsService.getContact(id);
});
}
Worked for me in your Plunk.
There appear to be multiple lifeCycle hooks that could possibly be used for this. I managed to get the desired behavior using the DoCheck interface and implementing the associated ngDoCheck() method in the component class, as seen below.
import { Component, DoCheck } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ContactsService } from './contacts.service';
#Component({
selector: 'contacts-detail',
template: `
<h2>{{contact.name}}</h2>
`
})
export class ContactsDetailComponent implements AfterViewChecked, DoCheck {
constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
}
ngDoCheck() {
this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
}
}
Here's a plunk with the updated code.
I'm not convinced this is the best/correct lifecycle hook to use, though. Perhaps there is some sort of hook available from the Router that would serve this better.
Another way to do this:
ngOnInit() {
this.route.params.forEach((params: Params) => {
let id = +params['id'];
this.contact = this.contactsService.getContact(id);
});
}
Here retrieve the route params from an Observable. The advantage of using an Observable over Snapshot is to reuse the component without instantiating it again. Looks like this is the recommended way of doing this as per Angular 2.0 final documentation.