im trying to make a portfolio where when if someone wants changes the language in the menu the components change what json load for getting all data. I try to use BehaviourSubject and subject but i cant understand them so it difficult to use them. sorry for any mistake in english im learning
this is my service
export class PortfolioService {
language: string = 'espaniol';
constructor(private http: HttpClient) {}
obtenerDatos(): Observable<any> {
if (this.language === 'espaniol') {
return this.http.get('./assets/i18n/espaniol.json');
} else {
return this.http.get('./assets/i18n/english.json');
}
}
changeLang(value: string) {
this.language = value;
}
}
this is my header
export class HeaderComponent {
#Input() currentSection = 'section1';
siteLanguage = 'english';
languageList = [
{ code: 'english', label: 'English' },
{ code: 'espaniol', label: 'Español' },
];
constructor(private portfolioService: PortfolioService) {}
changeLang(localeCode: string) {
this.portfolioService.changeLang(localeCode);
}
scrollTo(section: string) {
document.querySelector('#' + section)!.scrollIntoView();
}
}
my template
<ng-container *ngFor="let language of languageList">
<li role="menuitem">
<a class="dropdown-item" (click)="changeLang(language.code)">
{{ language.label }}
</a>
</li>
</ng-container>
and my component that load the data
export class HomeComponent {
constructor(private datosPortfolio: PortfolioService) {}
miPortfolio: any;
ngOnInit(): void {
this.datosPortfolio.obtenerDatos().subscribe((data) => {
this.miPortfolio = data;
});
}
}
i tried to make a portfolio where i could change the language with a service that picked up when a changed happened in the header. the changed get picked up but the language is not changed in other components.
you need to wrap your data source observable with observable that changes when language changes. for that, the best is to use the BehaviorSubject.
take a look at this:
export class PortfolioService {
language = new BehaviorSubject<string>('espaniol');
constructor(private http: HttpClient) {}
obtenerDatos(): Observable<any> {
return this.language.asObservable().pipe(
switchMap(lang => this.http.get(`./assets/i18n/${lang}.json`))
)
}
changeLang(value: string) {
// update the value of this BehaviorSubject, and all
// subscribers notify about it
this.language.next(value);
}
}
this way every time language changed, new source emit in this.language BehaviorSubject and subscribe function fires again because of the new value, and them switch to other observable that makes the HTTP request with the language as a parameter that goes into the URL of the request.
hope it helps :)
Related
In Angular 9+ I can successfully convert a string to a html and then load that that html using innerHtml and bypassSecurityTrustHtml().
My question is it possible to also dynamically load/render the converted html to include and recognise angular/javascript markup language eg *ngIf, handle bars and click events.
Below is the code and stackblitz at the attempt so far but as you can see it doesn't recognise the markup.
https://stackblitz.com/edit/dynamic-angular?file=app/app.component.ts
export class AppComponent implements OnInit {
text: string = "Hello world";
content: any;
constructor(private domSantizer: DomSanitizer) {}
ngOnInit() {
let body: any =
'<div>{{text}}<div><br><button (click)="test()">Test</button>';
this.content = this.domSantizer.bypassSecurityTrustHtml(body);
}
test() {
alert("It works");
}
}
Html
<div [innerHTML]="content"></div>
I have researched and tried many solutions.
My research and trial results are below.
html
<div #container></div>
typescript side as below
export class AppComponent implements OnInit {
#ViewChild("container", { read: ViewContainerRef })
container: ViewContainerRef;
constructor(private compiler: Compiler) {}
text: string = "asdasd";
ngOnInit() {
this.addComponent(
`<div>{{text}}<div><br><button (click)="test()">Test</button>
`,
{
text: "Hello word",
test: function() {
alert("It's work");
}
}
);
}
private addComponent(template: string, properties?: any = {}) {
#Component({ template })
class TemplateComponent {}
#NgModule({ declarations: [TemplateComponent] })
class TemplateModule {}
const mod = this.compiler.compileModuleAndAllComponentsSync(TemplateModule);
const factory = mod.componentFactories.find(
comp => comp.componentType === TemplateComponent
);
const component = this.container.createComponent(factory);
Object.assign(component.instance, properties);
// If properties are changed at a later stage, the change detection
// may need to be triggered manually:
// component.changeDetectorRef.detectChanges();
}
demo
some posts I have reviewed
compile dynamic Component
angular-html-binding
I think it makes the most sense :)
I managed to display of the current user email, but how do I get the user id?
Template:
<div *ngIf="!hasLoggedIn">
{{ (profile$ | async)?.email }}
</div>
TypeScript:
import { Profile, GetProfile, ProfileState, } from '#abp/ng.core';
export class Component implements OnInit {
#Select(ProfileState.getProfile) //State
profile$: Observable<Profile.Response>; //Model
get hasLoggedIn(): boolean {
return this.oAuthService.hasValidAccessToken();
}
constructor(private oAuthService: OAuthService) {}
ngOnInit() {
this.store.dispatch(new GetProfile()).subscribe(); //Action
}
}
Inside app-routing.module.ts there is import ofApplicationLayoutComponent and inside there is a declaration of the variable currentUser $: Observable <ApplicationConfiguration.CurrentUser>; which is being used to display the user name in the navbar, and inside the ApplicationConfiguration models there is an Id, but I couldn't implement it as I did with email
Ps: Sorry for my English 😂
you can use GetCurrentLoginInformations() which resides in SessionServiceProxy.
It returns an object which contains UserLoginInfoDto which contains email, name etc
public async Task LinkToUser(LinkToUserInput input)
{
var loginResult = await _logInManager.LoginAsync(input.UsernameOrEmailAddress, input.Password, input.TenancyName);
if (loginResult.Result != AbpLoginResultType.Success)
{
throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, input.UsernameOrEmailAddress, input.TenancyName);
}
if (AbpSession.IsUser(loginResult.User))
{
throw new UserFriendlyException(L("YouCannotLinkToSameAccount"));
}
if (loginResult.User.ShouldChangePasswordOnNextLogin)
{
throw new UserFriendlyException(L("ChangePasswordBeforeLinkToAnAccount"));
}
await _userLinkManager.Link(GetCurrentUser(), loginResult.User);
}
I've set up next.service.ts with 3 variables (user, action, rest) and made setters(updateNext()) and getters (getUser, getAction, getRest). I've got to use the setter to change the variables in one component (stock-management component) and retrieved these variables in another component (inventory-record component) but I can't seem to retrieve them from another component (inventory-record-filled component).
I've tried returning a string ("TEST") in the getter and it worked, but when I tried returning a variable, it just returned nothing/empty string.
export class NextService {
private action: string;
private user: string;
private restraunt: string;
constructor() { }
updateNext(actions, users, restraunts) {
this.action = actions;
this.user = users;
this.restraunt = restraunts;
}
getAction(): string {
return this.action;
}
getUser(): string {
return this.user;
}
getRest(): string {
return this.restraunt;
}
export class InventoryRecordComponent implements OnInit {
name = '';
rest = '';
action = '';
constructor(private next: NextService) {
this.name = this.next.getUser();
this.action = this.next.getAction();
this.rest = this.next.getRest();
}
ngOnInit() {
document.getElementById('dne').style.display = 'none';
}
onSubmit(f: NgForm) {
const x = document.getElementById('dne');
if (!this.next.updateCode(this.code)) {
x.style.display = 'block';
f.resetForm();
} else {
this.next.updateCode(this.code);
location.replace('inventory-record/qty');
}
}
}
export class InventoryRecordFilledComponent implements OnInit {
name: string;
action: string;
rest: string;
constructor(private next: NextService) {
this.name = this.next.getUser();
this.action = this.next.getAction();
this.rest = this.next.getRest();
}
ngOnInit() {
}
}
Each component have its respective html files with {{ name }} {{ action }} {{ rest }}
If you need your component to behave as a Simpleton (where it contains the same values regardless of where in the application it is used) you must set its providedIn value to "root", like so:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class NextService {
// The rest of the code stays the same
}
The description for that can be found here: https://angular.io/guide/singleton-services#providing-a-singleton-service
If you don't do that, each component that imports NextService will have it's own instance of NextService, with its own isolated values. If you want the values of a service to be available everywhere that the service is used in, then you want the service to be a Simpleton, so you must follow the steps.
Following the steps above is not the only way to make your component a Simpleton, but as the link mentions, it is the preferred way to do that.
Hope that helps!
This question already has answers here:
Angular 2 http.post() is not sending the request
(3 answers)
Closed 3 years ago.
Okay so what I'm trying to do is I have an *ngFor that populates with category names what I'm trying to do is get the one the user clicked on and then making an API call with it but when I click the button nothing happens. Is there a way to pass the const to my api.service or is the way I'm doing it in the component the only way to achieve this.
section: string;
sectionlink(text: any): void {
this.section = text;
const endpointlist = 'https://api.nytimes.com/svc/books/v3/lists/{{this.section}}?api-key=my-key-here';
return this.http.get<Listmodel[]>(endpointlist);
console.log(this.section);
}
<li class="uk-active"><button (click)="sectionlink(section.list_name)">{{section.list_name}}</button></li>
service.ts
getSectionlinks(text: string): Observable<Listmodel[]> {
const endpointlist = `https://api.nytimes.com/svc/books/v3/lists/${text}?api-key=7W3E72BGAxGLOHlX9Oe2GQSOtCtRJXAt`;
return this.http.get<Listmodel[]>(endpointlist);
}
component.ts
sectionlink(text: string){
this.service.getSectionlinks(text).subscribe(response => this.sectionData = response);
}
HTML
<li class="uk-active">
<button (click)="sectionlink(section.list_name)">{{section.list_name}}< /button>
</li>
Assuming the text you send in your function is valid, you can do something like this.
sectionlink(text: string): void {
const endpointlist = 'https://api.nytimes.com/svc/books/v3/lists/' + text + '?api-key=7W3E72BGAxGLOHlX9Oe2GQSOtCtRJXAt';
this.http.get<Listmodel[]>(endpointlist).subscribe(result => {
console.log(result);
});
}
This will call your API and subscribe the result. For more info about HttpClient, please check the documentation here
Yes you can retrieve API call results right from the component, as Antoine showed. However, as your app grows, so will your component, so it's a best practice to put your API calls in a separate service like so:
import {HttpClient} from '#angular/common/http';
#Injectable({providedIn:'root'})
export class APIService {
public static API_ENDPOINT = "https://api.nytimes.com/svc/books/v3/lists/";
private static API_KEY = "IUOAHIUSYDFGOIAGHSFDOIHASDH"; // made up api key
// the app will inject the HttpClient for you when this class is instantiated
constructor(private http: HttpClient) {}
// you can call this method whatever you like, as long as it makes sense
public getBooks(text:string):Observable<ListModel[]> {
return this.http.get<ListModel[]>(APIService.API_ENDPOINT + text + "?api-key=" + APIService.API_KEY);
}
}
then in your component you can just call this method:
// angular will inject your custom service here
constructor(private apiService: APIService) {}
public sectionlink(text:string) {
this.apiService.getBooks(text).subscribe(response => {
console.log(response);
// do whatever you need to do with response here
});
}
don't forget to provide this in your module (only if this is part of a feature module; the #injectable({providedIn:'root'}) should take care of this):
#NgModule({
declarations: [
YourCustomComponent
],
imports: [
HttpClientModule
],
providers: [APIService],
})
export class FeatureModule { }
I need to display the data on html that I get from web service. I am able to see the data in a format that I want, but I can't display properly on html. I think -any- in http.get is the problem. I can read data in console without -any- but it works fine with . When it works with it, it still does not print in html properly. Can anyone provide advice on this?
html
<div>{{this.res}}</div>
app.component.ts
import { Component, OnInit } from '#angular/core';
//import { IMovie } from './movie';
import { AppService } from './app.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
res: any[] ;
errorMessage: string;
constructor(private _appService: AppService) { }
ngOnInit(): void { this.getData(); }
getData(): void {
this._appService.getData()
.subscribe(
(res: any []) => this.res = res,
(error: any) => this.errorMessage = <any>error);
}
}
app.service.ts :
Injectable()
export class AppService {
private urlNorth = '';
constructor(private http: HttpClient) { }
getData(): Observable<any> {
const headers = new HttpHeaders();
headers.set('Content-Type', 'text/sml');
headers.set('Accept', 'text/xml');
headers.set('Content-Type', 'text/xml');
return this.http.get<any>(this.urlNorth,{responseType:'text', headers: headers})
.do(data => {
// console.log(data)
var dataParsed = data.replace('<string xmlns="service">', '').replace('</string>', '').replace(/</g, '<').replace(/>/g, '>');
// console.log(dataParsed);
parseString(dataParsed, (err, res) => {
if (err) {
return console.dir('invalid XML');
}
else {
console.log(res);
console.log(res.NewDataSet.Table[0].DataPointName[0]);
}
})
})
.catch(this.handleError);
}
**data in console w/o any **
{{this.res}} in html
I'm pretty sure you don't have to put any at this line in app.service.ts
return this.http.get<any>(this.urlNorth,{responseType:'text', headers: headers})
because get method expects 0 type arguments.
Type any is not the problem. It's just TypeScript annotation to organise your code. The problem is you are refering to the res in inline template as this.res, but you should just res. However it won't work as you think. Looking at your data structure You will have to iterate throught this data due to Table is an array. Additionaly I Highly suggest to always represnt your data as class
export class Apps {
public Table: Array<any>; //here should be another type instead of "any"
/* rest of data if any */
}
Back to your question you should have in your html file <div>{{res}}</div> but that's just print your object as string if I good remember. So to properly access your data you should iterate through table using *ngFor
<div *ngFor="let el of res.NewDataSet.Table">
<span>{{el.BackColor}}</span>
<!-- all other data -->
</div>
It looks as though the data is coming back. I'll answer your initial question first (since you added a few issues in comments):
My guess is when you get data back, it's not showing because it's HTML, and angular doesn't like injecting html.
Add this to your TS:
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
res[]: safeHTML;
And change your html to this:
<div [innerHTML]="res"></div>
As mentioned in a previous answer, this is a solution for a single return of res, not an array of different htmls. If it's an array, you'll have to handle it accordingly. for instance:
<ng-container *ngFor="let r of res">
<div [innerHTML]="r">
</ng-container>