I have a feature in my program where I have to enter the user's details such as the user's age, height, and weight. This data will be stored in Firestore.
this is how the user will input their details
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.material.button.MaterialButton;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FirebaseFirestore;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class account_information extends AppCompatActivity {
public static final String TAG = "TAG";
EditText et_age, et_height, et_weight;
Button btn_create;
FirebaseFirestore db;
FirebaseAuth mAuth;
String userID;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_account_information);
mAuth = FirebaseAuth.getInstance();
db = FirebaseFirestore.getInstance();
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
btn_create = findViewById(R.id.btn_create);
btn_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String age = et_age.getText().toString().trim();
String height = et_height.getText().toString().trim();
String weight = et_weight.getText().toString().trim();
if(age.isEmpty()){
et_age.setError("Age is mandatory");
et_age.requestFocus();
return;
}
if(height.isEmpty()){
et_height.setError("Height is mandatory");
et_height.requestFocus();
return;
}
if(weight.isEmpty()){
et_weight.setError("Weight is mandatory");
et_weight.requestFocus();
return;
}
userID = mAuth.getCurrentUser().getUid();
DocumentReference documentReference = db.collection("userDetail").document(userID);
Map<String,Object> user = new HashMap<>();
user.put("Age",age);
user.put("Height",height);
user.put("Weight",weight);
user.put("UserId", userID);
documentReference.set(user)
.addOnSuccessListener((OnSuccessListener) (aVoid) -> {
Log.d(TAG, "onSuccess: user Detail is created for " userID);
startActivity(new Intent(account_information.this, MainActivity.class));
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull @NotNull Exception e) {
Log.d(TAG, "onFailure" e.toString());
}
});
}
});
}
}
And this is how it will store in firebase
However, when the user decides to update their profile (for example his weight) the rest of the input will return null at the firebase
like this:
And this is how the user can update their details
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FirebaseFirestore;
import org.jetbrains.annotations.NotNull;
public class UpdateProfile extends AppCompatActivity {
public static final String TAG = "TAG";
EditText et_age, et_height, et_weight;
Button btn_update;
FirebaseAuth mAuth;
FirebaseFirestore mStore;
String userID;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update_profile);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
btn_update = findViewById(R.id.btn_update);
mAuth = FirebaseAuth.getInstance();
mStore = FirebaseFirestore.getInstance();
userID = mAuth.getCurrentUser().getUid();
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String age = et_age.getText().toString().trim();
String height = et_height.getText().toString().trim();
String weight = et_weight.getText().toString().trim();
userID = mAuth.getCurrentUser().getUid();
DocumentReference documentReference = mStore.collection("userDetail").document(userID);
documentReference
.update("Age",age, "Height",height, "Weight",weight)
.addOnSuccessListener((OnSuccessListener) (aVoid) -> {
Log.d(TAG, "onSuccess: user Detail is created for " userID);
startActivity(new Intent(UpdateProfile.this, MainActivity.class));
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull @NotNull Exception e) {
Log.d(TAG, "onFailure" e.toString());
}
});
}
});
}}
Update: I've tried the method of @zen_of_kermit but I got the same problem, maybe there's something in the code that I've missed
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.FirebaseFirestore;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class UpdateProfile extends AppCompatActivity {
public static final String TAG = "TAG";
EditText et_age, et_height, et_weight;
Button btn_update;
FirebaseAuth mAuth;
FirebaseFirestore mStore;
String userID;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update_profile);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
btn_update = findViewById(R.id.btn_update);
mAuth = FirebaseAuth.getInstance();
mStore = FirebaseFirestore.getInstance();
userID = mAuth.getCurrentUser().getUid();
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String age = et_age.getText().toString().trim();
String height = et_height.getText().toString().trim();
String weight = et_weight.getText().toString().trim();
userID = mAuth.getCurrentUser().getUid();
DocumentReference documentReference = mStore.collection("userDetail").document(userID);
Map<String, Object> dataToUpdateMap = new HashMap<>();
if (isValidAndChanged(documentReference, "Age", age)) {
dataToUpdateMap.put("Age", age);
}
if (isValidAndChanged(documentReference, "Height", height)) {
dataToUpdateMap.put("Height", height);
}
if (isValidAndChanged(documentReference, "Weight", weight)) {
dataToUpdateMap.put("Weight", weight);
}
documentReference.update(dataToUpdateMap).addOnSuccessListener((OnSuccessListener) (aVoid) -> {
Log.d(TAG, "onSuccess: user Detail is created for " userID);
startActivity(new Intent(UpdateProfile.this, readProfileData.class));
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull @NotNull Exception e) {
Log.d(TAG, "onFailure" e.toString());
}
});
}
});
}
boolean isValidAndChanged(DocumentReference docRef, String key, Object value) {
return true;
}
}
CodePudding user response:
You are passing all three fields (age, height, weight) without checking if they are valid or contain changes to the existing data. You should make those checks before calling update, similar to how you did in the create function.
For example, create a function to check if new data is valid and different than old data:
boolean isValidAndChanged(DocumentReference docRef, String key, String value) {
// TODO get snapshot of data and compare to new value
}
And then use that to update (passing a Map
to update
instead of var-args):
String age = et_age.getText().toString().trim();
String height = et_height.getText().toString().trim();
String weight = et_weight.getText().toString().trim();
userID = mAuth.getCurrentUser().getUid();
DocumentReference documentReference = mStore.collection("userDetail").document(userID);
Map<String, String> dataToUpdateMap = new HashMap<>();
if (isValidAndChanged(documentReference, "Age", age)) {
dataToUpdateMap.put("Age", age);
}
if (isValidAndChanged(documentReference, "Height", height)) {
dataToUpdateMap.put("Height", height);
}
if (isValidAndChanged(documentReference, "Weight", weight)) {
dataToUpdateMap.put("Weight", weight);
}
documentReference.update(dataToUpdateMap)
// ...
Also, best practice is to use constants instead of passing magic strings around all over your code (e.g. "Age" becomes AGE
.)
CodePudding user response:
You're getting all the data overwritten because you're passing empty strings to the update() method. Firestore can indeed hold null values, but the values of Age
and Height
after the update are not null, but empty strings.
If you want to update a single field and leave the other ones untouched, then you should simply pass to the update() method only values for the fields you need to update. In code, it should look like this:
documentReference.update("Weight", weight).addOnSuccessListener(/* ... /*);