What is the best place to call a service and load data in xamarin forms
Till now I am calling the service in the view model constructor and loading data
I have a new situation -
In my app I check for network connectivity, if it's not connected to internet I will not load data in the main form and show a modal form saying network is not available check. If the recheck is success will pop the modal but as the data is not loaded the main form is empty.
So in this case I have to write service call in on appearing override function. Which gets called after the modal popped. Which is okay. But problem is every time when we navigate to that view it will make a service call.
Please guide me the best place to call these services
For your problem, you can use C# Events (Publisher-Subscriber) as the solution
Write a event called InternetDisconnectedEvent in the View Model. Subscribe to that event in Code behind of the View (.xaml.cs)
Make UI Changes in the View, when internet disconnected.
For more about Event & Delegates, check this tutorial
"...But problem is every time when we navigate to that view it will make a service call."
Create a flag (a boolean property), say IsLoaded in the viewmodel (or view, depending on your current implementation), that set to false initially. Then add a logic that check IsLoaded flag before calling the service. If IsLoaded is false, run your current logic of checking internet connection, calling service, so on.. and lastly update IsLoaded to true.
Related
I have followed the tutorials and now I am trying to extend that learning into a real app. In my app I use a JSON model. Unlike the tutorials, mine is a real-world app and I have to get user credentials to act as a filter when I load the data model. In the tutorials the model is loaded in component.js. In my app I have to prompt the user for id and password so I have a login fragment that appears modally over the first view in the app. This happens to be a master view, and critically it runs after component.js. After validating the user I collect JSON data from the server via Ajax and place it into the default model via this.setData(my_json).
When testing the routing from master to detail view I produced a stubborn bug in that this.getModel() called in the detail view produced an empty model. Huh - I just set the model in the master view and can see the data in the table control - what gives?
I considered a routing issue but confirmed that was not the problem - I can console log the parameters that pass through the router and anyway the detail view appears so routing is ok.
Recap: I use this.setModel() in the master page then this.getModel() in the detail page but the latter is an empty model.
Question: I want the model to be available across the app. The tutorials focus on setting model in component.js but I cannot. What is the correct syntax for setting the global model from the master view for example, or any other place that is not the component.js.
I think I need to use the following in the master (last line is significant):
var oModel = new JSONModel(); // declare a JSON model
oModel.setData(<json string>); // load a JSON string fetched from serve etc.
sap.ui.getCore().setModel(oModel); // important - set as the core model
I think the source of my confusion is that in the tutorials it seems that models are set in the component via
this.setModel(oModel); // a line in component.js
I therefore assume that this in component.js context is app-global whilst this in a view relates to the view along, which makes sense. Am I right?
In the tutorials this.setModel(...) inside the Component.js will set the model on the Component directly. Therefore, the model is visible in all views inside that Component.
When you see this.getView().setModel(...) inside a controller you know that the model is only set on that one view (and therefore it's also visible for it's children).
However, if you see something like this.setModel(...) inside a controller you should check what happens inside this.setModel(...). It is possible that the model is set on the view, or on the Component, or even somewhere else! Some of the tutorials make use of the so called "BaseController" concept. This is basically a parent controller of other controllers and therefore this approach allows to code some handy APIs that you can easily reuse in the child controllers that extend from this BaseController. For example, have a look at the BaseController of the Worklist App. There you can see that the setModel(...) API is setting the model on the view. That means whenever you call this.setModel(...) in your controllers which extend from that BaseController your model is set on the view!
Furthermore, because in a Master-Detail app there is no hierarchy between Master and Details page (parent/child relation) your models on the Master view are not visible on the Detail view.
In your case it seems to be best setting the model on the Component directly. You can do this by calling
this.getOwnerComponent().setModel(...);
inside any of your controllers. Or just do it directly on the Component.js like in the Wordlist tutorial. You can propagate the data to that model later, i.e. at anytime later from within your controllers.
I am wondering why binding a button inside the Loaded event in WPF page does not work and will only work after navigating to another page, and going back.
I have an inventory app and on the main page, most of the ViewModel are called because of a Back button which goes back to a specific lists and what causes that is, it will start binding the even if that command is not for that page and it will also load the collections for other pages.
So I used Loaded page event to call the necessary methods to populate the lists and also start binding commands for this specific page. I also used Unloaded page event for clean up like unsubscribing to some CRUD events.
The problem now though is, buttons does not get binding in Loaded page event. I do not know why..
I have made a miniature app to demo the problem. It can be downloaded here
(full source code included)
https://www.dropbox.com/s/qzumzyicuvrktsi/ICommandTest.zip?dl=0
This is because your views are not getting notified about the change of Command_ShowAddWindow and Command_ClickMe. Let me explain:
When your Page constructor is first run the bindings to your commands are initialized and transferred to the view, but by that time your commands are null, so the view binds both buttons' commands to null.
Then when your Loaded event is fired the commands are initialized, but the view is not getting notified about it, so it keeps command bindings to null.
The solutions to the problem are:
You manually call RaisePropertyChanged to notify the view about commands change when you initialize them:
void InitCommands()
{
Command_ShowAddWindow = new RelayCommand(Command_ShowAddWindow_Click);
Command_ClickMe = new RelayCommand(Command_ClickMe_Click);
RaisePropertyChanged("Command_ShowAddWindow");
RaisePropertyChanged("Command_ClickMe");
}
Or you initialize your commands in your ViewModel constructor before DataBindings are initialized:
public ViewModel_Page1()
{
InitCommands();
...
}
In my application I have a menu which open a SelectionDialog, this dialog is used to choose an object.
When this object is selected I have to display it in the view.
What is the best way to update my view?
Currently, I call myview.update(object) after the dialog is closed (in the handler of the menu). But I think this solution is not well design.
I have read about update my model and notify my view but my model does not change (no data are changed, I only display different Data ).
Does anyone has some ideas for a well design solution ?
Define model listener ( dataPopulated(Event e))
Make your view implement model listener and register it with the Model.
Define a Model class that can contain the objects that you want to populate in the view
When Model.setInput(Object input) is invoked fire dataPopulated() event on all registered model listeners.
Above steps works properly when you have view activated. You need to consider cases like when if view is deactivated or not visible ( make sure you refresh view is visible else you will have unnecessary overhead of refreshing view though it is notvisible)
Try adding a selection listener in the view and register this selection in the dialog.
In the listener action, add the code to show the selected object.
I am developing a Prism application where I need to load some data in the model in the background after the application has loaded. I have a separate view which is hidden which would show this data via a view model. I dont want to increase the application load time and neither do I want to increase the view load time. The only solution I could think of is to raise a Shell Loaded event and let the view model or model subscribe to it.
Is this a good approach or is there a better approach?
Also, I am not sure whether I should raise the Shell Loaded event in the Run of the Bootstrapper or in the Shell ViewModel. How do I link a routed command (for ex: Loaded) to an ICommand
I try to avoid to handle these kinds of events in the bootstrapper. So attaching the Loaded event of the shell view to the shell view model is a good way to go. The only reason to actually raise this event in the bootstrapper is if you need the StartupEventArgs (which I sometimes do).
There are quite a few ways of handling the loaded event.
100% MVVM use an attached property that passes the event straight to an ICommand or method. Ex: how to call a window's Loaded event in WPF MVVM?
Just handle the Loaded event it in the code behind and call the Loaded method / Command in the VM from there. This a simple and easy solution to get started.
To reduce loading time, you should try to avoid putting too much work on the UI thread. You may want to investigate the TPL or a nice framework called ReactiveUI which provides a very convient mechanism to load things asynchronously.
I am working on an application which is using WPF, MVVM, Prism, and MEF.
I am using a combination of navigation with request navigate, controllers with view management using region manager, and eventing via event aggregator to run the application in a single window. I'm using a view first approach similar to how the Stock Trader RI works.
This works great when any view model code that would interact with the UI (busy indicator) is kicked off by the user, but when it is started behind the scenes there can be problems.
I know this may seem like a poor implementation, but I think I have a valid scenario. My particular example has to do with login.
Currently the application starts and loads the shell. The login view is loaded into the main content region of the shell. When the user clicks "login" a busy indicator is shown and the client application services login is executed. When the login is complete, the busy indicator goes away, and the screen is navigated to the user's home screen.
This works well because the navigation login and navigation are initiated by the user clicking the login button.
So now I have a new requirement that a user can select Auto Login on the login form, such that the next time the user starts the app, the login view will not show up and login will happen behind the scenes.
Now if I just want to call the auto login feature, there is no problem, this by itself has no UI interaction and will work fine. But login takes a few seconds and I want to display my busy indicator.
The problem is where do I initiate the auto login call? The shell view model constructor? The shell view model PartImportsSatisfied implementation? In any of these places, the shell view (which contains my busy indicator) isn't really loaded yet. As a result, none of the resources I need, like regions and region managers aren’t available.
So what might be a good way for me to implement this:
Check if previous user should auto login (got this part figured out)
If yes then
Show busy indicator
Attempt to auto login
If auto login was success
Hide busy indicator
Navigate to user home screen
Else
Hide busy indicator
Navigate to login screen
Else
Hide busy indicator
Navigate to the login screen
Any ideas are greatly appreciated.
Implement an interface within your ShellViewModel which will deal with the concept of being loadable. Do not perform this logic within the constructor of the ShellViewModel as this is typically bad practice and should be used to instantiate objects at the most.
public class ShellViewModel : ILoadable
{
public ShellViewModel()
{
}
bool ILoadable.Load()
{
//this is where you can take care of your auto login
}
}
You could then call this within your Bootstrapper class. If this were being done within another module you could call it within the IModule.Initialize method.
I would also argue that this logic should get implemented within a service which could be called by the ShellViewModel as mentioned above or could in theory be called directly from the Bootstrapper class; allowing the ShellViewModel to potentially then make use of stateful data from within the service.