With angular js, How can I manage an complex view layout that allows the hiding and showing of windows (and so on.) I need to do so without loosing any state or scope while reopening windows if the same url-path will be used. This is like showing a list of items, and opening each in a window parallel to the others. With angular and routes, the previous scope will be destroyed.
(#/itemlist -> #/itemlist/item/1 and #/itemlist/item/2)
One item opens multiple components (two windows; please move them around) with the same state, manged by the angular ui states.
Each state has its own controller and each controller has its own scope.
Scope inheritance can be designed later in an extended prototype, but should be independent of the view hierarchy.
Controller and views will be destroyed by scope destruction or if all views are destroyed the controller and scope must be also destroyed (not completely implemented yet.)
If a route is recalled and a scope exists the correlated view(s) will be reactivated.
The idea is to use two services that manage/stores the controllers and, via scope, the associated views. To shadow the management of both components, a generic controller $stateProvider is used to resolve the controller / view injection:
$stateProvider
.state({
name : 'item',
url: '/item/:id',
views:{
'genericCtrl' : {
resolve : {
views : function() { return ['TestView', 'TestView']; },
ctrl : function() { return 'TestCtrl' ; }
},
controller : 'GenericCtrl'
}
}
})
See this fiddle example
I dont know if the concept works, or if angular has its own means (which I haven't found yet) or if another, better solution exists. Please write your thoughts, improvements and so on down. This should be the base concept of our big application.
Related
A little confused on how to implement this into my main view controller. The example project shows it as a navigation controller, but I wasn't able to add an existing class on a fresh navigation controller or my current UIViewController. I could just be implementing it wrong though. Much appreciation if I can gain some traction on how to work with these.
If you could share some code that would be great.
How things work:
Navigation Controllers
There are currently 4 different Navigation Controllers that each offer their own features. The controllers can be used individually, or together.
SideNavigationViewController
The SideNavigationViewController offers 3 bodies to display content: mainViewController, leftViewController, and rightViewController.
MainViewController
The mainViewController must always exist, and has a facility for transitioning between view controllers using the transitionFromMainViewController method. Using this method is as easy as passing a UIViewController in its first parameter.
sideNavigationViewController?.transitionFromMainViewController(InboxViewController())
There are further parameters that allow for animations, completions, etc... to be set when transitioning between view controllers.
LeftViewController and RightViewController
The leftViewController and rightViewController can be set only once. To make them dynamic, you would need to use another Navigation Controller as its view controller.
NavigationBarViewController
The NavigationBarViewController offers a NavigationBarView along side the ability to manage two UIViewControllers, the mainViewController and the floatingViewController.
MainViewController
The mainViewController is like the SideNavigationViewController's mainViewController, and has a transitionFromMainViewController method that transitions from view controller to view controller in the body portion of the NavigationBarViewController.
FloatingViewController
The floatingViewController is a modalViewController and when set, it pops over MainViewController and NavigationBarView. Setting that value is like so:
navigationBarViewController?.floatingViewController = InboxViewController()
To close and hide the floatingViewController set it to nil, like so.
navigationBarViewController?.floatingViewController = nil
SearchBarViewController
The SearchBarViewController offers a single transitioning mainViewController, as well, has a SearchBarView at the top. Transitioning the mainViewController is like so:
sideNavigationBarViewController?.transitionFromMainViewController(InboxViewController())
MenuViewController
The MenuViewController is another controller that has a mainViewController, which takes the entire screen. Floating above it, is a MenuView that is used to transition between mainViewControllers.
menuViewController?.transitionFromMainViewController(InboxViewController())
Final Notes
These Navigation Controllers can be used in any combination and any amount of times creating a robust and intricate stack of controllers that act like one.
I hope this helps :)
I am creating a winRt app. In which I am having a Home.xaml page which having a another page called Market.xaml. So for snapped mode the same code is repeated.
Now my itemListView (used for snapped) and itemGridView (for full view) both have this page (Market)
<views:Market x:Name="viewMarket"/>
And the constructor of this page is called twice which I not want.
So do I use some flag kind of thing or some better approach is there.
Thanks
So, let's talk about this:
XAML is basically a varying syntax to C#. So, when XAML references a control like your views:Market with <Views:Market />, you are actually putting in something like new Views.Market() in both places. Basically, invoking the class twice. Should the constructor not fire twice, the time-space continuum would split in half. Dogs and cats living together, the whole 9 yards.
But, more fundamental here, what is the purpose of the constructor in C#, or in a XAML class? Is to do expensive things that you would not want to repeat? No. The reason for this is because the completion of the constructor is necessary before the UI thread is released and allowed to render the control. The resulting effect is a UI hang.
Moreover, the C# constructor is a synchronous method. It cannot properly invoke or hold asynchronous operations. This means long-running or expensive tasks that should be invoked immediately, should not be invoked in the constructor because it would also require them to be synchronous and UI-blocking. It is because of these last two points I suspect your constructor is being misused.
The solution is in the XAML framework. The XAML Page pipeline includes the constructor (since it is a C# class and they all have it) but it also includes a Loaded event. In many cases, the hard work necessary to fill page controls is in the Loaded handler. This allows the page to render properly, then starts the long-running action that will ultimately and asynchronously update control content.
In WinRT, the Page pipeline also includes an OnNavigatedTo() virtual method in the base that you can override to the same effect. In the override you can include the hard work of talking to a service, deserializing from a file, or whatever you need to make your UI work. Both the Loaded event and the override can be asynchronous, and neither prevent rendering by freezing the constructor.
But, there's another thing to consider since we're in C# and that the rather common pattern called singleton that allows for us to reference a type in two different contexts but without creating a brand new class. This is accomplished by making the class constructor private, but exposing a public property usually called Instance that returns a single, shared instances in some static place.
That might solve your problem already. Then again, none of that is probably what you need. Assuming you already know all that, the quick answer is you can't prevent a constructor because a constructor is necessary to create a new instantiation of any class, including a XAML view. Instead, whatever you are trying to prevent being double might need to be a combination of the discussions above. An offloaded method, and a static reference to prevent duplicate efforts.
Something like this:
public class Market : UserControl
{
public Market()
{
Loaded += Market_Loaded;
}
static bool AlreadyLoaded = false;
void Market_Loaded(object sender, RoutedEventArgs e)
{
if (AlreadyLoaded)
return;
AlreadyLoaded = true;
// TODO: your work
}
}
But that might not do it for you because the static variable is scoped too large. Instead, you can control if it does the big operation with a dependency property you add to your control. With a boolean dependency property set to false, the second control knows not to do something. With it set to true, the first knows to go ahead. And, so on. This prevents all future use of the view or user control in your app from thinking it should not run because of the static property in the above solution. Then again, that might be perfect.
Best of luck!
We try to build an application with a few tabs. As reference-project we use that example: http://slodge.blogspot.co.uk/2013/06/n25-tabs-n1-days-of-mvvmcross.html
To get the ViewModel-instances we need to create the tabs, we used the "HomeViewModel"-pattern as mentioned in that post: Create View Model using MVVMCross built in factory?
What I don't like at this approach is the initialisation of ViewModel's with "new". As far as I understand, it skips the whole ViewModel-Lifecycle (https://github.com/slodge/MvvmCross/wiki/View-Model-Lifecycle) which we really like. In our current project, we'd like to use the "start()" lifecycle-method, but it's never called due to initialisation with "new".
What worked for us was to go that way:
var loaderService = Mvx.Resolve<IMvxViewModelLoader>();
var vm = (UserListViewModel)loaderService.LoadViewModel(new MvxViewModelRequest(typeof(UserListViewModel), null, null, null), null);
So my question: Is that the way to do the job or is it just a dirty workaround and there is a much better solution?
Update: We came to that solution:
CreateTabFor<SettingsViewModel>("Settings", "settings");
//This method loads the ViewModel
private UIViewController CreateTabFor<TTargetViewModel>(string title, string imageName)
where TTargetViewModel : class, IMvxViewModel
{
var controller = new UINavigationController();
controller.NavigationBar.TintColor = UIColor.Black;
var viewModelRequest = new MvxViewModelRequest(typeof(TTargetViewModel), null, null, null);
var screen = this.CreateViewControllerFor<TTargetViewModel>(viewModelRequest) as UIViewController;
SetTitleAndTabBarItem(screen, title, imageName);
controller.PushViewController(screen, false);
return controller;
}
The 'viewmodel lifecycle' is an area of conflicting interests in MvvmCross. The root cause is the conflict between:
viewmodel's which are just the models for any view
viewmodel's which are specifically used within the 'ShowViewModel' navigation process
For simple 'whole page' User Experiences, the C-I-R-S viewmodel lifecycle is easy to support and to ensure it gets consistently used.
However, as soon as the user experience starts to merge in tabs, flyouts, hamburger menus, dialogs, split views, etc then:
the developers sometimes want to control viewmodel lifecycles themselves
it's not as easy for the framework to ensure that view models are always created, activated and tombstoned/rehydrated consistently
Personally, I like your approach - of trying to ensure all viewmodels are independent and all constructed the same way - but MvvmCross doesn't force this approach on all developers.
Specifically for tabs, most of the existing examples do use the 'owned sub-viewmodel' pattern that you've identified.
However, it should be relatively easy to implement other mechanisms if you want to - just as you already have.
In particular, you can:
use the loaderService directly - getting hold of it via Mvx.Resolve<IMvxViewModelLoader>();
use ShowViewModel with a custom presenter to create both views and viewmodels - the beginnings of this is illustrated in that N=25 video but you could take it much further and actually add the tabs in response to ShowViewModel calls.
use alternative calls to create the child tabs and their viewmodels inside the Views - e.g. where the Touch sample currently calls
var screen = this.CreateViewControllerFor(viewModel) as UIViewController;
this could easily be replace with something like:
var screen = this.CreateViewControllerFor<ChildViewModel>() as UIViewController;;
(or one of the other overloads from MvxCanCreateIosViewExtensionMethods.cs)
One repo where I know some users have taken some of these ideas and played with them is the Sliding menu repo - I think they have chosen to use this.CreateViewControllerFor<TViewModel> to create their view models. This may or may not be the way you choose to go - but it might be of interest for you to experiment with.
I have made a HomeViewModel which contains some other ViewModels to be presented in a TabParPresenter. This works fine and the ViewModels associated Views are presented correctly in their corresponding tabs. However on of the ViewModels have a check in the ctor that tells it in when some conditions apply it needs to navigate to SettingsViewModel , which is not a part of the ViewModels contained in HomeViewModel.
The navigation event gets caught by the TabBarPresenterHost, which is simply the Presenter of the application and if a TabBarPresenter is present and not null it is passed to the TabBarPresenter.ShowView() method. All this happens before the TabBarPresenter is done loading and SelectedViewController is set in ViewDidLoad. Hence the TabBarPresenter relies on using the SelectedViewController to push new Views I obviously get a Null Reference Exception.
In short what I want is:
if (conditionForShowingHome == true)
GoToHome();
else
GoToSettings();
Then when inside SettingsViewModel I have set the stuff I need when going back you return to HomeViewModel.
What breaks this is that the ViewModels are loaded before the View is done loading and the navigation event is executed before the View is done loading. Any ideas how to go around this?
I'm afraid that putting this code inside a ViewModel constructor is likely to lead to problems.
The ViewModel constructors are called during navigations - and not all platforms will support starting new navigations while existing ones are still in progress.
To workaround this, I personally opt to put this type of behaviour in the code before the navigation.
In the case of a normal navigation, this can go inside an MvxCommand execution handler.
In the case of application startup, I do this in a custom app start - see some notes in https://speakerdeck.com/cirrious/appstart-in-mvvmcross
I have a MainView which has an associated MainViewModel in my WPF application, assigned via its contructor.
App > start up uri > MainWindow.xaml
public MainWindow()
{
InitializeComponent();
var viewModel = new MainViewModel();
DataContext = viewModel;
}
My MainView holds as many as four nested views or child views who are hidden and displayed based upon the button that has been clicked on the MainView. So we toggling the visibility property vi a binding which gets updated via command bindings assigned to each button on the MainView.
Each nested View does not have an associated ViewModel, all bindings found on child views find their information in the MainViewModel. So binding system ends waling up the UI tree of the app to find out that the parent 'MainView' has an associated ViewModel.
So overall there is 'ONE' -> ViewModel. This seems to work fine and but as expected this VM has gotten to big and needs re-factoring. It holds information that contextually it should not. But this is a proof concept application. So i decided to keep it simple and make sure it was do-able.
PROBLEM:
When i tried assigning a empty view with an empty view model I noticed binding errors in the output window and as expected weird and broken behaviour. Which makes no sense ... Is there a more clear and concise way of letting WPF know how to handle any bindings it finds in a nested view control? I thought if each view's constructor assigned itself a corresponding VM as shown above, then it should work as this logically makes sense. Unfortunately all buttons on the MainView stop working when the corresponding view it is designated to switch on and hide the others has an associated ViewModel. On some buttons it works and the others it does not? This is really weird?
As mentioned in my answer above, the problem was that WPF binding system was struggling to to resolve bindings at run time. The main view has its associated view model instantiated and assigned via the Main View contructor and this pattern is repeated for all nested views that the MainView also houses.
By default, I tend to use the implied binding syntax which means that without explicitly specifiying a source the binding system will try to resolve the name you supply in the binding. So it's all implied and nothing is explicitly set!
Upgrading each nested view to have its own view model makes this auto discovery/resolution of bindings go a little crazy and I have not explicitly told the binding system where to find the property I am looking for hence the output window binding errors.
This leads to unexpected behaviour as the output window was telling that it was trying to resolve binding expressions in nested views --> viewmodels. When in actual fact that VM it is looking in, IS EMPTY!
So clearly the binding system is good when you do not explicitly set a source property inside the binding syntax. It is clever enough to find things on its own. In my case it needed some extra help, as it was unsure as to where to find things.
SOLUTION:
Remove the constructor declaration for the MainViewModel in the MainView constructor.
Scope in an xmlns for ViewModels namesapce into your MainView.xaml
Create a window resource inside the MainView .xaml
Give the resource a key.
Upgrade all your bindings in the MainView xaml file to include source property.
Give the source property a static resource binding that points to your ViewModel key value set up in step 4.
Only do step 6 for bindings who refer to the ViewModel that is associate with the MainView.
All nested views are to be left alone, they should handle their own bindingds in their own xaml files. The MainView simply instantiates them and places them onto the UI. For me this was the case, I did not have any more bindings concerning my nested views. All Bindings that lived on the MainView.xaml file referred to data in the MainViewModel.cs. This makes alot easier when viewing your problem.
For some reason the editor was being awkward so I chose to omit any sample code but the steps above are descriptive enough to follow the steps that I took. Above is what worked for me.
ANOTHER WAY OF SUMMING UP THIS PROBLEM
Most books teach the shorter binding syntax
What happens when more than one data context is available?
How is the binding system supposed to know how to resolve your short hand binding expressions.