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.
Related
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.
My Flutter app has the following general structure:
The initial screen shows a list of contact objects. The user can tap a contact, which brings up
a detail screen that lets the user see the whole contact info, and modify any details temporarily. The user can either
dismiss the screen without saving the changes, or
tap a save button, which updates the contact permanently, and upon completion leads back to the initial screen.
I'm using FireStore. The list is built with Stream<QuerySnapshots>, and when the user taps an item, the app's router parses the route name (e.g. /contacts/123), creates the respective DocumentReference and forwards it to the detail screen's initializer, which then uses DocumentReference.get to load the details, and DocumentReference.updateData to save changes. Works beautifully – but is only a proof of concept.
Now I would like to hide FireStore and the remaining business logic behind a BLoC. This leads to some questions:
Keeping business logic out of the UI, as far as I understand it, implies that I should stick to named routes, and have the detail screen somehow use the route details to retrieve the relevant contact data through the BLoC. Is that true, and what's the best solution for this?
How can I subscribe to nested data using the BLoC? I want the detail screen to observe data changes, so that I can warn the user if the data becomes stale. Using functions on BLoCs (something like streamOfContact(contact) -> Stream<Contact>) is forbidden, so how would I do that with Sinks? Or is there generally a different way to do this without breaking the BLoC pattern? I'm very new to all of this, so may very well be I'm overlooking something important.
Similar question: How would I update a particular contact?
The tutorials I've found online only deal with root data (e.g. adding cart items to a cart, handling user authentication, ...), but I haven't seen an example showcasing nested data yet. Any help is highly appreciated!
1) Routing and navigation is in the responsibility of the UI layer. That means the UI layer must call Navigator.push[Named](...).
If it makes sense, you can move the logic that decides if the app should navigate to the detail screen:
// in the BLoC:
Stream<int> showContactDetail;
// in the UI layer:
bloc.showContactDetail.listen(_showContactDetail);
How you transfer the parameters to the detail route is totally up to you. You can use named routes, but it would be easier to transfer data with a builder:
void _showContactDetail(int contactId) {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
return ContactDetailPage(
contactId: contactId,
);
}));
}
2) One BLoC should be tied to a single screen, that means there should be a separate BLoC for the detail screen (or dialog/sidebar/...), and you would pass the id of your contact to the second BLoC as a constructor parameter, or using a StreamSink, or using a simple setter method.
I would recommend you to use plain old methods instead of StreamSinks for the BLoC inputs. Here is a recent discussion about the topic.
3) The question is not only how to update your contact from the detail screen, but also how the detail BLoC obtains the contact data (if you are only transferring the contact id).
The answer: Another application layer, what I would call the data layer, that is shared between all BLoCs. You can store the data in Firebase, a sqlite database or a simple Map<int, Contact>.
The data layer would also propagate changes to the backend, and notify all BLoCs when the data has changed, probably using a Stream.
The next question that would come up is where you put this data layer (e.g. a class called ContactService):
You can create the ContactService in your ContactListPage, then pass it to the ContactDetailPage in a constructor parameter (using a route builder, as explained above). No magic here. A side effect that you may not want is that the service will be discarded when the list page is popped.
InheritedWidget that is above the MaterialApp, or at least above the Navigator generated by the MaterialApp (You can use the builder of MaterialApp to wrap the navigator with your own widgets). Putting it that high up in the tree ensures that all pages of your app can access it.
Using scoped_model, which is basically the same. Must also be inserted above the navigator to be accessible from both routes
A static variable/singleton
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.
I am building an Android app via Xamarin Mono for Android, I recently started using the azure mobile service .
I need guidance regarding the architecture that should be designed for this functionality:
3 Fragments in my app will be using the Mobile service database, all of them using the same table Item:
Fragment A - List Fragment - querying Item and populate the list with the result.
Fragment B - List Fragment - querying Item and populate the list with the result (with different ListItem layout than Fragment A list).
Fragment C - Fragment - Insert an item to Item table.
I currently have a Adapter class implementing BaseAdapter that holds the table and queries it and insert to it, and then populates the Fragment A list.
However this way I'm unable to show a different ListItem layout in fragment B as the adapter is already set to a specific layout.
I have tried to find documentation about the design standards when using azure mobile service but with no luck.
I would be glad if someone could refer me to a guide like that, or explain where should the MobileServiceClient, MobileServiceTable etc. should be held and where should the table methods InsertAsync, ToListAsync... should be called.
Thank you
Since you already have a instantiated class that I assume is populated there are a couple different ways of going about this. You could create an abstract base class adapter that contains all your code except for the GetView methods, and then create a specialized class adapter for each view that has the GetView method that applies to that view. When you fire up the fragment just pass it the list items in the constructor.
Alternatively (and I'm not sure how well or not this would work since I have never tried it) would be to keep 1 class, but set a bool to designate if it is View A or view B you want to display. You would need to have the fragment change the flag as appropriate for your particular requirements.
What do you think about this programming practice:
- I need to execute one transaction at first form and after that to force some updates that are placed at another form (for each item that is shown at another form). I.e. it would be like show that form and click at some button. Because it is mandatory to execute these functionalities from second form, I thought to do it without showing second form. Is that good programming practice or you have some other recommendation?
Also, is it enough just to set property> Visible:=False before ShowModal for the second form or I need to do some other actions?
Well, it's unusual to have a form that you don't show. Normally you separate your business logic from the UI.
To answer your question, I don't think you need to call ShowModal at all. Just define a method on the form class and call that. Ultimately forms are just Delphi objects and you can use them as such. If you don't want to show them, don't call ShowModal or Show.
Second question first: Setting Visible := False is of no benefit because the point of all ShowXXX methods is to make the form visible. As David says, you could perform the actions without calling Show at all, provided of course your form doesn't rely on any OnActivate or OnShow code in order to do it's job properly.
As for whether this is a good idea, I say no!
As I've already pointed out there is a concern you have to watch out for. I.e. that currently (or even due to maintenance at some point in the future) your form relies on being visible to do its job properly.
Of course, you could work around that by letting the form flicker open, and be programatically closed. Clearly an aesthetically poor choice.
Not to mention the problems of getting it right. You'll end up writing a bunch of patch-work code to wrap the form so that it can do what you need to do, when you should rather do the following...
Correct Approach
Your form is currently doing at least 2 distinct things:
Visual UI control (call it A)
and "mandatory functionalities" (call it B)
It doesn't matter much whether B is doing validation rules, extra processing, or whatever.
B is a process that does not require user interaction.
Therefore, you need to:
Copy B into a non-UI location (either a simple unit with a custom object or a data module). Call it B*
Modify the form to call B* instead of using B.
Test that your form still behaves correctly.
Delete B
And now you can have your new form call B* instead.
The above approach will save you huge headaches in the future.