Kotlin read from room database in fragment - kotlin

I write an application with room database and I have a problem with reading data and use it in the mine thread.
consider tables as those:
Table1
| ID | Name |
| -- | ------ |
| 1 | first |
| 2 | second |
Table2
| ID | Name | description | table1_id|
|--- | ---- | ------------- |----------|
| 1 | test | not important | 2 |
| 2 | test | not important | 1 |
I have no problem to display information about object from table2 since it is passed to the fragment, but when I try to get Name by ID from table1 I am unable to do so. I searched for information, but nothing I've found helped me.
My code:
private var _binding: FragmentXUpdateBinding? = null
private val binding get() = _binding!!
private lateinit var mXViewModel: XViewModel
private lateinit var mXTypeViewModel: XTypeViewModel
private val args by navArgs<XUpdateFragmentArgs>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentXUpdateBinding.inflate(inflater, container, false)
mXViewModel = ViewModelProvider(this)[XViewModel::class.java]
mXTypeViewModel = ViewModelProvider(this)[XTypeViewModel::class.java]
setHasOptionsMenu(true)
val XType = getXTypeName(args.currentX.X_type_id)
binding.XTypeText.text = XType
binding.etXName.setText(args.currentX.name)
binding.etXDescription.setText(args.currentX.description)
binding.buttonUpdateX.setOnClickListener {
updateItem()
findNavController().navigate(my.food.R.id.action_XUpdateFragment_to_XListFragment)
}
return binding.root
}
private fun getXTypeName(id: Int): String {
var XTypeString = ""
mXTypeViewModel.readAllData.observe(viewLifecycleOwner) { type ->
type?.forEach {
if (it.id == id) {
XTypeString = it.name
}
}
}
return XTypeString //it returns "" since database thread is still ongoing
}
Please note the comment in the second from the end line.
What's the solution to my headache?
THANKS!!!!

The problem is that you are trying to retrieve XTypeString before your asynchronous task(readAllData from ViewModel) has completed. You should move the binding of XTypeText inside observe, as below. Note the change in function signature for bindXTypeText().
private fun bindXTypeText(id: Int) {
var XTypeString = ""
mXTypeViewModel.readAllData.observe(viewLifecycleOwner) { type ->
type?.forEach {
if (it.id == id) {
XTypeString = it.name
}
}
binding.XTypeText.text = XTypeString //moved the assignment of XtypeText here.
}
}

Related

How can I avoid a "Primary constructor call expected" in this case?

I've created a Person class and a Student class here in Kotlin:
In line 27, I'm trying to achieve a case where a user can create a "Student" class by providing 4 parameters: FirstName, LastName, Age, and Degree.
I've also written the equivalent code in Java. I'm trying to achieve the Java equivalent code's Secondary Constructor in line 30:
How can I avoid a "Primary constructor call expected" in the Kotlin code?
For your use case you don't even need secondary constructors. You could have optional arguments in the constructor. Like this for example:
open class Person(var firstName: String, var lastName: String, var age: Int? = null) {
override fun toString() = "$firstName | $lastName | $age"
}
class Student(firstName: String, lastName: String, var degree: String, age: Int? = null) : Person(firstName, lastName, age) {
override fun toString() = "$firstName | $lastName | $age | $degree"
}
To demonstrate:
fun main() {
val a = Person("Aaa", "aaA")
val b = Person("Bbb", "bbB", 20)
val c = Student("Ccc", "ccC", "degreeC")
val d = Student("Ddd", "ddD", "degreeD", 21)
println(a)
println(b)
println(c)
println(d)
}
Output:
Aaa | aaA | null
Bbb | bbB | 20
Ccc | ccC | null | degreeC
Ddd | ddD | 21 | degreeD

How to convert set/println to string?

I'm trying to add set to email as text
//Data class
#Entity(tableName = "inventory")
data class InventoryDataClass(
#PrimaryKey(autoGenerate = true)
var id: Int,
#NonNull var itemNumber: String,
#NonNull val itemDescription: String,
val currentInventory: Int?,
#NonNull val optimalInventory: Int,
#NonNull val minInventory: Int
)
// getting list from room
val inventoryList by mainViewModel.getInventoryItems.collectAsState(initial = emptyList())
//converting list to set
val emailNumber = inventoryList.map { item ->
println("Item Number: ${item.itemNumber} | Item Description: ${item.itemDescription} | Current Inventory: ${item.currentInventory.toString()}")}.toSet()
I'm able to get the output I need:
I/System.out: Item Number: 123| Item Description: item1 | Current Inventory: 47
I/System.out: Item Number: 456| Item Description: item2 | Current Inventory: 8
...
How can I get it as a string and add it to my email as text? So far i'm only able to get Kotlin.Unit
Log.d(TAG, "InventoryMainScreen: $emailNumber")
//output
D/MainActivity: InventoryMainScreen: [kotlin.Unit]
You can use joinToString:
val logsString = inventoryList.joinToString(separator = "\n") { item ->
"Item Number: ${item.itemNumber} | Item Description: ${item.itemDescription} | Current Inventory: ${item.currentInventory.toString()}"
}
You can append to a StringBuilder:
//Output this:
//Item Number: 123| Item Description: item1 | Current Inventory: 47
//Item Number: 123| Item Description: item1 | Current Inventory: 47
val emailNumber = StringBuilder()
inventoryList.forEach { item ->
emailNumber.append("Item Number: ${item.itemNumber} | Item Description: ${item.itemDescription} | Current Inventory: ${item.currentInventory}").append("\n")
}
emailNumber.removeSuffix("\n")

Different Property setter for three same class objects in Kotlin

What I am trying to implement are three different temperature values depending on the city name.
The following class:
class City(val name: String) {
var degrees: Int = 0
set(value) {
when(this.name){
("Dubai") -> 30
"Moscow" -> 5
"Hanoi" -> 20
}
field = value
}}
And main func:
fun main() {
val firstCity = City("Dubai")
val secondCity = City("Moscow")
val thirdCity = City("Hanoi")
println(firstCity.degrees) // 0
}
Why is it set to default value 0? For Dubai it should have been 30.
The degrees are initialized with 0 and never changed due to no invocation of the setter, which lacks a value for cities that are not expected (maybe that's why you initialized the degrees?).
You could do what you want way shorter:
class City(val name: String) {
var degrees: Int = when(name) {
"Dubai" -> 30
"Moscow" -> 5
"Hanoi" -> 20
else -> 0 // default value for unpredictable cities
}
}
fun main() {
val firstCity = City("Dubai")
val secondCity = City("Moscow")
val thirdCity = City("Hanoi")
println(firstCity.degrees)
}
This will output 30

type-safe builder example kotlin

I want to have following person object in Kotlin :
var p = person {
age = 22
gender = "male"
name {
first = "Ali"
last = "Rezaei"
}
}
I have following code to build it :
data class Person(var age: Int? = null, var gender: String? = null
, var name : Name? = null) {
}
fun name(init: Name.() -> Unit): Name {
val n = Name()
n.init()
return n
}
data class Name(var first: String? = null, var last : String? = null)
fun person(init: Person.() -> Unit): Person {
val p = Person()
p.init()
return p
}
But when I print it, the result is following :
Person(age=22, gender="male", name=null)
What is wrong with my code?
You could make name an extension function on Person that assigns the Name to the Person instead of returning it:
fun Person.name(init: Name.() -> Unit) {
val n = Name()
n.init()
this.name = n
}
You could even consider a more concise syntax for the same, like this:
fun Person.name(init: Name.() -> Unit) {
this.name = Name().apply(init)
}
Shameless plug for my repository discussing DSL design and containing examples.
You need to assign to name. This ended up working for me...
var p = person {
age = 22
gender = "male"
name = name {
first = "Ali"
last = "Rezaei"
}
}

Access to external value inside apply

Short example:
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply{ someName = someName }
// other stuff below
}
SomeOther will apply the value of its own someName to someName, so value application makes no difference (x=x).
Q: How can I access external someName ("want this value") being inside apply?
UPDATE
I have further doubts related to suggestion to use this.someName=someName, below 2 code snippets, the first one works as expected, surprisingly the second fails with similar behavior as described.
First
fun main(args: Array<String>) {
class SomeOther {
var someName: String? = null
}
val someName = "want this value"
print(SomeOther().apply { this.someName = someName }.someName) // works!
}
Second
class SomeOther {
var someName: String? = null
}
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply { this.someName = someName }
fun go() = print(someOther.someName)
}
fun main(args: Array<String>) = MyClass().go() // prints null
Q: Where's the difference?
You could use the also-function instead. It is equivalent to apply, except it will bind your object to it instead of this:
val someName = "want this value"
val someOther = SomeOther().also { it.someName = someName }
The also-function was added to Kotlin 1.1 specifially for when you do not want to shadow this from the outer scope.
using this reference expression as below:
val someOther = SomeOther().apply { someName = this#MyClass.someName }
// reference to the outer class ---^
AND the T.apply function is a convenient way to applying Builder Design Pattern, in this way you never need using this or additional parameter at all, for example:
val foo = Foo().apply {
//v--- no need using `this` or any addition parameters
foo = "bar"
fuzz = "buzz"
}
class Foo {
lateinit var foo: String;
lateinit var fuzz: String
}
Edit
you can assuming apply(lambda) which will apply an anonymous class of Function2<T,ARG,T> instance, then you know why immediately?
in your first approach it looks like as below:
val lambda: Function2<SomeOther, String, SomeOther> = { thisRef, arg ->
thisRef.someName = arg;
// ^--- parameter is used in lambda
thisRef
}
val someName = lambda(SomeOther(), "want this value").someName
println(someName)
in your second approach it looks like as below:
class MyClass {
val lambda: Function2<SomeOther, MyClass, SomeOther> = { thisRef, arg ->
// the parameter `arg` is never used in lambda ---^
thisRef.someName = thisRef.someName
// ^--- it use thisRef's someName rather than arg's
thisRef
}
val someOther = lambda(SomeOther(), this)
}
You can access out of apply like that
class SomeOther {
var someName: String? = null
}
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply { this.someName = this#SomeOther.someName }
fun go() = print(someOther.someName)
}
Try this:
val someName = "want this value"
val otherName = SomeOther().apply { this.someName = someName }
// internal someName ---^ ^
// external someName ---^
print(otherName.someName) // >>> want this name