I am trying to change the URL when someone clicks on a choosen language using ngx-translate. I guess I should do it by subscribing to language change events and then modifying the current url to reflect the chosen language, Since I am a newbie I am not sure if I need a service to do it, or may be another way to solve it.
I want to change from this:
https://amarello.cloud/es/
To this:
https://amarello.cloud/en/
,depending on the choosen language.
This is my 'header.component.ts'
import { Component, OnInit } from '#angular/core';
//For translate language
import { TranslateService } from '#ngx-translate/core';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
menu: boolean = false;
submenu: boolean = false;
submin: boolean = false;
constructor(private translate: TranslateService)
{ translate.addLangs(['es','en']);
translate.setDefaultLang('es');
}
ngOnInit(): void {
}
open() {
if (this.menu == false) {
this.menu = true;
} else {
this.menu = false
}
if (this.submin == false) {
this.submin = true;
} else {
this.submin = false
}
}
openSub() {
if (this.submenu == false) {
this.submenu = true;
} else {
this.submenu = false
}
}
useLanguage(language: string): void {
this.translate.use(language);
// this.translate.onLangChange().subscribe(trans => {
// })
}
}
Related
I have a SPA that ultimately lists out a lot of data, but in batches.
I created a component at the bottom of the list, with a 'Visibility' directive so that when it is visible we make a new request to the dataset in a SQL server to get the next batch.
html-tag-for-component
<app-infinity-scroll
[(pageNumber)]="page"
[displayPage]="displayPage"
[authd]="authd"
[done]="done"
[numResults]="displayPage == 'tiles-hub' ? hubs.length : wallets.length"
class="{{scrollVisible ? '' : 'hiddenDisplay'}}"
trackVisibility
></app-infinity-scroll>
component-to-trigger-data-call
import { outputAst } from '#angular/compiler';
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '#angular/core';
import { DbSqlService } from 'services/db-sql.service';
import { TokenAuthService } from 'services/token-auth.service';
import { TrackVisibilityDirective } from 'src/app/directives/track-visibility.directive';
import { SortStyle } from 'src/app/interfaces/mvlot';
import { MatProgressBar } from '#angular/material/progress-bar';
#Component({
selector: 'app-infinity-scroll',
templateUrl: './infinity-scroll.component.html',
styleUrls: ['./infinity-scroll.component.scss']
})
export class InfinityScrollComponent implements OnInit {
#Input() pageNumber: number;
#Input() displayPage: string;
#Input() authd: boolean;
#Input() done: boolean;
#Input() numResults: number;
#Output() pageNumberChange = new EventEmitter<number>();
lastDisplay = '';
loading: boolean = true;
constructor(
private visTrack: TrackVisibilityDirective
, private cdr: ChangeDetectorRef
, private dbApi: DbSqlService
, private authService: TokenAuthService
) {
}
ngOnInit(): void {
this.authService.UserAuthd.subscribe((res) => {
// if (res) {
this.dbApi.initGetWalletsHandler(0, 50, SortStyle.scoreDesc);
this.pageNumber = 1;
// }
})
this.visTrack.visibile.subscribe((val) => {
if (!this.done) {
this.loading = true;
if (val) {
if (this.displayPage == 'tiles') {
this.dbApi.initGetWalletsHandler((this.pageNumber) * 50, 50, SortStyle.default);
this.pageNumber += 1;
}
if (this.displayPage == 'tiles-hub') {
this.dbApi.initGetHubsHandler((this.pageNumber) * 50, 50);
this.pageNumber += 1;
}
}
}
})
}
}
Some functions run, call out to a back-end, respond with data, where a listener is waiting.
this.dbApi.resultObs.subscribe(val => {
if (val.append != true) {
this.results = [];
}
if (val.reset) {
this.page = 1;
}
val.data.data.forEach((b: any) => {
var result: Collection;
var existingResults = this.results.filter(w => w.ownerId == b.ownerId);
if (existingResults.length == 0) {
result = {
ownerId: b.ownerId
, totalScore: b.modifiedLandScore
, filteredCount: b.filteredCount
, totalLots: b.totalLots
, totalPrice: b.totalPrice
, name: ''
, lands: []
, type: CollectionType.b
}
result.bs.push(b);
this.results.push(result);
} else {
result = existingResults[0];
result.bs.push(b);
}
});
this.resultDataSource = new MatTableDataSource(this.results);
this.collectionType = CollectionType.b;
this.uiService.loadingBar(false);
this.done = val.data.data.length == 0;
this.cdr.detectChanges();
})
And, finally this is laid out for the user:
<tr *ngFor="let result of results">
<td>
<display-block
[collection]="b"
[displayVertical]="displayVertical"
[displayCaseCount]="displayCaseCount"
[gridClassName]="gridClassName"
[authd]="authd"
[type]="result.type"
[expanded]="results.length == 1"
[isPhonePortrait]="isPhonePortrait"
></display-block>
</td>
</tr>
Everything works fine on the first grab of data.
And everything appears to work fine on the second pull, but for any of the items appended to the view with the second pull, ChangeDetector just seems to give up. I'll trigger an action, that should modify the view, but nothing happens, unless I manully put in cdr, or I flip to a new window, or something, then they respond.
I'm going to continue trying to find a root cause, but at the moment, I'm out of ideas. There's no prominent error message that would imply something broke. The items fromt the first batch still work. But the ones from the second will appear to lock up. until CDR is forced by an outside event.
I wanted to check here to see if anyone had any ideas on what may be causing this.
Also, here's the declaration code for 'trackVisibility'
import {
Directive,
ElementRef,
EventEmitter,
NgZone,
OnDestroy,
OnInit,
Output,
} from '#angular/core';
#Directive({
selector: '[trackVisibility]',
})
export class TrackVisibilityDirective implements OnInit, OnDestroy {
observer!: IntersectionObserver;
#Output()
visibile = new EventEmitter<boolean>();
constructor(private el: ElementRef<HTMLElement>, private ngZone: NgZone) {}
ngOnInit(): void {
this.ngZone.runOutsideAngular(() => {
this.observer = new IntersectionObserver((entries) => {
entries.forEach((e) => {
this.visibile.emit(e.isIntersecting);
});
});
this.observer.observe(this.el.nativeElement);
});
}
ngOnDestroy(): void {
this.observer.disconnect();
}
}
here is the solution
You used runOutsideAngular function in your Directive.
"Running functions via runOutsideAngular allows you to escape Angular's zone and do work that doesn't trigger Angular change-detection or is subject to Angular's error handling. Any future tasks or microtasks scheduled from within this function will continue executing from outside of the Angular zone."
I also changed some parts of the code for more readability.
Dear I am developing a page with Angular 7 and I am presented with the error TS2559: Type 'BookInterface[]' has no properties in common with type 'BookInterface', I have changed the code but I still can not find the solution, I leave the code below, the error is thrown in the method getListBooks(): this is my file list-books.component.ts
import { BookInterface } from './../../../models/book';
import { DataApiService } from './../../../services/data-api.service';
import { Component, OnInit } from '#angular/core';
import {NgForm} from '#angular/forms';
#Component({
selector: 'app-list-book',
templateUrl: './list-book.component.html',
styleUrls: ['./list-book.component.css']
})
export class ListBookComponent implements OnInit {
constructor(private dataApi: DataApiService) { }
private books: BookInterface = {};
ngOnInit() {
this.getListBooks();
}
getListBooks() {
this.dataApi.getAllBooks().subscribe(books => {
this.books = books;
});
}
onDelete() {
console.log('LIBRO ELIMINADO');
}
}
I also leave the code of my data-api.service.ts from where I call the interface
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs/internal/Observable';
import { Injectable } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from '#angular/fire/firestore';
import { BookInterface } from '../models/book';
#Injectable({
providedIn: 'root'
})
export class DataApiService {
constructor(private afs: AngularFirestore) {
this.bookCollecction = afs.collection<BookInterface>('books');
this.books = this.bookCollecction.valueChanges();
}
private bookCollecction: AngularFirestoreCollection<BookInterface>;
private books: Observable<BookInterface[]>;
private bookDoc: AngularFirestoreDocument<BookInterface>;
private book: Observable<BookInterface>;
getAllBooks() {
return this.books = this.bookCollecction.snapshotChanges()
.pipe(map( changes => {
return changes.map( action => {
const data = action.payload.doc.data() as BookInterface;
data.id = action.payload.doc.id;
return data;
});
}));
}
// metodo que trae un libro a traves de su id
getOneBook(idBook: string) {
this.bookDoc = this.afs.doc<BookInterface>(`books/${idBook}`);
return this.book = this.bookDoc.snapshotChanges().pipe(map(action => {
if (action.payload.exists === false){
return null;
} else {
const data = action.payload.data() as BookInterface;
data.id = action.payload.id;
return data;
}
}));
}
addBook(book: BookInterface): void {
this.bookCollecction.add(book);
}
updateBook(book: BookInterface): void {
let idBook = book.id;
this.bookDoc = this.afs.doc<BookInterface>(`books/${idBook}`);
this.bookDoc.update(book);
}
deleteBook(idBook: string): void {
this.bookDoc = this.afs.doc<BookInterface>(`books/${idBook}`);
this.bookDoc.delete();
}
}
The version of typescript that I am currently using is Version 2.7.2, but also update it without solving the problem
You need to change the following:
private books: BookInterface = {};
to:
private books: BookInterface[] = [];
I have three components. A parent component (dashboard.component.ts) which houses two siblings (poll-form.component.ts and poll.component.ts). One of the siblings, poll-component sends an Output() back to the parent called (editEvent)="editPoll($event)". When this Output() is sent I'd like that to trigger a function inside of the other sibling poll-form.component instead of triggering the function in the parent dashboard.component. What's the correct way of doing this?
My current implementation "sort of" works. It fires the function in the parent and then the parent imports the component and calls the function in the sibling component. The issue, however, is that sibling component skips the ngOnInit() and doesn't have access to the component variables needed within the function.
Ultimately what I'm trying to do is break out the form into its own component but the act of opening the form is triggered by a sibling component.
dashboard.component.html
<div class="page">
<div style="margin-bottom: 20px; margin-right: 20px; text-align: left;">
<button type="button" (click)="showCreateModal()" pButton icon="pi pi-check" label="Create Poll"></button>
</div>
<div *ngFor="let pollId of pollIds" style="display: inline-block;">
<app-poll [pollKey]="pollId" (editEvent)="editPoll($event)" (deleteEvent)="deletePoll($event)"></app-poll>
</div>
</div>
<div *ngIf="displayCreateModal">
<app-poll-form [type]="'create'" (closeEvent)="closeModal($event)"></app-poll-form>
</div>
<div *ngIf="displayEditModal">
<app-poll-form [type]="'edit'" (closeEvent)="closeModal($event)"></app-poll-form>
</div>
poll.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
import * as Chart from 'chart.js';
import { Observable } from 'rxjs';
import { FirebaseService } from '../services/firebase.service';
import { first } from 'rxjs/operators';
import { CardModule } from 'primeng/card';
import { AngularFireAuth } from '#angular/fire/auth';
#Component({
selector: 'app-poll',
templateUrl: './poll.component.html',
styleUrls: ['./poll.component.scss']
})
export class PollComponent implements OnInit {
poll:any;
#Input()
pollKey: string;
#Output()
editEvent = new EventEmitter<string>();
constructor(private firebaseService: FirebaseService, private afAuth: AngularFireAuth) { }
ngOnInit() {
this.firebaseService.getPoll(this.pollKey).subscribe(pollDoc => {
if (!pollDoc.payload.exists) {
return;
}
const pollData:any = pollDoc.payload.data();
this.poll = {
id: pollDoc.payload.id,
helperText: pollData.helperText,
pollType: pollData.pollType,
scoringType: pollData.scoringType,
user: pollData.user
};
}
edit() {
this.editEvent.emit(this.poll);
}
}
poll-form.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
import { DialogModule } from 'primeng/dialog';
import { DropdownModule } from 'primeng/dropdown';
import { AutoCompleteModule } from 'primeng/autocomplete';
import { SelectButtonModule } from 'primeng/selectbutton';
import { FirebaseService } from '../services/firebase.service';
import nflPlayers from '../../assets/data/players-nfl.json';
import nflPollTypes from '../../assets/types/poll-types-nfl.json';
import nflScoringTypes from '../../assets/types/scoring-types-nfl.json';
#Component({
selector: 'app-poll-form',
templateUrl: './poll-form.component.html',
styleUrls: ['./poll-form.component.scss']
})
export class PollFormComponent implements OnInit {
title:string;
btnLabel:string;
selectedScoringType:any;
choices:any;
selectedPollType:any;
selectedPollKey:any;
displayEditModal:boolean = false;
displayCreateModal:boolean = false;
filteredPlayersMultiple: any[];
displayModal:boolean = true;
nflPlayers:any = nflPlayers.Players;
nflPollTypes:any = nflPollTypes.types;
nflScoringTypes:any = nflScoringTypes.types;
activePlayers:any;
#Input()
type: string;
#Output()
closeEvent = new EventEmitter<string>();
constructor(
private firebaseService: FirebaseService
) { }
ngOnInit() {
this.initFormDefaults();
}
initFormDefaults() {
this.choices = [[],[]];
this.selectedScoringType = this.nflScoringTypes[0];
this.selectedPollType = this.nflPollTypes[0];
if (this.type == "create") {
this.btnLabel = "Create";
this.title = "Create Poll";
} else {
this.btnLabel = "Update";
this.title = "Edit Poll";
}
// Filter out active NFL players
this.activePlayers = this.nflPlayers.filter(player => player.active == "1");
}
editPoll(poll) {
this.type = "edit"
this.activePlayers = this.nflPlayers.filter(player => player.active == "1");
this.selectedPollKey = poll.id;
// Set scoring type
this.selectedScoringType = this.nflScoringTypes.find((type) => {
return type.code == poll.scoringType
});
// Set poll type
this.selectedPollType = this.nflPollTypes.find((type) => {
return type.code == poll.pollType
});
// Populate edit modal with properly formatted Player objects
for (let i=0; i < poll.choices.length; i++) {
this.choices[i] = poll.choices[i].players.map((choicePlayer:any) => {
return this.activePlayers.find(player => player.playerId == choicePlayer.id);
});
}
}
}
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { AngularFireAuth } from '#angular/fire/auth';
import { first } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { PollFormComponent } from '../poll-form/poll-form.component';
#Component({
providers: [PollFormComponent],
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent implements OnInit {
displayEditModal:boolean = false;
constructor(
private pollFormComponent: PollFormComponent
) { }
ngOnInit() {
}
editPoll(poll) {
this.displayEditModal = true;
this.pollFormComponent.editPoll(poll);
}
}
I have already done the project using simple java script. Now Its revamping as SPA using Angular.
Now I'm stucked to do the same using Angular.
Functionality:
Click the button to disable and append in particular div and if button clicks inside appended div then previously disabled button to be enabled.
That's it.
I have done other than to enable disabled button:
Problem is pBtn not available in ElementRef
Below is my code and stackblitz link:
Hope someone could help in this.
import { Component, OnInit, DoCheck, AfterViewInit, ViewChild, ElementRef,Renderer2 } from '#angular/core';
import { Interviewcreate } from '../../shared/interview-create';
import { Interview } from '../../shared/interview';
import { DataService } from '../../data-service';
import { Router } from '#angular/router';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Component({
selector: 'dashboard-component',
templateUrl: './dashboard-component.html',
styleUrls: [ './dashboard-component.css' ]
})
export class DashboardComponent implements OnInit, DoCheck, AfterViewInit, OnChanges {
users: Interviewcreate;
#ViewChild('answerbox') div:ElementRef;
#ViewChild('htmlToAdd') htmlToAdd:ElementRef;
#ViewChild('questionbox') questionbox:ElementRef;
question1 = ['<p>', '</p>', 'Polar bears live in the north pole']
constructor(private service: DataService,
private router: Router,
private http:HttpClient,
private renderer: Renderer2,
private el:ElementRef
){
}
ngOnInit(){
}
ngDoCheck(){
if(this.htmlToAdd.nativeElement.children.length>0){
Array.prototype.forEach.call(this.htmlToAdd.nativeElement.children, (element) => {
//console.log(element)
element.addEventListener('click', (e)=>{
this.resultview()
console.log(e)
e.target.remove()
})
});
}
}
ngAfterViewInit() {
let sss = this.el.nativeElement.querySelector('.dotted-box > button')
//.addEventListener('click', this.onClick.bind(this));
}
onClick(event) {
console.log(event);
}
getvalue(e){
const li = this.renderer.createElement('button');
const text = this.renderer.createText(e.target.textContent);
this.renderer.appendChild(li, text);
this.renderer.appendChild(this.htmlToAdd.nativeElement, li);
setTimeout(
()=>{
this.resultview()
}
,100)
e.target.disabled = true;
Array.prototype.forEach.call(this.htmlToAdd.nativeElement.children, (element) => {
this.renderer.addClass(element, 'btn');
this.renderer.addClass(element, 'btn-outline-primary');
});
}
resultview() {
this.div.nativeElement.innerHTML = this.htmlToAdd.nativeElement.textContent.trim();
}
}
Try out this, you had written some hard logic.
Push appending value will solve your problem.
export class DashboardComponent {
#ViewChild('answerbox') div:ElementRef;
#ViewChild('htmlToAdd') htmlToAdd:ElementRef;
#ViewChild('questionbox') questionbox:ElementRef;
question1 = ['<p>', '</p>', 'Polar bears live in the north pole' ]
questionboxvalue = [];
#Output() someEvent = new EventEmitter<string>();
constructor(private service: DataService,
private router: Router,
private http:HttpClient,
private renderer: Renderer2,
private el:ElementRef
){
}
onClick(event) {
console.log(event);
}
getvalue(e){
this.questionboxvalue.push({index: e.target.dataset.index, value: e.target.textContent.trim()})
e.target.disabled = true;
this.resultview();
}
getbvalue(event) {
this.someEvent.next(event);
Array.prototype.forEach.call(this.el.nativeElement.querySelectorAll('.shadowbutton'), (element, i)=>{
if(element.dataset.index === event.target.dataset.index) {
element.disabled = false;
this.questionboxvalue = this.questionboxvalue.filter((val)=>{
return val.index !== event.target.dataset.index;
})
this.resultview()
}
})
}
resultview() {
setTimeout(()=>{
this.div.nativeElement.innerHTML = this.htmlToAdd.nativeElement.textContent.trim();
}, 100)
}
}
Is there a posibility to set variables dynamically?
My code looks like this. The if gets true but how do I (if possible) set the variable to true dynamically?
import { Component, OnInit, } from '#angular/core';
import {forEach} from "#angular/router/src/utils/collection";
#Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {
menuContentSize = false;
menuContentBackground = false;
menuContentImages = false;
menuContentText = false;
menuContentFrame = false;
menuOptions: string[] = ['menuContentSize',
'menuContentBackground',
'menuContentImages',
'menuContentText',
'menuContentFrame'];
constructor() {
}
ngOnInit() {
}
menuOptionSelected(event){
this.menuOptions.forEach(function(element){
if(element == event){
// Set name of element(variable) to true
// In my dreamworld this.element = true; will be e.g. this.menuContentSize = true;
}
});
}
}
this.menuOptions.forEach(function(element){
needs to be
this.menuOptions.forEach((element) => {
if you want to use this to reference to the current component instance
See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
I'm not sure about the rest of your question.
I guess what you want is
this[element] = true;
which sets this.menuContentSize to true if element holds the string value 'menuContentSize'