How can I update TableView in ScalaFX? - scala

I have a table view. When I update the properties of one row, I can not see the modifications? For example:
implicit class PersonView(p:Person) {
val fname = new ObjectProperty(this, "fname",p.name)
}
and in my table view
lazy val tableLines = ObservableBuffer(persView)
val personTable = new TableView[PersonView](tableLines) {
columns ++= List(
new TableColumn[PersonView, String] {
text = "Name"
cellValueFactory = _.value.fname
cellFactory = { _ =>
new TableCell[PersonView, String] {
item.onChange { (_, _, newValue) => text = newValue }
}
}
}
)
}
It works fine, but when I update the name, I can not see that in GUI.

Firstly, I'll attempt to summarize what I'm seeing, and how I think you might get this to work:
The PersonView class decorates a Person instance by providing an fname property, that is initialized to the name field of the associated Person. When creating each cell in the "Name" column, you create such a property and associate it with the value of the cell. Henceforth, whenever the value of that property changes, the cell will automatically change its item field to show the new value of that property. (BTW, the onChange property is redundant and unnecessary—it provides an opportunity to perform some other actions when the item property—that is, the bound fname property—changes, so the cell will have already been updated when it executes.)
So, if you now change the name of a Person instance, what happens to the cell for that Person in the "Name" column? Nothing.
Why?
Firstly, as #James_D points out, you have not established a relationship between the name of a Person instance, and the value of the ObjectProperty instance originally associated with it. That is, all you've done is change a String value. For the GUI to be updated, the value of that ObjectProperty needs to change too.
Adding to your problem is the fact that there is no relationship from the Person to its associated PersonView. So, when the Person name field is changed, there's no way for the Person to person to notify its PersonView. Worse, by making PersonView an implicit class, you're suggesting that PersonView instances themselves are unimportant and transient, existing temporarily solely to decorate some Person instance with an additional set of methods and/or properties.
So, how can we change things so that they work as you might expect? There are two basic approaches, and your choice will depend upon how much control you can exert on the Person class. The key in both cases is to ensure that the StringProperty (a better option than an ObjectProperty, incidentally) containing the name of the Person changes whenever the name of the Person is changed...
Firstly, the simplest method is to do away with PersonView class altogether. Clearly, you'll need to be able to edit Person to do this; if you cannot, you'll have to try the second approach. Person should be modified to add an fname property field, with name being converted to a function that reports the current value of fname:
// initName is the initial name of the Person, and may be changed later...
class Person(initName: String, /*Whatever other arguments you require*/) {
// String property storing this Person's name. Name is initialized to initName.
val fname = new StringProperty(this, "fname", initName)
// Report the current name of this Person.
def name = fname.value
// This function is not necessary, since we could change the value through fname directly
// but it does look better...
def name_=(newName: String): Unit = fname.value = newName
}
In this case, your table initialization now looks like this:
val tableLines = ObservableBuffer(persView) // Of Person, not PersonView!
val personTable = new TableView[Person](tableLines) {
columns ++= List(
new TableColumn[Person, String] {
text = "Name"
cellValueFactory = _.value.fname
// No need for a cellFactory - default works fine.
}
)
}
Now, you can change the name of a Person like this:
val someone = new Person("Bob"/*, etc...*/)
someone.name = "Fred"
And all is good. The fname property, the name field and the value of the corresponding cell in the GUI table, will now all have the same value.
The second approach is required if you cannot modify the definition of the Person type. Here, we use PersonView to change the names of Person instances, and hope that no-one changes Person names outside of our control. (That is, if some other code modifies the name of a Person instance without going through PersonView, then we'll know nothing about it, and the GUI will not be updated accordingly.)
PersonView, in this case, must not be an implicit class. We want to retain a PersonView instance and use it to interact with an associated Person instance. PersonView now looks like this:
class PersonView(p: Person) {
// String property initialized to the name of the associated person.
val fname = new StringProperty(this, "fname", p.name)
// Change the name of the person. Note that we MUST also change the name of the
// associated person instance.
def name_=(newName: String): Unit = {
// Change the name of the Person instance. Verify it has the value we think it has.
assert(p.name == fname.value)
p.name = newName // Might be p.setName(newName), etc. in your case
// Change the name of our property.
fname.value = newName
}
}
Now, say you have a list of Person instances, you'll need to map them to PersonView instances, and use those latter instances subsequently.
Your GUI code now looks like this:
val tableLines = ObservableBuffer(persView)
val personTable = new TableView[PersonView](tableLines) {
columns ++= List(
new TableColumn[PersonView, String] {
text = "Name"
cellValueFactory = _.value.fname
// No need for a cellFactory - default works fine.
}
)
}
Changing the names of people is now a little more complex, because we need to be able to find the right PersonView instance, but it would look like this:
val someone = new Person("Bob"/*, etc...*/)
val someoneView = new PersonView(someone)
someoneView.name = "Fred"
And all is good once again. The PersonView.fname property, the Person.name field and the value of the corresponding cell in the GUI table (once someoneView is added to the tableLines observable), will now all have the same value.
However, the following line just changes the name of a Person instance. The PersonView and GUI do not get updated:
someone.name = "Eric"

Related

One instance of a class changing property value but other instance doesn't get updated with the change of property in swift

Here as you can see class SameData there are two instances data1 and data2. If I change data2 instances userName that is stored in the class properties it doesn't get updated in data1 instance of the same class.
In swift class should get updated in all instances when properties change in one instance. But why this is happening?
Code:
class SameData {
var userName = "Anonymous"
}
var data1 = SameData()
var data2 = SameData()
data2.userName = "Not Anonymous"
print(data2.userName)
print(data1.userName)
Result:
"Not Anonymous\n"
"Anonymous\n"
As this is a class and one instance is changing userName then all the instances of the class should adopt the change am I right? So, the result should be something like this.
Expected Result:
"Not Anonymous\n"
"Not Anonymous\n"
in the class properties
These aren't class properties. These are just instance properties, on two different instances. Naturally, changing one doesn't change the other. That's the whole point of instances. They're separate instances. These are just instance properties with default values, similar to writing:
class SameData {
var userName: String
init() {
self.userName = "Anonymous"
}
}
Class properties are marked with class (or similarly, static, which is roughly equivalent to class final).
That said, marking a username as a class constant doesn't really make sense. Presumably, each user should have their own username, and they should be independent. You should add more detail on the kind of data you're trying to model, so we can give more concrete advice on thow to handle it.
I will explain as an answer, since I can't fit it into comment.
In var data1 = SameData() the SameData() is the object, and data1 is like a finger pointing to that object.
Then when you say var data2 = SameData(): you create a second object, and point a finger data2 to that object.
So you have 2 objects, and 2 fingers pointing to each object respectively, neither object knows about existence of the other.
Now, if you do var data2 = data1, you are not creating a new object, instead you are pointing another finger data2 to the same object you previously created. So here you have 2 fingers pointing to the same object.
Hence in first case, by changing data1 you are changing the first object, but the second object (to which data2 points) remains intact.
In second case, by changing data1, you are changing the only object you have. And since both fingers point to that same object, they both see the change.
Lets look at the case of structures:
struct SomeData {
var userName = "Anonymous"
}
Structures, just like arrays and dictionaries are value types in swift, which means they have no finger pointing to the object (and there's no object), just the structure itself. So unlike the classes, the structures are copied on assignment. I.e. in
var data1 = SameData()
var data2 = data1
we create a structure data1, then we copy it to data2. As the result we have 2 identical instances of the structure. If we were to change one of the structures, the other structure won't change. E.g.
data2.userName = "Not Anonymous"
means we changed data2 and not data1
By the way, here's an excellent article from Apple on this topic:

How do I create an object of an unknown type until method is called?

I want to create a method that will accept any entity framework database model I've created and return me back another object of the same type. How do I create another object of the same type that I pass to this method?
public object CopyDBObject(object a) {
Type type = typeof(a).MakeGenericType(a.GetType());
object a1 = Activator.CreateInstance(type);
PropertyInfo[] propertys = a.GetType().GetProperties();
foreach (var property in propertys)
{
//iterate through properties
//set a1 properties equal to several a properties
}
return a1; }
Basically I just want "a1" to be the same type as "a" which I'm passing into this method.
At first I just had a line of code:
object a1 = new object();
but I got an error since "a1" was just a generic object, so I couldn't set its properties similar to what "a" has.
So I changed that line to these listed above:
Type type = typeof(a).MakeGenericType(a.GetType());
object a1 = Activator.CreateInstance(type);
but this doesn't work. I don't know what should be in place of "typeof(a)". The error says that it is "a is a variable being used like a type." When I set something to "a.GetType()" it does show me the correct object type for "a" that I passed it, but how do I set "a1" to be that same type?

Do two copies of a case class use twice the memory when only one property is changed, or does Scala reuse immutable values on copy to save memory?

In the following code I instantiate Name once and memory must be allocated to store two strings. I then make a copy of that first object, but only changing one property:
case class Name(first: String, last: String)
val original = Name("Freddie", "Mercury")
val copy = original.copy(first = "Robert")
My question - since last is an immutable val, does Scala point copy.last to the same string in memory as original.last in order to save space?
Name would look something like this in Java (output after compiling and decompiling):
public Name copy(final String first, final String last) {
return new Name(first, last);
}
public String copy$default$1() {
return this.first();
}
public String copy$default$2() {
return this.last();
}
public String first() {
return this.first;
}
public String last() {
return this.last;
}
You can see that when you copy a Name object, the default second argument is last()/last, meaning that the last field of the newly created Name is, as you guessed, pointing to the same object as the original Name's last field.
I suppose you could say this is to save the JVM space, but it would also just not make any sense to copy all the fields of a Product instance. copy is not deepCopy, and if you wanted to copy the original object's fields, where would you stop? Do you also copy the fields of those fields, and so on? And how exactly would you copy a field of type Foo without the compiler generating code that does some nasty reflection? It just makes more sense to reuse the fields.

Scala Play Framework Editing a value and storing the new value

I have a need in my web application where I need to allow a user to update an existing item. However I want to know how I can store the original value so that it can be deleted and the new one used once the user has filled in the form.
Let me expand a bit:
Basically, I initially provide the user with a form to fill. After the form is filled in, I create an object from the properties of the form - things like age, name, height, etc are stored in an object, let's call it Person:
case class Person(age: Int, name: String, height: Int)
Now this information can be edited when the user clicks an edit button beside the item. So on the website, there is an option to edit the person.
My question is, how do I store the previous person, and once the user presses Update on the site, I am able to delete the original person object and replace it with the new one?
My current solution is to store the original object in the Session (as JSON), then when the form is updated, I read the JSON from the session to do more work.
I feel like this is not safe because (correct me if I'm wrong) it is possible to change the session data json from outside the app, thus allowing someone to change the person's name or something and I will not know this and then delete the wrong person from the list rather than the one I was going to update.
def editPerson(name: String) = Action { implicit request =>
Person.findByName(name).map { person =>
val form = personForm.fill(person)
Ok(views.html.persons.editPerson(form))
.addingToSession(("Edit", Json.prettyPrint(Json.toJson(person))))
} getOrElse NotFound
}
The above method is mapped to a route which allows one to specify a person to edit. When the Update button is pressed, the following controller method is called:
def save = Action { implicit request =>
val newPersonForm = personForm.bindFromRequest()
newPersonForm.fold(
hasErrors = { form =>
request.session.get("Edit").map { person =>
Redirect(routes.Persons.editPerson(person.name))
.flashing(Flash(form.data) + ("error" -> Messages("validation.errors")))
} getOrElse(BadRequest.removingFromSession("Edit"))
},
success = { newPerson =>
request.session.get("Edit").foreach { prevP =>
Person.remove(prevP) // delete the person that was edited
}
Person.add(newPerson) // add the new person
val message = Messages("persons.new.success", newPerson.name)
Redirect(routes.Persons.show(newPerson.name)).flashing("success" -> message).removingFromSession("Edit")
}
)
}
What the above save method does is that if the form was completed with errors, the person information is read once again from the session and we are redirected to the edit page once more.
If the form has no errors, we once again read the person information from the session and this time, it is deleted and the new person added.
What other methods can I use to ensure that the object is not exposed to the outside and remains within the controller until the user enters valid values in the form, at which point the object to edit is then deleted and the new one added?

How to change Class object name in a loop

So I have Class
class MyClass
....do things...
and I add objects to it with
ObjName = MyClass(things)
and my problem is that when I add ObjName to MyClass in a loop, I can't figure out a way to create a new object name each loop so it keeps overwriting the only Obj this ends up creating. I tried adding a list as in
ObjName[i] = MyClass(things)
but it wouldn't work.
Here is what I'm trying to do specifically (edited for clarity):
So when I add objects to MyClass, the name of the object added should be callable with input, like so:
somename = input("objname: ") # User input decides how the object values can be called
TempObjName = MyClass(things) # Values of the specific object, will contain more than one unique object
*...*
somename.someattribute() ## 2 different
somename2.someattribute() ## values, sets or etc
Try this:
ObjList = []
for i in whatever:
temp = MyClass(things)
ObjList.extend(temp)
I wasn't able to create the function I wanted per-say, but because my code had a dictionary that saved the obj name and obj value in it, I was able to do what I initially wanted to do by rerunning the requested name from the dictionary in the Class.