Named (map) constructors do not work in grails.gsp.PageRenderer - grails

When I use a map constructor like:
Person p = new Person(name: "Bob")
through something that is called via a grails.gsp.PageRenderer, the field values are not populated. When I use an empty constructor and then set the fields individually like:
Person p = new Person()
p.name = "Bob"
it succeeds. When I use the map constructor via a render call, it also succeeds.
Any ideas as to why this is the case?
Sample project is here in case anyone wants to dig deeper: https://github.com/danduke/constructor-test/
Actual use case, as requested by Jeff below:
I have a computationally expensive view to render. Essentially it's a multi-thousand page (when PDF'd) view of some data.
This view can be broken into smaller pieces, and I can reliably determine when each has changed.
I render one piece at a time, submitting each piece to a fixed size thread pool to avoid overloading the system. I left this out of the example project, as it has no bearing on the results.
I cache the rendered results and evict them by key when data in that portion of the view has changed. This is why I am using a page renderer.
Each template used may make use of various tag libraries.
Some tag libraries need to load data from other applications in order to display things properly (actual use case: loading preferences from a shared repository for whether particular items are enabled in the view)
When loaded, these items need to be turned into an object. In my case, it's a GORM object. This is why I am creating a new object at all.
There are quite a few opportunities for improvement in my actual use case, and I'm open to suggestions on that. However, the simplest possible (for me) demonstration of the problem still does suggest that there's a problem. I'm curious whether it should be possible to use map constructors in something called from a PageRenderer at all. I'm surprised that it doesn't work, and it feels like a bug, but obviously a very precise and edge case one.

"Technically it is a bug" (which is the best kind of bug!), and has been reported here: https://github.com/grails/grails-core/issues/11870
I'll update this if/when additional information is available.

Related

How to bind from DataTemplate to Page's DataContext?

I've got a page where a DataTemplate is being used to bind to the model for that content, e.g.:
<DataTemplate x:DataType="models:MyDataType">
... content ...
</DataTemplate>
In that content, I need to be able to bind a Click event. I need that click event to exist in the view model that is set as the page's DataContext:
<Page.DataContext>
<vm:MyViewModel x:Name="ViewModel">
</Page.DataContext>
but I'm really struggling with getting it to compile. Every approach I try results in the compilation error "Object reference not set to an instance of an object".
I know I can't use x:Bind because that will bind to the DataTemplate's DataContext, so I've been trying to use Binding and, based on other SO answers I've read, it seems like the answer should be:
Click="{Binding DataContext.Button_Click, ElementName=Page}"
where Page is defined as the x:Name for the Page. I've tried removing DataContext. I've tried adding ViewModel.
What am I misunderstanding? Is it not possible to do what I want to do? I've tried using code-behind instead but I'm using Template 10 and that pushes almost everything onto the view model, which makes it harder for me to access things like the navigation service from code-behind.
tl;dr; use Messaging.
#justinXL is right, 'ElementName' can work. But is it best?
The problem you are trying to solve has already been solved with messaging. Most MVVM implementations include a messaging solution. Prism uses PubSubEvents; MVVM Light has its own messenger. There are others, too.
The idea is that an outside class, typically described as a message aggregator, is responsible for statelessly receiving and multicasting messages. This means you need to have a reference to the aggregator but not a reference to the sender. It’s beautiful.
For example
A common use case might be a mail client and how the data template of a message in the list would include a trash/delete button. When you click that button, what should be called? With messaging, you handle the button_press in the model and send/publish a message (one that passes the item).
The hosting view-model has subscribed to the aggregator and is listening for a specific message, the Delete message that we just sent. Upon receipt, it removes it from the list and begins the process to delete it from cache/database, or whatever – including prompting the user with “Are you sure?”
This means all your data binding in your data template is local, and does NOT extend outside its local scope. Why does this matter? Because if you use Element Binding to reach the hosting page, it means you cannot 1) move this template to a resource dictionary or 2) reuse this template.
There are two other reasons.
you cannot use compiled x:Bind to do this because it already limits use of this painful binding approach – this matters because a data template is typically in a list, and performance should always be prioritized, and
It adds considerable complexity.
Complexity?
I am a big fan of sophisticated solutions. I think they are rare and are the trademark of truly smart developers. I love looking at such code/solutions. Complex is not the same as sophisticated. When it comes to complexity, I am not a fan. Data binding is already difficult to wrap your head around; multi-sourcing your data binding across scope boundaries is pure complexity.
That’s what I think.
Your binding expression is correct, except it won't work with a Button_Click event handler. You will need an ICommand defined in your page's ViewModel.
Since you are using Template10, you should be able to create a DelegateCommand called ClickCommand like this
private DelegateCommand<MyDataType> _clickCommand;
public DelegateCommand<MyDataType> ClickCommand
{
get
{
_clickCommand = _clickCommand ?? new DelegateCommand<<MyDataType>>((model) =>
{
// put your logic here.
});
return _clickCommand;
}
}
And the binding will be updated to
<Button Command="{Binding DataContext.ClickCommand, ElementName=Page}" CommandParameter="{x:Bind}" />
Note I have also added a CommandParameter binding to the button as you might want to know which MyDataType instance is associated with the clicked button.

Fragments and ViewModels

I am trying to have a single activity with a dynamically created fragment within its view.
I have a ActivityViewModel and a FragmentViewModel and matching views and layouts (ActivityView has a FrameLayout to host fragment). The fragment is shown by calling ShowViewModel<> from within ActivityViewModel.Start method.
I am using a CustomePresenter as described in http://enginecore.blogspot.ro/2013/06/more-dynamic-android-fragments-with.html.
It works fine from cold start and after resume. However, it won't work after activity is destroyed.
This is the sequence that happens in this problematic situation:
Activity is created, Mvx finds a cached ViewModel and attaches it to the Activity. Since ViewModel was cached it won't fire Start method (which triggers fragement creation). That's fine. But in next step Android recreates the fragment but it won't get its associated ViewModel because neither CustomPresenter (which takes care of that when fragment is created) or MvxFragment.OnCreate won't create it - like MvxActivity mechanism does. And thus I get a ViewModel-less fragment.
So I wonder, shouldn't be good if MvxFragemnt creates its own ViewModel upon create like MvxActivity does? Furthermore it should handle Save,Resume (call to adjacent ViewModel's methods).
Or perhaps I am handling this in wrong way or missing something.
I created a sample which describes the same problem, you are describing. You can alter the sample, to support multiple regions with multiple fragments in it. These regions can be used in presenter as well.
Please take a look at this presenter sample, which shows of a simple implementation of using fragments in an Android project: https://github.com/JelleDamen/CustomMvxAndroidPresenter
FYI:
I used the same tutorial as an inspiration. Let me know if you need any help with it.
Sorry, you are correct.
This behavior can be reproduced when creating a simple app with an activity and a fragment and then in the 'developer options' choose to always destroy activity. Now switch to another app and then switch back.
Init and Start are not called, the activity view-model is obtained from the cached view model.
This isn't related to fragments, it's about how view-model works for activity.
Now, regarding the fragment lifecycle and the fact that it doesn't get the view-model bound, as you mentioned, currently this is not available in Mvvmcross.

When is it right to use ViewData instead of ViewModels?

Assuming you wanted to develop your Controllers so that you use a ViewModel to contain data for the Views you render, should all data be contained within the ViewModel? What conditions would it be ok to bypass the ViewModel?
The reason I ask is I'm in a position where some of the code is using ViewData and some is using the ViewModel. I want to distribute a set of guidelines in the team on when it's right to use the ViewData, and when it's just taking shortcuts. I would like opinions from other developers who have dealt with this so that I know my guidelines aren't just me being biased.
Just to further Fabian's comment; you can explicitly ensure viewdata is never used by following the steps outlined in this article. There's really no excuse not to use models for everything.
If you have no choice but to use ViewData (say on an existing project); at the very least use string constants to resolve the names to avoid using 'magic strings'. Something along the lines of: ViewData[ViewDataKeys.MyKey] = myvalue; Infact, I use this for just about anything that needs to be "string-based" (Session Keys, Cache Keys, VaryByCustom output cache keys, etc).
One approach you may wish to consider as your views become more complex, is to reserve the use of Models for input fields, and use ViewData to support anything else the View needs to render.
There are at least a couple of arguments to support this:
You have a master-page that requires some data to be present (e.g. something like the StackOverflow user information in the header). Applying a site-wide ActionFilter makes it easy to populate this information in ViewData after every action. To put it in model would require that every other Model in the site then inherit from a base Model (this may not seem bad initially, but it can become complicated quickly).
When you are validating a posted form, if there are validation errors you are probably going to want to rebind the model (with the invalid fields) back to the view and display validation messages. This is fine, as data in input fields is posted back and will be bound to the model, but what about any other data your view requires to be re-populated? (e.g. drop-down list values, information messages, etc) These will not be posted back, and it can become messy re-populating these onto the model "around" the posted-back input values. It is often simpler to have a method which populates the ViewData with the..view data.
In my experience I have found this approach works well.
And, in MVC3, the dynamic ViewModels means no more string-indexing!
I personally never use ViewData, everything goes through the Model, except when im testing something and i quickly need to be able to see the value on the view. Strongtyping!
In terms of ASP.NET MVC 2, ViewModel pattern is the preferred approach. The approach takes full advantage of compile time static type checking. This in combination with compiling mvc views will make your development work-flow much faster and more productive since errors are detected during build/compile time as opposed to run time.

how should I structure data for a complex object in ActionScript 3?

I hope this isn't too vague, but I'm stuck on a problem that has put me in an Unfortunate Position.
I'm Flash developer getting my feet wet with with AS3 and am trying to build an interior decoration tool for a client. My thinking so far has been: create the basic user interface, get the screen flow down, and then finally use a couple of simple arrays to store user selections and stuff like that.
Naturally my 'couple of simple arrays' is totally inadequate to model the many user decisions that my program has to take into account. So I find myself trying to create an enormous, multi-dimensional array with several layers of nesting and before Panic sets in.
Here's an example of my thinking for the 'bedding' component of the application in pseudo ActionScript:
bedding['size'] = 'king':String
bedding['cover'] = cover:Array
cover['type'] = 'coverlet':String
cover['style'] = 'style_one':String
cover['variation'] = 'varation_one':String
cover['fabric'] = fabrics:Array
fabrics[0] = 'paisely':String
fabrics[1] = 'argyle':String
fabrics[2] = 'plaid':String
cover['trim'] = trim:Array
trims[0] = trim_pair:Array
trim_pair['type'] = 'trim_one':String
trim_pair['color'] = 'blue':String
trims[1] = trim_pair:String
trims[2] = trim_pair:String
cover['embellishments'] = embellishment_pair:Array
embellishment_pair['type'] = 'monogram':String
embellishment_pair['letters'] = 'TL':String
... keep in mind that this is just a fraction of what goes into bedding and there are several other of these kinds of arrays that would go into a room like flooring and walls and funture... all equally complex. And I'll need to frequently access different combinations like, how many options under bedding have no value associated and things like that.
So, I realize I'm out of my league and am going to get hurt on this, but I'd like to try to get this right so that I get better and any help you guys can provide is great.
My questions are:
1) Could it be that using nested arrays like this actually isn't such a bad thing and I should just stick it out? That would suprise me, but I want to make sure I'm not already on the right path.
2) If not, where do I go from here if I want to do this right?
Off the top of my head I feel like I could maybe make everything class based. So my sheets are a class and beds have instances of sheets and rooms have instances of beds... etc. It think it would be complicated but might be the way to go.
Or maybe, I go the XML route and store all of the room options in nested blank XML nodes that a user then populates as they move through the application.
These are my thoughts but I'd like to hear what more experienced members of the community say.
Thank you so much for your help!
My suggestion would be to use a strongly typed model. Look into using collections and value objects to store and retrieve data. A collection could be a class that wraps an Array and provides a clean interface for fetching the value objects that it stores. Value objects are simple objects representing data that can be assembled in various ways to create more complex collections. Value objects can also be passed around to transfer data to various parts of an application. The advantage to using collections and value objects is that your code will be ( potentially ) more explicit and over-all easier to read than if you went with a dynamic approach. For some, the downside to this approach is that you end up with too many classes. Personally, I prefer working with many small to medium size classes versus one monolithic class.
If you are not familiar with the concept of value objects: http://en.wikipedia.org/wiki/Data_transfer_object.
AFAIK, AS3 is not well suited to the type of complex data model you're trying to create.
You need to completely decouple the UI/Flash tier from your "inventory" system. The UI should be completely abstract, with no knowledge of, or coupling to, your data schema or content. This could be accomplished with a middle-tier webservice-styled system that handles all the business logic around searching/retrieving/updating your data.
Store everything your UI needs to handle presentation-side rendering in your product metadata. This will allow you to add new products and types without having to update the UI every time new products are introduced. For example, if a product comes with an image, store a URI to the image with the product record and load it on demand. You could extend this all the way to custom animations, I believe- just reference an outside .SWF file and load it into your application on request.

How to sort data as I want in a VirtualExplorerTreeview (VirtualShellTools)

This is probably a very "dumb" question for whoever knows VirtualShellTools but I only started using it and couldn't find my answer in the demos' code. Please note that I'm also unfamiliar with virtualtreeview.
I use a VirtualExplorerTreeview to display a directory structure, linked with a VirtualExplorerListview to display a certain type of files in the selected directory as well as specific informations about them
I've been able to point them at the right place, link them as I wanted, filter everything in the listview, and looking at the demos I have a pretty good idea about how to add my own columns and draw it to display my custom data.
My issue lies with the Treeview: I would like to sort the directories displayed in the order I want; specifically, I want "My Docs" and other folder to appears first, then drives, then removable media. Looking in the TNamespace property I found how to distinguish them (Directory and Removable properties), but I don't know how to implement my own sort/what event I need. I tried CompareNode but that doesn't even seem to be called.
If you want to do everything yourself, then set toUserSort in the TVirtualExplorerTree.TreeOptions.VETMiscOptions property. That causes the control to just use the DoCompare method inherited from the virtual tree view, and that should call the OnCompareNodes event handler.
A better way is to provide a custom TShellSortHelper. Make a descendant of that class and override whichever methods you need. Create an instance of that class and assign it to the tree's SortHelper property. (The tree takes ownership of the helper; free the old one, but not the new one.) If the items are being sorted on a column that that class doesn't know how to compare, then handle the tree's OnCustomColumnCompare event.
To help you figure out exactly which methods you need to override or events you need to handle, set a breakpoint in TCustomVirtualExplorerTree.DoCompare and step through to see what gets called in various situations.

Resources