So I have a movie app. If I liked the movie I'm gonna save its id to firebase. And my goal is to get all movie_id's and display them on screen.
This is the saving code.
firebaseDb.getReference("Users").child(uid).child("movie")
.push().child("movie_id")
.setValue(args.movie.id)
And this is the firebase display
So how to get all movie_id's? I tried this but nothing happened.
firebaseDb.getReference("Users").child(uid).child("movie").addValueEventListener(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val map: Map<String, Any> = snapshot.getValue() as Map<String, Any>
val mapSize = map.keys.size
for (i in 1..mapSize) {
val keys = map.filterKeys { it == map.keys.toTypedArray()[i - 1] }
}
}
If want to create a list of movie IDs, then use the following lines of code:
val uid = FirebaseAuth.getInstance().currentUser?.uid
val db = FirebaseDatabase.getInstance().reference
val movieRef = db.child("Users").child(uid).child("movie")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val movieIds = mutableListOf<String>()
for (ds in dataSnapshot.children) {
val movieId = ds.child("movie_id").getValue(String::class.java)
movieIds.add(movieId)
}
Log.d("TAG", movieIds.size.toString())
}
override fun onCancelled(error: DatabaseError) {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
movieRef.addListenerForSingleValueEvent(valueEventListener)
The result in the logcat will be:
2
As I only see two movies in the screenshot.
Related
I am looking for a way to populate my RecyclerView. I know how to populate a RecyclerView from a single node of my database but I wonder how can I get data from multiple nodes. In my app the user can select multiple hobbies. And I want to populate a recycler view depending which hobbies the user has selected.
Here is a picture of my user node from my database
As you can see my user has selected the hobbies Caffe's,Camping,Hiking
I have a node called Locations and there a six child nodes Caffe's,Camping,Hiking,Museums,Night life and restaurants. Here is my Locations node
I get users hobbies from this code block
val currentuid = FirebaseAuth.getInstance().currentUser!!.uid
val getCurrentUserHobbies =
FirebaseDatabase.getInstance().getReference("users/$currentuid/hobbies")
getCurrentUserHobbies.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val value = snapshot.getValue(String::class.java)
val valueAsString = value.toString()
if (valueAsString.contains("Hiking")){
}
}
override fun onCancelled(error: DatabaseError) {
}
})
If I want to get my recycler view to show all Hiking locations I do this:
val adapter = GroupAdapter<GroupieViewHolder>()
val hikingRef = FirebaseDatabase.getInstance().getReference("Locations/Hiking")
hikingRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (snap in snapshot.children) {
val recommendItems = snap.getValue(RecommendationsClass::class.java)
if (recommendItems != null) {
adapter.add(RecommendationsAdapter(recommendItems))
}
}
recommendationsRecyclerView.adapter = adapter
}
override fun onCancelled(error: DatabaseError) {
}
})
Now if the user has selected hiking and camping for example. I want my recycler view to show all locations from nodes Locations/Hiking and Locations/Camping. I tried multiple but nothing works. I am not going to write them here because it will take too much space. I am trying to keep my question as simple as possible.
Thanks in advance!
I unfortunately could not find any possible solution how to make it work. I had to do something different. What I did I copied sub nodes from my Locations node and put them in users/currentuid/Recommendations depending which hobbies my current user has.
Here is the code snipped that does that:
val currentuid = FirebaseAuth.getInstance().currentUser!!.uid
val getCurrentUserHobbies = FirebaseDatabase.getInstance().getReference("users/$currentuid/hobbies")
getCurrentUserHobbies.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val value = snapshot.getValue(String::class.java)
val valueAsString = value.toString()
if (valueAsString.contains("Caffe's")) {
val ref = FirebaseDatabase.getInstance().getReference("Locations/Caffe's/")
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (snap in dataSnapshot.children) {
val data = dataSnapshot.value
val newData = mapOf("Recommendations/${dataSnapshot.key}" to data)
FirebaseDatabase.getInstance().getReference("users")
.child(currentuid).updateChildren(newData)
}
}
override fun onCancelled(databaseError: DatabaseError) {
// Handle error
}
})
}
if (valueAsString.contains("Camping")) {
val ref = FirebaseDatabase.getInstance().getReference("Locations/Camping/")
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (snap in dataSnapshot.children) {
val data = dataSnapshot.value
val newData = mapOf("Recommendations/${dataSnapshot.key}" to data)
FirebaseDatabase.getInstance().getReference("users")
.child(currentuid).updateChildren(newData)
}
}
override fun onCancelled(databaseError: DatabaseError) {
// Handle error
}
})
}
if (valueAsString.contains("Hiking")) {
val ref = FirebaseDatabase.getInstance().getReference("Locations/Hiking/")
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (snap in dataSnapshot.children) {
val data = dataSnapshot.value
val newData = mapOf("Recommendations/${dataSnapshot.key}" to data)
FirebaseDatabase.getInstance().getReference("users")
.child(currentuid).updateChildren(newData)
}
}
override fun onCancelled(databaseError: DatabaseError) {
// Handle error
}
})
}
}
override fun onCancelled(error: DatabaseError) {
}
})
What the code above does it checks if caffe's camping and hiking exists in my users/currentuid/hobbies node. If it does it copies the data from Locations and puts it in users/currentuid/Recommendations.
Note: I also have to check for remaining three hobbies. I only put three of them for testing purposes.
Here is also an image to show what I did
After this I only looped through my Recommendations node and added all the locations to my recycler view with this code snippet:
val recommendedLocations = FirebaseDatabase.getInstance().getReference("users/$currentuid/Recommendations")
recommendedLocations.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (parentSnapshot in snapshot.children) {
for (childSnapshot in parentSnapshot.children) {
val recommendItems = childSnapshot.getValue(RecommendationsClass::class.java)
if (recommendItems != null) {
adapter.add(RecommendationsAdapter(recommendItems))
}
}
}
recommendationsRecyclerView.adapter = adapter
}
override fun onCancelled(error: DatabaseError) {
}
})
I want to update the Name-based from User Input NIK then show the data to the field and by pressing the update button it will update the data inside the Database.
I'm using this Constructor Class :
import com.google.firebase.database.Exclude
import com.google.firebase.database.IgnoreExtraProperties
#IgnoreExtraProperties
class DatabaseModelUpdate(
var nama : String = ""
) {
#Exclude
fun toMap(): Map<String, Any?> {
return mapOf(
"nama" to this.nama
)
}
}
and my source code is :
val nama = findViewById<EditText>(R.id.editNama).text.toString().trim()
val model = DatabaseModelUpdate(nama)
val post = model.toMap()
var query = reference.orderByKey().equalTo(NIK)
query.addListenerForSingleValueEvent(object : ValueEventListener {
#SuppressLint("SetTextI18n")
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
for (userSnapshot in dataSnapshot.children) {
var keys = userSnapshot.ref.key.toString()
val updates = hashMapOf<String, Any>(
"/Users/$keys" to post
)
userSnapshot.ref.updateChildren(updates)
}
}
My JSON :
But, after I pressed the Update Button nothing change.
UPDATE
Codes:
private fun updating() {
val NIK = findViewById<EditText>(R.id.inputNik).text.toString().trim()
val nama = findViewById<EditText>(R.id.editNama).text.toString().trim()
var query = reference.orderByChild("nik").equalTo(NIK)
query.addListenerForSingleValueEvent(object : ValueEventListener {
#SuppressLint("SetTextI18n")
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
for (userSnapshot in dataSnapshot.children) {
var keys = userSnapshot.ref.key.toString()
val updates = hashMapOf<String, Any?>(
"Users/$keys/nama" to nama
)
dataSnapshot.ref.updateChildren(updates)
Database :
The codes just making a new nodes instead updating the data
UPDATES 2
using
"nama" to nama
database :
To update the "nama" property of the object which has the key equal to "NIK", you can directly point to that particular property and set the new name as shown in the following lines of code:
val query = reference.orderByKey().equalTo(NIK)
val listener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
ds.child("nama").getRef().setValue("newNama")
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
query.addListenerForSingleValueEvent(listener)
So there is no need at all to create any additional classes for a simple update.
Edit:
You might also consider trying this:
for (userSnapshot in dataSnapshot.children) {
val updates = hashMapOf<String, Any>(
"nama" to "newNama"
)
userSnapshot.ref.updateChildren(updates)
}
How to get data from firebase?
I can implement some data on it, but I do now know why I cannot get and put it in my activity, application.
This is my ValuEventListener, should I use here Livedata?
class FirebaseDB : LiveData<List<Shopping>>() {
var fbItemCount:Long = 0
private val firebaseDB2: FirebaseDatabase = FirebaseDatabase.getInstance()
private val userID = FirebaseAuth.getInstance().uid
public val ref = firebaseDB2.getReference("user/"+userID.toString())
fun removeAll() {
}
fun delete(shopping: Shopping){
}
fun modify(shopping: Shopping) {
}
fun add(shopping: Shopping) {
ref.child(shopping.id.toString()).setValue(shopping)
}
fun getShopping(): List<Shopping> {
val lista: ArrayList<Shopping> = ArrayList()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
fbItemCount = lista.size.toLong()
return lista
}
}
How can i check if it is downloaded somewhere on my app or I just cannot see it or implement well.
EDIT:
I just added log.v... and it starts working...
However after adding a new element in the list, new element is added with double information from firebase for example:
In database I have:
Orange
Apples
I add:
Bananas
Now in application it is like:
Orange
Apples
Orange
Apples
Bananas
onDataChange is an async callback and does not result in the same thread, so return one liveData and observe it, then in the on data change process the list and post it on the liveData.
fun getShopping(): LiveData<List<Shopping>> {
val lista: ArrayList<Shopping> = ArrayList()
val data=MutableLiveData<List<Shopping>>()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
fbItemCount = lista.size.toLong()
data.postValue(lista)
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
return data
}
About your double items problem this code making it because before making ValueEventListener you created an empty list then after get data from firebase in onDataChange method you add all items that firebase returns then suppose you add a new item by calling 'add' method them onDataChange will be triggered again and giving you again all items with a new one added, but you never remove the old item and add these new items again to this list. That's why it's happening
val lista: ArrayList<Shopping> = ArrayList()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
Correct code would be like this:
var lista = emptyList<Shopping>()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
lista = emptyList()
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
I have a target on my recyclerView
When I move on to this task, I call the updateTarget() method:
private fun updateTarget(name: String, description: String) {
val map = mapOf("name" to name, "description" to description)
databaseReference?.child("targets")?.child("users")
?.child(uid.toString())?.child("targets")?.child(targetGuid)?.updateChildren(map)
}
But after I change the name with the description, and click update,
it does not change the existing target but adds a new one without the guid field.
And it turns out the following structrure:
-LmjBTkVXhmXaqL0b4RG here redundant.
To update the value of your name property, please use the following lines of code:
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val rootRef = FirebaseDatabase.getInstance().reference
val targetsRef = rootRef!!.child("targets").child("users").child(uid).child("targets")
val query = targetsRef.orderByChild("guid").equalTo("-LmjBTkVXhmXaqL0b4RG")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
ds.child("name").getRef().setValue("The New Vadim Morozov")
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
query.addListenerForSingleValueEvent(valueEventListener)
I display a list of my targets in fragmentA, when I click on one of them, I pass the guid of this target to the fragmentB
After that, I try in the fragmentB display the data of this target for this guid:
private fun fetchTarget(guid: String) {
val uid = firebaseUser!!.uid
// Attach a listener to read the data at the target id
databaseReference?.child("targets")?.child("users")
?.child(uid)?.child("targets")?.child(guid)?.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val data = dataSnapshot.value as? HashMap<String, String>?
val name = data?.get("name") ?: ""
val description = data?.get("description") ?: ""
if (name.isEmpty()) Log.d("some", "nameIsEmpty")
else updateViewsContent(name = name, description = description)
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", databaseError.message)
}
})
}
Here I get the guid: -LmfEVnwgqCUqt7beHDg
And in my console i have next structure:
Unfortunately I can't display data of target, though like all the chains I installed
Q: How i can download -LmfEVnx-y7c3oh8_U9F ?
To display the data that belongs to a single guid, you should use a query and then iterate through the DataSnapshot object like in the following lines of code:
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val rootRef = FirebaseDatabase.getInstance().reference
val targetsRef = rootRef!!.child("targets").child("users").child(uid).child("targets")
val query = targetsRef.orderByChild("guid").equalTo("-LmfEVnwgqCUqt7beHDg")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val target = ds.getValue(Target::class.java)
Log.d(TAG, target.name)
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
query.addListenerForSingleValueEvent(valueEventListener)
The result in the logcat will be:
fgg
You are not getting any data because to get above data your guid value in query should be "-LmfEVnx-y7c3oh8_U9F" but you are passing "-LmfEVnwgqCUqt7beHDg".
You can try below query to get above data:
val uid = firebaseUser!!.uid
// Attach a listener to read the data at the target id
databaseReference?.child("targets")?.child("users")?.child(uid)?.child("targets")?.orderByChild("guid").equalTo(guid)?.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val data = dataSnapshot.value as? HashMap<String, String>?
val name = data?.get("name") ?: ""
val description = data?.get("description") ?: ""
if (name.isEmpty()) Log.d("some", "nameIsEmpty")
else updateViewsContent(name = name, description = description)
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", databaseError.message)
}
})