How to store data under specific user id? Angular&Firebase - javascript

it's been a while.
My question is how to store data in realtime database (firebase) by current logged in user id, so when I log in from another account, I can't see that data (only my own).
This is how I do it now:
employee.service.ts:
#Injectable({
providedIn: 'root'
})
export class EmployeeService {
userId: string;
constructor(public firebase: AngularFireDatabase, private datePipe: DatePipe, private afu:
AngularFireAuth, public clientService: ClientService, public contractsService: ContractsService,
public maintainerService: MaintainerService) {
this.afu.authState.subscribe(user=>{
if(user) this.userId=user.uid;
})
}
employeeList: AngularFireList<any>;
clientList: AngularFireList<any>;
maintainerList: AngularFireList<any>;
contractList: AngularFireList<any>;
array=[];
form: FormGroup=new FormGroup({
$key: new FormControl(null),
sifra: new FormControl(''),
caseName: new FormControl(''),
department: new FormControl(''),
startDate: new FormControl(new Date()),
startTime: new FormControl(''),
finishTime: new FormControl(''),
isPermanent: new FormControl(false), //nije obavezno
description: new FormControl(''),
remark: new FormControl(''), //nije obavezno
serviceType: new FormControl('1'),
isReplaceable: new FormControl(''),
maintainer: new FormControl(''),
contracts: new FormControl(''),
dnevnica: new FormControl(''),
client: new FormControl('')
});
initializeFormGroup(){
this.form.setValue({
$key: null,
sifra: '',
caseName: '',
department: '',
startDate: '',
startTime: '',
finishTime: '',
isPermanent: false,
description: '',
remark: '',
serviceType: '1',
isReplaceable: '',
maintainer: '',
contracts: '',
dnevnica: '',
client: ''
});
}
getEmployees(){
this.employeeList=this.firebase.list(`employees/${this.userId}`);
return this.employeeList.snapshotChanges();
}
And in my compoent file:
ngOnInit(): void {
this.service.getEmployees().subscribe(
list=>{
let array = list.map(item=>{
let clientName=this.clientService.getClientName(item.payload.val()['client']);
let maintainerName=this.maintainerService.getMaintainerName(item.payload.val()['maintainer']);
return{
$key: item.key,
clientName,
maintainerName,
...item.payload.val()
};
});
this.listData= new MatTableDataSource(array);
this.listData.sort=this.sort;
this.listData.paginator=this.paginator;
this.listData.filterPredicate=(data, filter)=>{
return this.displayColumns.some(ele=>{
return ele != 'actions' && data[ele].toLowerCase().indexOf(filter) != -1;
});
}
});
}
When I login for the first time, everything is good. When I refresh page, all my keep disappearing!
It's pretty strange, since my data is still in my database but if I click back button on my browser and enter my component again, data is there again!
Thanks in advance.

That is because onAuthStatusChanged(), which is what authState proxies, returns a trinary value, not binary.
Since you're using a truthy check to determine if the user is authenticated, you've created a race condition because you're not waiting for the SDK to fully initialize.
constructor(private afu: AngularFireAuth) {
this.afu.authState.subscribe(user=>{
if(user) this.userId=user.uid;
})
}
Since Firebase Auth is asynchronous, the value returned from authState or onAuthStatusChanged can be one of three values:
undefined: The JS SDK has initialized but hasn't checked the user's authentication status yet.
null: The user is unauthenticated.
User Object: The user is authenticated.
What you need to do is wait until authState returns either null or User like this:
enum AuthState {
UNKNOWN,
UNAUTHENTICATED,
AUTHENTICATED
}
// This subject will store the user's current auth state
private _state = new BehaviorSubject<AuthState>(AuthState.UNKNOWN);
constructor(private afu: AngularFireAuth) {
this.afu.authState.subscribe(user=>{
if (typeof user === 'undefined') {
// Do nothing because the user's auth state is unknown
return;
} else if (user === null) {
// User is unauthenticated
this._state.next(AuthState.UNAUTHENTICATED);
} else {
// User is authenticated
this.userId = user.uid;
this._state.next(AuthState.AUTHENTICATED);
}
})
}
// Public method to monitor user's auth state
public state$(): Observable {
return this._state.asObservable();
}
Then in your component you need to subscribe to the state$() observable before calling getEmployees().
ngOnInit(): void {
this.service.state$().subscribe((state: AuthState) => {
// We don't know what the user's auth state is, so exit waiting for an update
if (state === AuthState.UNKNOWN) {
return;
} else if (state === AuthState.UNAUTHENTICATED) {
// User is unauthenticated so redirect to login or whatever
} else {
// User is authenticated, so now we can call getEmployees()
this.service.getEmployees().subscribe(...);
}
});
}

Related

Logged in user can see only his own content (Angular+Firebase)

I have a file named user.service.ts with a following code (I set firebase authentication here):
export class UserService {
uid = this.afAuth.authState.pipe(
map((authState) => {
if (!authState) {
return null;
} else {
return authState.uid;
}
})
);
isAdmin: Observable<boolean>= this.uid.pipe(
switchMap(uid=>{
if(!uid){
return observableOf(false);
} else{
return this.firebase.object<boolean>('/admin/'+ uid).valueChanges()
}
})
);
constructor(private afAuth: AngularFireAuth, private firebase: AngularFireDatabase) {}
login() {
this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider());
}
logout() {
this.afAuth.auth.signOut();
}
I have a file named employee.service.ts, and this is how I insert employees in firebase:
insertEmployee(employee) {
this.employeeList.push({
caseId: employee.caseId,
caseName: employee.caseName,
department: employee.department,
startDate: employee.startDate==""?"":this.datePipe.transform(employee.startDate, 'dd.MM.yyyy'),
startTime: employee.startTime,
finishTime: employee.finishTime,
isPermanent: employee.isPermanent,
description: employee.description,
remark: employee.remark,
serviceType: employee.serviceType,
isReplaceable: employee.isReplaceable,
maintainer: employee.maintainer,
contracts: employee.contracts,
dnevnica: employee.dnevnica,
client: employee.client
});
}
In the same file, I get employees like:
getEmployees(){
this.employeeList=this.firebase.list('employees');
return this.employeeList.snapshotChanges();
}
My question is: How I can connect this insert with client id? To be more concrete, I want to make every user see only his content, how can I do that?

How do I properly update entity relationships in Typeorm

I have a basic User table in which they can have proficiencies in various musical Instruments. I'm having trouble figuring out the correct way to make a basic updateUser function in which they can update their User information, as well as their instrument proficiencies using Typeorm and a MySQL database.
User Class
#Entity()
#ObjectType()
export class User extends BaseEntity {
public constructor(init?:Partial<User>) {
super();
Object.assign(this, init);
}
#Index({ unique: true})
#Column()
email: string;
#Index({ unique: true})
#Column()
username: string;
#Column()
#HideField()
password: string;
#Column({
nullable: true
})
profilePicture?: string;
#Index({ unique: true})
#Column()
phoneNumber: string;
//Lazy loading
#OneToMany(() => UserInstrument, p => p.user, {
cascade: true
})
instruments?: Promise<UserInstrument[]>;
}
User Instrument Class
#Entity()
#ObjectType()
export class UserInstrument extends BaseEntity {
public constructor(init?:Partial<UserInstrument | InstrumentProficiencyInput>) {
super();
Object.assign(this, init);
}
#Column({
type: 'enum',
enum: Instrument
})
instrument : Instrument
#Column()
proficiency : number;
#JoinColumn()
#HideField()
userId : number;
#ManyToOne(() => User, p => p.instruments)
#HideField()
user : User;
}
Now creating a new user with predefined instruments hasn't been an issue. I can simply insert a new User and it will autofill the Id and UserId fields for the appropriate tables.
async create(request: RegisterUserInput) : Promise<boolean>{
const user = new User();
user.username = request.username;
user.password = request.password;
user.phoneNumber = request.phoneNumber;
user.email = request.email;
user.instruments = Promise.resolve(request.instruments?.map(p => new UserInstrument(p)));
const result = await this.usersRepository.save(user);
}
The issue
Now whenever I do something similar to Update the User/Instrument tables I get a "ER_BAD_NULL_ERROR: Column 'userId' cannot be null" exception
async updateUser(request: UpdateUserInput, id : number): Promise<boolean> {
var user = new User(classToPlain(request));
user.id = id;
user.instruments = Promise.resolve(request.instruments?.map(p => new UserInstrument(p)));
(await user.instruments)?.forEach(p => p.userId = id);
await this.usersRepository.save(user);
return true;
}
This code generates the following exception
code:'ER_BAD_NULL_ERROR'
errno:1048
index:0
message:'ER_BAD_NULL_ERROR: Column 'userId' cannot be null'
name:'QueryFailedError'
parameters:(2) [null, 8]
query:'UPDATE `user_instrument` SET `userId` = ? WHERE `id` = ?'
sql:'UPDATE `user_instrument` SET `userId` = NULL WHERE `id` = 8'
sqlMessage:'Column 'userId' cannot be null'
sqlState:'23000'
stack:'QueryFailedError: ER_BAD_NULL_ERROR: Column 'userId' cannot be null
Even though when I inspect my user object I can see the userId field is set correctly
__has_instruments__:true
__instruments__:(1) [UserInstrument]
0:UserInstrument {created: '2020-11-29 02:46:10', instrument: 'Guitar', proficiency: 5, userId: 30}
length:1
So what am I doing wrong? Is there a more preferred way to update the users Instrument table, without accessing the Instrument repository directly? I'm not sure why the initial save on my create method works, but not on update.

Angular 8 /Firebase: how do I set the displayName when creating user with email and password?

After reading so many posts and empty(or not?) solutions, I figured that the best thing was to post of my own. So my goal is just to get the displayName of the user so in a list of posts or whatever, the authorship doesnt look ugly with a uid...
Here's what I find relevant for the problem:
Signup reactive form, later called on onInit:
createForm() {
this.registerForm = this.fb.group(
{
name: ['', Validators.required],
email: ["", [Validators.required]],
password: [null, [Validators.required, Validators.minLength(5)]],
retype: ["", Validators.required]
},
{
validator: PasswordValidators.passwordsShouldMatch
}
);
}
The submit method:
onSubmit() {
if (this.registerForm.invalid) return;
this.registered = true
this.authService.registerUser(this.email.value, this.password.value).then(data=>{
console.log(data)
})
}
The authService calling the uid and the register method:
constructor( private afAuth: AngularFireAuth, private router: Router) {
this.afAuth.authState.subscribe(user=>{
this.userId = user.uid;
})
}
registerUser(email:string, password:string) {
return this.afAuth.auth.createUserWithEmailAndPassword(email, password)
.then((result) => {
this.router.navigate(['/login']);
console.log(result.user)
}).catch((error) => {
window.alert(error.message)
})
}
When logging this line this.afAuth.authState.subscribe(user=>{
this.userId = user.uid;, actually logging the user, i can see the whole object and the displayName, but since the register method only accepts 2 arguments, email and pass, how to I workaround this? I've tried an uproach dealing with updateprofile but I got stuck on an error...Is there any solution? Thank you
If you wan to set the display name of a user account, you won't be able to do that at the time of account creation using createUserWithEmailAndPassword. You'll have to perform a followup call to updateProfile() on the user object you get back.
result.user.updateProfile({
displayName: ...
})
.then(...)
.catch(...)
You will obviously have to pass along the name in the call to registerUser().

Angular - Re-Populate form inputs from service

I have a basic angular component that allows some one to edit the details of a user once they go to their profile and click on "edit".
Component:
export class EditUserComponent implements OnInit {
// Define our vars
user: Users[];
editUserForm: FormGroup;
message: {};
displayMessage = false;
userID: number;
errorMessage: any = '';
constructor(
private fb: FormBuilder,
private _userService: UserService,
private activatedRoute: ActivatedRoute
) {
}
ngOnInit(): void {
// Get the userID from the activated route
this.activatedRoute.params.subscribe((params: Params) => {
this.userID = params['id'];
});
// Call our service and pass the UserID
this._userService.getUser(this.userID)
.then(res => {
this.user = res;
this.createForm();
});
}
// Generate the form
createForm() {
this.editUserForm = this.fb.group({
QID: ['', Validators.required],
favoriteColor: [''],
favoriteNumber: [''],
favoriteActor: ['']
});
}
}
Service:
// Fetch a single user
getUser(userID: number) {
return this._http.post(this.baseUrl + '/fetchUser', { "userID": userID }, { "headers": this.headers })
.toPromise()
.then(res => res.json())
.catch(err => { this.handleError(err); });
}
Interface:
export interface Users {
RecordID?: number;
QID: string;
favoriteColor?: string;
favoriteNumber?: number;
favoriteActor?: string;
}
I am trying to pass the values to my formGroup but I am having trouble figuring out how to access the values.
I assumed I could do something like this where I could access the user model and select a property from it but that is throwing an undefined error.
Would I pass the values here in the form group or bind them to the elements directly somehow? I am receiving the data back from the service just fine, just not sure how to get each of the values back to their respective fields.
createForm() {
this.editUserForm = this.fb.group({
QID: [this.user.QID, Validators.required],
favoriteColor: [''],
favoriteNumber: [''],
favoriteActor: ['']
});
}
If I understand correctly ... this is what my code looks like:
onProductRetrieved(product: IProduct): void {
if (this.productForm) {
this.productForm.reset();
}
this.product = product;
// Update the data on the form
this.productForm.patchValue({
productName: this.product.productName,
productCode: this.product.productCode,
starRating: this.product.starRating,
description: this.product.description
});
this.productForm.setControl('tags', this.fb.array(this.product.tags || []));
}
I'm using patchValue for the values and setControl for the array.
OR
Since you are creating the form after retrieving the data, you could do something like this:
createForm() {
this.editUserForm = this.fb.group({
QID: [this.user.QID, Validators.required],
favoriteColor: [this.user.favoriteColor],
favoriteNumber: [this.user.favoriteNumber],
favoriteActor: [this.user.favoriteActor]
});
}
AND just to be complete ... each input element needs a formControlName property like this:
<input class="form-control"
id="productNameId"
type="text"
placeholder="Name (required)"
formControlName="productName" />
<span class="help-block" *ngIf="displayMessage.productName">
{{displayMessage.productName}}
</span>
</div>
You can find a complete working example here: https://github.com/DeborahK/Angular2-ReactiveForms
Bind a submit event to your form, then use this.editUserForm.value to access the data from the form.
In the component template:
<form [formGroup]="editUserForm" (submit)="saveIt()">
In the Component:
saveIt() {
if (this.editUserForm.dirty && this.editUserForm.valid) {
alert(`Number: ${this.editUserForm.value.favoriteNumber} Actor: ${this.editUserForm.value.favoriteActor}`);
}
}

Nativescript + Angular 2 chatService

i have a trouble.
here i let my code.
https://gist.github.com/anonymous/b651408a8419f13a949d719e6b87d8ea
in my app i connect to the firebase cloud message service, in the appComponent i listen the messages that firebise send and emit the data content whit the DataInterchage.service, in the chatComponent i suscribe to the event emited and i process the data.
the problem is the next. when I receved the data, i set the this.messeges variable the data content but the view dont update.
what do you believe that be?
when you set this.messages your code might be running outside the angular because that code is written in service callback. that is why when you assign values to variable it doesn't update the view.
try running code inside the angular NgZone. after that your view will be updated successfully.
for your code snippet will be
import {NgZone,ChangeDetectorRef} from "#angular/core";
export class ChatComponent implements OnInit{
constructor(
private zone: NgZone,
private cd: ChangeDetectorRef,
) {}
ngOnInit() {
this.user = JSON.parse(appStorage.getString("user_info"));
this.me = {
id: this.user.id,
name: this.user.full_name,
pictureUrl: this.user.icon
};
this.other = {
id: "",
name: "",
pictureUrl: "",
coverUrl: ""
};
this.emitter.msgRecived$
.subscribe(data => {
data = JSON.parse(data);
this.http.get(`${ env['api_route'] }/api/users/${ data.user }`)
.subscribe((res: Response) => {
let user = res.json().data;
this.other = {
id: user.id,
name: user.full_name,
pictureUrl: user.icon,
};
this.zone.run(()=>{
this.messages.push({
sender: this.other,
content: data.message,
date: data.date
});
});
console.dump(this.other);
console.dump(this.messages)
}, (err: Response) => {
this.oauth.isLogged(err);
});
});
}
}

Categories

Resources