I have a problem and very strange behavior with autofill in Chrome. When I login and then logout from app, input fields (email, password) were autocompleted but fields looks like they are frozen and not clickable.
This bug is not every time reproduced, it happens in 1/10 cases.
I noticed on logout when fields get autocompleted, after 1 seconds font gets smaller in inputs and after that if you click on input there it seems like you are not clicking , nothing happens, but if you type some text (numbers not work, stays like frozen) input field goes as normal.
Here is gif with strange behavior:
https://gifyu.com/image/kTkX
I tried to set autocomplete="off" , but not working.
Also I matched all css clases in input fields to see is there some overriding css, but everything looks good.
<form [formGroup]="loginForm">
<input id="emailHeader" type="text" formControlName="email" placeholder="E-mail">
<input #password type="password" formControlName="password" placeholder="Lozinka">
<input type="submit" (click)="executeLogin()" value="Prijava">
</form>
I expect to fields not been frozen after autofill.
public loginForm: FormGroup;
public emailInput: ElementRef;
public passwordInput: ElementRef;
#ViewChild('email') set emailContent(content: ElementRef) {
this.emailInput = content;
}
#ViewChild('password') set passwordContent(content: ElementRef) {
this.passwordInput = content;
}
// UI helpers
public showLoginForm: boolean;
public showBalance: boolean;
public player: PlayerModel = new PlayerModel({});
public balanceInfo: BalanceInfoModel = new BalanceInfoModel({});
public nxcsBalanceInfo: NXCSBalanceInfoModel = new NXCSBalanceInfoModel({});
public dialogType = DialogType;
public customMessageError = '';
// Store
private headerState$: Observable<any>;
private loginState$: Observable<any>;
private playerState$: Observable<any>;
private emailInput$: Observable<any>;
private passwordInput$: Observable<any>;
private balanceState$: Observable<any>;
private headerSubscription: Subscription;
private loginSubscription: Subscription;
private playerSubscription: Subscription;
private emailSubscription: Subscription;
private passwordSubscription: Subscription;
private balanceSubscription: Subscription;
// tslint:disable-next-line:no-inferrable-types
private leftMenu: string = '';
// tslint:disable-next-line:no-inferrable-types
private rightMenu: string = '';
constructor(
private authService: AuthService,
private fb: FormBuilder,
private store: Store<any>,
private route: Router,
private localStorageService: LocalStorageService,
private playerService: PlayerService,
private notificationService: NotificationService,
private dialogService: DialogService,
private helpers: HelpersService,
private translateCode: TranslateCode,
private promotionService: PromotionService,
) {
this.loginForm = this.buildLoginForm();
}
ngOnInit() {
this.setupStore();
}
ngAfterViewInit() {
this.formEventsAfterViewInit();
}
ngOnDestroy() {
this.headerSubscription.unsubscribe();
this.loginSubscription.unsubscribe();
this.playerSubscription.unsubscribe();
this.notificationService.closeConnection();
}
public executeLogin() {
if(!this.loginForm.valid) {
this.customMessageError = this.translateCode.transform("EMPTY_INPUT_MESSAGE");
return;
}
this.authService.login(new LoginModel({...this.loginForm.value, details: this.helpers.sendSessionData()}))
.subscribe(
data => {
this.localStorageService.setUserAfterLogin(data.token);
this.customMessageError = '';
this.loginForm.reset();
this.route.navigate(['/app/casino']);
},
error => {
error.message.includes('Račun je zaključan') ? this.store.dispatch(new PopupNotification(error.message)) : this.customMessageError = error.message
this.addAfterErrorSubscription();
}
);
}
public openDialog(dialogType: string): void {
switch (dialogType) {
case DialogType.PAYMENT:
this.openWithdrawalDialog()
break;
case DialogType.PAYMENT_DEPOSIT:
this.checkRegistrationStep();
break;
case DialogType.TRANSACTION_HISTORY:
this.store.dispatch(new OpenDialog({
type: dialogType,
}));
break;
}
}
public openInternalTransactionsDialog(): void {
this.promotionService.getPromotionsByLocation('NXCS_DEPOSIT')
.subscribe(
data => this.dialogService.openDialog(MENU_DIALOGS.INTERNAL_TRANSACTION, { promotions: data }),
error => this.dialogService.openDialog(MENU_DIALOGS.INTERNAL_TRANSACTION, { promotions: []}),
);
}
public backToRegistrationStep() : void {
switch (this.player.registrationStep) {
case 1 : this.route.navigate(['/auth/registration/step-two']);
break;
case 2 : this.route.navigate(['/auth/registration/step-three']);
break;
case 3 : this.route.navigate(['/auth/registration/step-four']);
break;
case 4 : this.route.navigate(['/auth/registration/step-five']);
break;
case 5 : this.route.navigate(['/auth/registration/step-six']);
break;
default : this.route.navigate(['/login']);
break;
}
}
public toggleMenu(dialog): void {
if (dialog === 'left') {
this.leftMenu = this.leftMenu === dialog ? '' : dialog;
}
if (dialog === 'right') {
this.rightMenu = this.rightMenu === dialog ? '' : dialog;
}
this.dispatchShadow();
}
private openWithdrawalDialog(_data: any = {}): void {
const playerRole = this.localStorageService.getPlayer()['profileRole'];
if (playerRole === 'WITHDRAWAL_DISABLED' && this.player.uploadedAdditionalInfo) {
this.store.dispatch(new OpenNotification({ type: NotificationType.WITHDRAWAL_DISABLED }));
return;
}
playerRole === 'WITHDRAWAL_DISABLED' ?
this.store.dispatch(new OpenNotification({type: NotificationType.MONEY_LAUNDERING})) :
this.dialogService.openDialog(MENU_DIALOGS.WHITDRAWALS, _data);
}
private openProceedToRegistration(): void {
this.store.dispatch(new OpenNotification ({type: NotificationType.PROCEED_REGISTRATION}))
}
private checkRegistrationStep(): void {
if(this.player.registrationStep < 6) {
this.openProceedToRegistration();
} else {
this.dialogService.openDialog(MENU_DIALOGS.DEPOSITS, {});
}
}
private dispatchShadow(): void {
if (this.leftMenu !== '') {
this.store.dispatch(new OpenedLeftMenu());
this.leftMenu = '';
}
if (this.rightMenu !== '') {
this.store.dispatch(new OpenedRightMenu());
this.rightMenu = '';
}
}
private buildLoginForm(): FormGroup {
return this.fb.group({
email: [
'', Validators.compose([Validators.required, Validators.min(5)]),
],
password: [
'', Validators.compose([Validators.required, Validators.min(5)])
],
});
}
private loadBalance(): void {
this.playerService.getPlayerBalance().toPromise()
.then(data => this.store.dispatch(new SetPlayerBalance({balanceInfo: new BalanceInfoModel(data) })))
.then(() => {
if (this.player.externalId) {
this.playerService.getNXCSPlayerBalance()
.subscribe(
data => this.store.dispatch(new SetPlayerNXCSBalance({ nxcsBalanceInfo: new NXCSBalanceInfoModel(data) })),
error => console.log(error),
);
}
});
}
// Store methods
private setupStore(): void {
this.headerState$ = this.store.pipe(select('headerStore'));
this.loginState$ = this.store.pipe(select('loginStore'));
this.playerState$ = this.store.pipe(select('playerStore'));
this.balanceState$ = this.store.pipe(select('balanceStore'));
this.addSubscriptions();
}
private formEventsAfterViewInit(): void {
if (this.emailInput && this.passwordInput) {
this.emailInput$ = fromEvent(this.emailInput.nativeElement, 'focus');
this.passwordInput$ = fromEvent(this.passwordInput.nativeElement, 'focus');
this.addFormEventsSubscriptions();
}
}
private addFormEventsSubscriptions(): void {
this.emailSubscription = this.emailInput$.subscribe(() => this.triggerEmailFocus());
this.passwordSubscription = this.passwordInput$.subscribe(() => this.triggerPasswordFocus());
}
private triggerEmailFocus(): void {
this.emailInput.nativeElement.select();
if (this.emailSubscription) {
this.emailSubscription.unsubscribe();
}
}
private triggerPasswordFocus(): void {
this.passwordInput.nativeElement.select();
if (this.passwordSubscription) {
this.passwordSubscription.unsubscribe();
}
}
private addSubscriptions(): void {
this.addHeaderSubscription();
this.addLoginSubscription();
this.addPlayerSubscription();
this.setBalanceSubscription();
}
private addHeaderSubscription(): void {
this.headerSubscription = this.headerState$
.subscribe(headerState => this.showLoginForm = headerState !== HeaderActionTypes.LoginPage);
}
private addLoginSubscription(): void {
this.loginSubscription = this.loginState$
.subscribe(loginState => {
if (loginState) {
this.loadBalance();
this.notificationService.connect(localStorage.getItem('token'));
} else {
this.notificationService.closeConnection();
}
this.showBalance = loginState;
this.formEventsAfterViewInit();
});
}
private addPlayerSubscription(): void {
this.playerSubscription = this.playerState$
.subscribe(playerData => this.player = playerData);
}
private addAfterErrorSubscription(): void {
this.passwordSubscription = this.passwordInput$.subscribe(() => {
if (this.customMessageError !== '') {
this.customMessageError = '';
this.passwordSubscription.unsubscribe();
}
});
}
}
I was facing the same issue in Angular and found a workaround for this scenario, resetting the input field's value on input click seems to fix the locking issue for me.
HTML:
<input (click)="unfreezeInput($event)">
TS:
unfreezeInput(el) {
// setting input value again makes it editable
el.target.value = el.target.value;
}
This is an issue that happens every time Google Chrome autofills the fields when you permit it to save your login credentials on any form. It bugged me out the first times as well.
I tried giving it a shot or two back when I encountered it for the first, but I gradually moved on and accepted it as a not-a-big deal kinda bug that every chrome user gotta have to deal with.
So in conclusion, it's got nothing to do with your code. If you want to unfreeze the input fields, start typing something in them to change their value.
I hope, this helps you and your nerves in accepting this browser-related behavior.
Have you tried using the a href tag? I know it's an obvious thing, but sometimes we forget to do things like that. Also, you can use the a:hover tag in your CSS.
Related
I'm encountering the error
ContestDetailsModalComponent.html:21 ERROR TypeError: Cannot assign to read only property 'winner' of object '[object Object]'
at ContestDetailsModalComponent.onWinner (contest-details-modal.component.ts:88:24)
at Object.eval [as handleEvent] (ContestDetailsModalComponent.html:21:17)
at handleEvent (core.js:43993:77)
at callWithDebugContext (core.js:45632:1)
at Object.debugHandleEvent [as handleEvent] (core.js:45247:1)
at dispatchEvent (core.js:29804:1)
at core.js:42925:1
at HTMLButtonElement.<anonymous> (platform-browser.js:2668:1)
at ZoneDelegate.invokeTask (zone-evergreen.js:399:1)
at Object.onInvokeTask (core.js:39680:1)
when I try to make the assignment
this.contest.winner = winnerId;
you see in the following code snippet:
public onWinner(winnerId: number): void {
const post: Post = new Post();
post.title = `The winner of the contest has been elected: ${this.contest.title}`;
post.description = `Tizio has been elected winner of the contest and ${this.user.name} gives him the most welcome compliments`;
post.user = this.user;
post.contest = this.contest;
post.type = Type.WINNER;
post.level = Level.SUCCESS;
this.contest.winner = winnerId;
this.store.dispatch(PostActions.savePost({post}));
this.store.dispatch(ContestActions.saveContest({contest: this.contest}));
}
I've done this kind of assignment with other classes around the project and they never bothered me.
I enclose the contest class and post if it is useful:
export class Contest {
public id: number;
public title: string;
public description: string;
public rules: string;
public startDate: Date;
public endDate: Date;
public status: Status;
public winner: number;
public bannedUser: number[];
}
export class Post {
public id: number;
public title: string;
public description: string;
public level: Level;
public type: Type;
public user: User;
public publishDate: Date;
public contest: Contest;
}
I also tried some solutions always found on this forum such as:
Object.assign(target, source);
But he rightly tells me that this.contest.winner is null or undefined.
I hope for your help. Thank you
Edit:
This is the entire component.ts where onWinner() is present.
#Component({
selector: 'app-contest-details-modal',
templateUrl: './contest-details-modal.component.html',
styleUrls: ['./contest-details-modal.component.scss'],
})
export class ContestDetailsModalComponent implements OnInit, OnDestroy, AfterViewChecked {
private readonly subscriptions: Subscription = new Subscription();
public id: number;
public user: User;
public contest: Contest;
public images: Image[];
public hasVoted: boolean;
constructor(
private readonly bsModalRef: BsModalRef,
private readonly modalService: BsModalService,
private readonly store: Store<AppState>,
private cdRef: ChangeDetectorRef
) { }
public ngOnInit(): void {
this.subscriptions.add(this.store.pipe(select(AuthSelectors.getUser)).subscribe((user: User) => {
if (user) {
this.user = user;
}
}));
this.subscriptions.add(this.store.pipe(select(ContestSelectors.getById)).subscribe((contest: Contest) => {
if (contest) {
this.contest = contest;
}
}));
this.subscriptions.add(this.store.pipe(select(ImageSelectors.getImages)).subscribe((images: Image[]) => {
if (images.length) {
this.images = images;
}
}));
this.subscriptions.add(this.store.pipe(select(UserSelectors.check)).subscribe((ack: Ack) => {
if (ack) {
this.store.dispatch(AuthActions.updatedUser({userId: this.user.id}));
this.modalService.show(UploadImageModalComponent);
this.bsModalRef.hide();
}
}));
this.subscriptions.add(this.store.pipe(select(ImageSelectors.check)).subscribe((ack: Ack) => {
if (ack) {
this.bsModalRef.hide();
}
}));
}
public ngOnDestroy(): void {
this.store.dispatch(UserActions.clean());
this.store.dispatch(ContestActions.clean());
this.subscriptions.unsubscribe();
}
public onWinner(winnerId: number): void {
const post: Post = new Post();
post.title = `The winner of the contest has been elected: ${this.contest.title}`;
post.description = `Tizio has been elected winner of the contest and ${this.user.name} gives him the most welcome compliments`;
post.user = this.user;
post.contest = this.contest;
post.type = Type.WINNER;
post.level = Level.SUCCESS;
this.contest.winner = winnerId;
this.store.dispatch(PostActions.savePost({post}));
this.store.dispatch(ContestActions.saveContest({contest: this.contest}));
}
public onJoin(): void {
this.store.dispatch(UserActions.saveUser({idUser: this.user.id, id: this.contest.id}));
}
public onVote(image: Image): void {
let vote: number = image.vote;
vote = vote + 1;
this.store.dispatch(ImageActions.updateImage({photoId: image.id, votes: vote, userId: this.user.id}));
this.store.dispatch(AuthActions.updatedUser({userId: this.user.id}));
}
public isContain(contestId: number): boolean {
if (this.user.myContest) {
for (const i of this.user.myContest) {
if (contestId === i) {
return true;
}
}
}
return false;
}
public isImageVoted(id: number): boolean {
if (this.user.favouritePhoto) {
for (const imageVoted of this.user.favouritePhoto) {
if (id === imageVoted) {
this.hasVoted = true;
return true;
}
}
return false;
}
}
public onBan(image: Image): void {
this.modalService.show(BanModalComponent, {initialState : image});
this.bsModalRef.hide();
}
public isBan(contestId: number): boolean {
if (this.user.whereBanned) {
for (const contestBanned of this.user.whereBanned) {
if (contestId === contestBanned) {
return true;
}
}
return false;
}
}
public ngAfterViewChecked(): void {
this.cdRef.detectChanges();
}
}
contest contains a reference to an Observable emission, and all references emitted by Observables are readonly. Either clone contest:
this.contest = { ...contest };
Or, better, leave it as an Observable and consume it as such, generally via the async pipe. Also, if you're using NgRx, you want to be using store.select():
this.contest$ = this.store.select(ContestSelectors.getById);
Element implicitly has an 'any' type because index expression is not of type 'number'.
I get this error on the [key] of <string>this.errors[key] || key))
I don't know why, can someone please explain to me why I get this error and provide a solution to it.
export class LinqChipsComponent implements OnInit, DoCheck, ControlValueAccessor {
#Input() label = '';
#Input() placeholder = '';
#Input() options: Options[] = [];
#Input() selectedOptions: Options[] = [];
#Input() floatLabel: FloatLabelType = 'auto';
#Input() hint!: string | undefined;
#Input() public required = false;
#Input()
public disabled = false;
#Input()
public data: string | undefined;
private errorMessages = new Map<string, () => string>();
public onChangeFn = (_: any) => {};
public onTouchedFn = () => {};
#ViewChild('optionInput') optionInput: ElementRef | undefined;
#Output() onRemoved = new EventEmitter<Options>();
#Output() selectedOptionsChanged = new EventEmitter<Options[]>();
formControl = new FormControl('');
filteredOptions: Observable<Options[]> | undefined;
iterableDiffer: IterableDiffer<Options>;
constructor(private readonly iterableDiffers: IterableDiffers, #Self() #Optional() public
control: NgControl) {
this.iterableDiffer = this.iterableDiffers.find([]).create();
this.control && (this.control.valueAccessor = this);
this.errorMessages.set('required', () => `${this.label} is required.`);
}
public get invalid(): boolean | null {
return this.control ? this.control.invalid : false;
}
public get showError(): boolean | null {
if (!this.control) {
return false;
}
const {dirty, touched} = this.control;
return this.invalid ? dirty || touched : false;
}
public get errors(): Array<string> {
if (!this.control) {
return [];
}
const { errors } = this.control;
return Object.keys(this.errors).map((key) => (this.errorMessages.has(key) ?
this.errorMessages.get(key)!() : <string>this.errors[key] || key));
}
public registerOnChange(fn: any): void {
this.onChangeFn = fn;
}
public registerOnTouched(fn: any): void {
this.onTouchedFn = fn;
}
public setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
public writeValue(obj: any): void {
this.data = obj;
}
public onChange() {
this.onChangeFn(this.data);
}
ngDoCheck(): void {
const optionChanges = this.iterableDiffer.diff(this.options);
if (optionChanges) {
this.filteredOptions = of(this.options);
}
}
ngOnInit(): void {
this.subscribeFilterOptions();
}
selected(value: Options): void {
if (this.optionInput) {
this.optionInput.nativeElement.value = '';
}
if (!this.selectedOptions.find((x) => x.text === value.text)) {
this.selectedOptions.push(value);
this.selectedOptionsChanged.emit(this.selectedOptions);
}
}
private subscribeFilterOptions() {
this.filteredOptions = this.formControl.valueChanges.pipe(
startWith(''),
map((value: string | Options) =>
value && typeof value === 'string' ? this.options.filter((o) =>
o.text.toLowerCase().includes(value.toLowerCase())) : this.options.slice()
)
);
}
}
I have a class with two private methods:
export class LayerEditor {
public layerManager: LayerManager;
constructor() {
this.layerManager = new LayerManager(this);
}
private executeCommand() {
switch (this.layerManager.editableLayer.type) {
case LayerType.common:
this.commandManager.set(new SelectExistCommand(this.reonMap));
break;
case LayerType.point:
this.commandManager.set(new SelectExistPointCommand(this.reonMap));
break;
}
}
private createTools(): EditorTools {
switch (this.layerManager.editableLayer.type) {
case LayerType.common:
return new CommonTools(new CommonToolsFactory());
case LayerType.point:
return new PointTools(new PointToolsFactory());
}
}
}
How to avoid two switches no joining two methods in one switch? How to apply flexibility of OOP?
Class LayerManager manages a layers:
class LayerManager {
public selectedLayer: TreeNode;
public editableLayer: Layer;
public polygonsArea: string;
private readonly layers: TreeNode[];
constructor(private layerEditor: LayerEditor) {
this.layers = this.layerEditor.r.layersManager.editableLayers;
}
getLayers() {
return this.layers;
}
selectLayer(layerId: string) {
this.selectedLayer = { ...this.layerEditor.r.layersManager.getLayerConfig(layerId), enabled: true };
this.layerEditor.r.state.changeTreeNode(this.selectedLayer);
}
createLayerObject() {
this.editableLayer = FactoryLayer.create(this.selectedLayer.id, this.selectedLayer.type, this.layerEditor.r);
}
}
Command manager class makes works with commands. This class works as dispatcher and stores all commands.
class CommandManager {
public currentCommand: Command;
protected commands: Command[] = [];
protected undoCommand: Command;
constructor(public layerEditor: LayerEditor) {}
set(command: Command): void {
if (command === this.currentCommand) return;
this.currentCommand = command;
this.commands.push(this.currentCommand);
}
execute(): void {
this.currentCommand.execute();
}
undo(): void {
this.currentCommand.undo();
}
redo(): void {
this.currentCommand.redo();
}
cancel(): void {
while (this.commands.length) {
this.currentCommand = this.commands.pop();
this.undo();
}
}
complete(): void {
this.currentCommand.complete();
}
}
If you need anything else I will post
Essentially what you want to do is move all split decisions into one place. Instead of having many pairs of classes like PointTools, PointToolsFactory, SelectExistPointCommand, etc. you have one single PointConfig. You want your two configurations to match the same interface as much as possible. They both create a command and some tools. Other classes should be able to use the returned tools interchangeably without caring if it is PointTools or CommonTools. That way you can just call this.config.createTools().
This is the Factory Method Pattern and the C# Example should be readable as it's extremely similar to TypeScript. I've seen more of your code than what is posted here so I know that you were trying to do this already with a class FactoryLayer. But you want to define it so that the factory is the only switch that you need.
It should be something roughly along these lines:
interface LayerConfig {
id: string;
type: SemanticLayerType;
fields: Record<string, FieldConfig>;
createTools(): Button[];
createSelectCommand(map: ReonMap): Command;
}
class PointConfig implements LayerConfig {
readonly type = SemanticLayerType.Point;
readonly fields = {}; // fill this in
constructor(public readonly id: string) {}
createTools() {
return []; // fill this in
}
createSelectCommand(map: ReonMap): Command {
// fill this in
}
}
class PolygonConfig implements LayerConfig {
readonly type = SemanticLayerType.Polygon;
readonly fields = {}; // fill this in
constructor(public readonly id: string) {}
createTools() {
return []; // fill this in
}
createSelectCommand(map: ReonMap): Command {
// fill this in
}
}
class Layer {
tools: Button[];
constructor(public readonly config: LayerConfig) {
this.tools = config.createTools();
}
// other stuff
}
export class FactoryLayer {
static create(layerId: string, layerType: LayerType): Layer {
if (layerType === LayerType.common) {
return new Layer(new PolygonConfig(layerId));
}
if (layerType === LayerType.point) {
return new Layer(new PointConfig(layerId));
}
throw new Error("Unknown Layer Type " + layerType);
}
}
export class LayerEditor {
public layerManager: LayerManager;
constructor(private map: ReonMap, private commandManager: CommandManager) {
this.layerManager = new LayerManager(map); //this);
}
private getCurrentLayer(): Layer {
if (!this.layerManager.editableLayer) {
throw new Error("No Layer Selected");
}
return this.layerManager.editableLayer;
}
private executeCommand(): void {
// you could probably clean this up but you get the idea
this.commandManager.execute(
this.getCurrentLayer().config.createSelectCommand(this.map)
);
}
public getTools(): Button[] {
return this.getCurrentLayer().tools;
}
}
I have just tried to bring common things together. And trying to leverage Typescript features.
In both the private method you have this.layerManager.editableLayer.type common, now by adding an optional parameter and making use of returning multiple datatype. Below is the updated code.
If need you can default parameter as type = 'execute' and whenever needed pass parameter as create
private checkTool( type?: string): EditorTools | void {
const layerManagerType = this.layerManager.editableLayer.type
switch(layerManagerType) {
case LayerType.common:
return (type === 'execute') ?
this.commandManager.set(new SelectExistCommand(this.reonMap))
: new CommonTools(new CommonToolsFactory());
break;
case LayerType.point:
return ( type === 'execute') ?
this.commandManager.set(new SelectExistPointCommand(this.reonMap))
: new PointTools(new PointToolsFactory());
}
}
I am having an issue when I try to get a specified user from Firebase, Firestore.
export class TaskService {
tasksCollection: AngularFirestoreCollection<Task>;
taskDoc: AngularFirestoreDocument<Task>;
tasks: Observable<Task[]>;
task: Observable<Task>;
constructor(private afs: AngularFirestore) {
this.tasksCollection = this.afs.collection('tasks', ref => ref.orderBy('title', 'asc'));
}
getTask(id: string): Observable<Task> {
this.taskDoc = this.afs.doc<Task>(`clients/${id}`);
this.task = this.taskDoc.snapshotChanges().pipe(map(action => {
if (action.payload.exists === false) {
return null;
} else {
const data = action.payload.data() as Task;
data.id = action.payload.id;
return data;
}
}));
return this.task;
}
}
And this is my Component.ts file
export class TaskDetailsComponent implements OnInit {
id: string;
task: Task;
hasHours = false;
showHoursOnUpdate: false;
constructor(
private taskService: TaskService,
private router: Router,
private route: ActivatedRoute
) { }
ngOnInit() {
// Get id from url
this.id = this.route.snapshot.params.id;
// Get client
this.taskService.getTask(this.id).subscribe(task => {
if (task != null) {
if (task.hours > 0) {
this.hasHours = true;
}
}
this.task = task;
});
console.log(this.id);
console.log(this.task);
}
}
The result for id is good.
But the result for object (task) is undefined.
P.S
I also have functions for getting all the users and adding a new user, so if that's relevant please let me know in the comments
Your line of code
this.id = this.route.snapshot.params.id;
In this case id is not a table column but it's your document id by Firestore
Here an example of firestore
So your Id in this case is the red one and not the blue one.
i need a bit of help.
I am trying to display data from specific loged in user but i have a hard time.
in html :
<div *ngFor="let to of UnData">
TS:
export class something {
todo: AngularFireList<any>;
UnData = [];
userId: string;
constructor(public navCtrl: NavController, public navParams: NavParams, private db: AngularFireDatabase, private fire: AngularFireAuth) {
this.fire.authState.subscribe(user =>{
if(user) this.userId = user.uid
});
if (!this.userId) return;
this.db.list("people/${this.userId}").valueChanges().subscribe(_data => {
this.UnData = _data;
console.log(this.UnData);
});
}
console.log gives me nothing in return. I think i am doing something wrong in code where i am getting data from database. Please give me a bit of help :-)
Thanks Frank,
the first problem was backticks but i still had problems and solved and important part was ngOnInit(userId:string):
export class something {
todo: AngularFireList<any>;
unData = [];
userId: string;
title: string;
desc: string;
constructor(public navCtrl: NavController, public navParams: NavParams, private db: AngularFireDatabase, private fire: AngularFireAuth) {
this.fire.authState.subscribe(user => {
if (user) {
this.userId = user.uid;
console.log(this.userId);
}
});
}
postDatatoDB(title: string, desc: string, userId: string): void {
this.db.list("/tasks/" + this.userId).push({ title: title, desc: desc });
this.title = '';
this.desc = '';
}
**ngOnInit(userId:string)** {
this.db.list<any>(`/tasks/${this.userId}`).valueChanges().subscribe(data => {
this.unData = data;
console.log(this.unData);
});
}
}