Scala Play Framework Editing a value and storing the new value - forms

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?

Related

Add or remove item in datagrid does not trigger WhenAnyPropertyChanged

I am using dynamic data with reactiveui,
` _propList.Connect()
.WhenAnyPropertyChanged()
.Subscribe(t =>
{
}`
the code will be trigger if I just edit any item in the grid. However, when I try to add or remove an item, it is not triggered.
In my view model I have something like this
private SourceList<Decision> _myList { get; set; } = new SourceList<Decision>();
private readonly IObservableCollection<Decision> _targetCollection = new ObservableCollectionExtended<Decision>();
public IObservableCollection<Decision> TargetCollection => _targetCollection;
in my view, I simply
this.OneWayBind(VM, vm => vm.TargetCollection, v => v.DataGrid1.DataSource);
If I remove or Add item in the grid, and press Save
_myList.Count() didn't change, but
_TargetCollection.Count() will increase or decrease by number of items I delete
In my ViewModel
OKCmd = ReactiveCommand.Create(() =>
{
//// _myList.Connect()
////.Subscribe(t =>
//// {
//// ;
//// }
//// );
t.Items.count() and it is the initial load items, but I couldn't seem to know what items have been added or removed. Am I missing something.
Of course, I can keep track of what items are added or removed in the UI, but I am hoping I don't have to do that.
Thanks.
To help me answer your question, I need to better understand what you are trying to achieve but first I will explain what the default behaviour of DD is.
If you want add / remove events you need _propList.Connect().Subscribe(changes => ...). These are the collection changes and you will receive all collection change events including the initial load, but no inline changes.
By default, no property changes are wire in. This is because to monitor property changes is expensive and is opt in only. Also WhenAnyPropertyChanged() never tiggers for the initial load. This is because the item is already loaded and no properties have changed between Connect being called and the property changed observable being subscribed to.
Following on from 2, you will never receive a property changed when an item is removed from the underlying source. This is because when an item it removed, any inline subscriptions are disposed of. Otherwise there would be memory leaks.
Another option for monitoring inline changes is to make use of 'MergeMany' which allows you to craft any observable on a specific item, and in your case you can create an observable to return the initial value as well as as subsequent changes.
It is possible using standard rx to listen to collection changes and inline changes in a single observable, which you would have to compose yourself. For example
var myCollectionChanges = _propList.Connect();
var myPropertyChanges = _propList.Connect().WhenAnyPropertyChanged();
var allMyChanges = myCollectionChanges.Select(_ => Unit.Default)
.Merge(myPropertyChanges.Select(_ => Unit.Default));
In the this example, I have used Select(_ => Unit.Default) to enable the merge operator as it requires the same signature. However what signature is returned is up to you, the key point being that the signatures must match.

How can I update TableView in ScalaFX?

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"

how to create a Form from an object of case class

I am using fold method of form as follows
def regSubmit = Action { implicit request =>
userForm.bindFromRequest.fold({
formWithErrors=>BadRequest(views.html.Error("Registration failed")( formWithErrors.errors))
},
{
userData=>Ok(views.html.regconf("Registration Successful")(**//here I want to send a Form, not data from the form**))
})
How can I create Form from a tuple or single variable, a class or a case class?
userForm will (usually?) be defined as a val, so immutable. It holds the mapping (this field name into a variable in this position of this type, ...) When you use bindFromRequest.fold you are not changing userForm, you are using the mapping information in userForm to generate a new instance of your case class, say userData (or a version of the form with errors in it). Each time you execute that method, you will get a new instance of userData.
userForm.fill(userData) returns a new form instance, a populated instance of the form, so also does not change userForm itself.

Load the User object on each request in a controller, how to make this object available?

From what I understand controllers in play are singletons, so I'm not sure how to go about this.
In Rails I could simply load a user in the base_controller, and then in any controller that inherits from the base_controller the user object would be available for me to use.
In play, I'm not sure how this will work. Basically in each Action in a controller I want the user object to be available for me.
def someAction = {
val name = user.name
val age = user.age
Ok("hello")
}
Is this possible in play? I'm' sure it is but I can't seem to figure out way to do this.
I'm not sure you'll be able to make it quite as terse as Rails, but using Play's Action Composition style you can build up something quite flexible.
We start by creating a trait which knows how to build such a user, and wraps a normal Action, allowing the User to be obtained, and passed into the body:
trait UserAware {
def buildUserFromCookie(request:RequestHeader):Option[User] = {
request.cookies.get("myUserCookie").map { c =>
// Build up a User object from cookie contents
// ...
}
}
def cookieNotFound = Results.InternalServerError("Lost your session cookie :-(")
def UserAction(f: User => Request[AnyContent] => SimpleResult):Action[AnyContent] =
UserAction[AnyContent](BodyParsers.parse.anyContent)(f)
def UserAction[T](bodyParser:BodyParser[T])(f: (User) => Request[T] => SimpleResult):Action[T] = {
Action(bodyParser) { request =>
buildUserFromCookie(request).fold(cookieNotFound) ({ user =>
f(user)(request)
})
}
}
}
I'm going to assume that you have a session cookie that holds sufficient information to be able to recreate a User object. Whatever you need to do within buildUserFromCookie() is out of scope here.
I've defined two UserAction wrappers, one delegating to the other. The difference is whether you need a special body parser or not. Below you'll see it in use.
The second UserAction method is the interesting one - if you supply it with a method that can take a User and a Request, it will call it for you after creating the user, or bombing out if it can't find the cookie.
Here it is in use:
object MyController extends Controller with UserAware {
def doSomething = UserAction { implicit user => implicit request =>
Ok(s"The user's name is $user.")
}
def doSomethingJsonish = UserAction(parse.json) { implicit user => implicit request =>
Ok(s"The user's name is $user and their request body is JSON: ${request.body}")
}
}
With the overloaded UserActions, you can still use Play's Body Parsers to get access to a nicely-typed request body, and you've got the freshly-created User available as user.

Really confused about snippets

I have a problem with my lift view. The thing is, I am making an expensive remote rest-api call twice - where I should really need to do it only once.
But I can't figure out how to solve this.
Basically I have an HTML template like this, that needs to display the list of users and their count:
//UserSearchResults.html
Num users: <span class="lift:UserSearchResults.userCount"></span>
User list:
<ul>
<lift:UserSearchResults.userList>
<li><user:userName/></li>
</lift:UserSearchResults.userList>
</ul>
And then I have an actual snippet that goes and retrieves the list of users from the rest-api server. However, note that it actually does this TWICE - once to count the number of users, and once to render the list.
//UserSearchResults.scala
/** Get list of users from api */
def users: List[User] = {
val url = "http://server/rest-api/user-search";
val result = io.Source.fromURL(url).mkString
//... parse users into List[User] and return it
return entries
}
/** Render user count */
def userCount =
"* *" #> users.length //<-- ONE call
def userList(in: NodeSeq): NodeSeq = {
users.flatMap(user => Helpers.bind("user", in, //<--SECOND call
"userName" -> user.user_name))
}
Is there a better place to put the api call? Is there like a "constructor" for the snippet, that I can use cache the user list, and to share it across all the functions in the class?
Any help is appreciated.
If UserSearchResults is a class (as opposed to an object), then there will be a per-request instance of that class. As such, all you have to do is change your def users to a lazy val users and you should be good to go.
If your snippet extends StatefulSnippet, you can just save the list in an instance variable. Another option would be to put the list into a RequestVar. Then it could also be accessed from other snippets.