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.
Related
I can't wrap my head around this issue I've been experiencing, or perhaps I'm missing some crucial point here. I jotted down this sample app on ionic playground, it is of course a simplified version of my app.
Basically I have a tabbed layout with two views which share a common datasource of items (in my app it's a sqlite db table); the first view displays items in a certain state whereas the other tab display the remaining items (in my example I've used the TODO list metaphor).
Each tab has a child state which I refer to as 2nd-level state (assuming level 0 is the abstract tab state. These two 2nd-level states are defined separately but share a common controller and template.
I cannot for the life of me understand why these two states aren't being navigated to when I click on a list item from either of the two lists (1st-level state views).
NOTE: In the ionic playground no error is thrown in the console, but I can't quite tell what is going on in terms of state URLs. But when I test my actual app (where the problem is the same) in a browser I can see the URL changing to #/tab/tasks/xxxx or #/tab/completed/xxxx but template is not loading. Upon googling I came across several SO questions:
Ui-router URL changes, nested view not loading
In Angular ui-router nested state url changes,but template is not loading
UI-Router: URL changes, but view is not loaded
Angular Router - Url changes but view does not load
URL changes but view does not hcange
Angular UI-Router : URL changed but view isn't loaded
but the answers provided therein haven't worked for me (tried, as per the last one I listed, to add the # sign after the view name in the child states, but to no avail).
Kinda stuck, would really appreciate some input! Cheers.
Managed to get it working following this answer; I had previously tried simply appending the # character after the view name in nested states but it turns out the trick was to append #tab, where tab is the name of the top-level abstract state. I updated my fiddle on ionic playground. Cheers to you all.
Can anyone help.
We are working on an app which has a consistent header and footer and therefore ideally we'll use one viewmodel for the "home page" but we want the header and footer to remain.
Before we switched to starting using Prism, this was easy enough to navigate as we could control that in the Pages event and set the page.contentFrame.Navigate method to go where we wanted.
Now we're using the MVVM structure (which is superb and wish I'd done it ages ago) the NavigationService class only navigates the entire page (the VisualStateAware page).
How can I set this up so that when calling the Navigate method on the interface in the viewmodel that only the main content frame is ever navigated? or is there a better approach to this?
Any help would be greatly appreciated.
thank you
The question title seems to, pre-empt the details of the question slightly as a solution. But to share a common view model and visual parts across all pages, within a frame, using the navigation service to navigate between pages here is an overview..
Create a shared ViewModel, say "HeaderViewModel" of type say IHeaderViewModel to be shared between the different pages' view models. Inject this into the constructor of each page's ViewModel.
Then expose this as a property of each page's ViewModel. This property could also be called HeaderViewModel too. You can then reference the properties of this common HeaderViewModel in the bindings in the View, using binding '.' notation.
If you are using Unity with Prism, you can create this shared instance HeaderViewModel in the OnInitialize override of the App.
Create a shared part for each Page/View as a UserControl, which can be positioned on each page in the same place. This enables you to bind to the same properties on your HeaderViewModel.
My application is designed to load up an XML file and display an error(s) (if any).
The problem I have is how to display both (the XML and Errors) on screen without coupling (my application does currently work).
My application currently looks like (no laughing or comments about me going on a Photoshop\UI course please):
The brown colour is a different view called XmlView.
The red box is where I want errors to be displayed.
So, the user clicks File->Open, selects the file and the .XML content is displayed in my XmlView (brown) and my error messages are shown in red. This works but, I have a horrible feeling my design is poor as I have totally coupled my MainWindow and XmlView.
The way I have this working is, when the user selects a valid XML file (from File->Open), I create an instance of my XmlView and bind it to my Views property of my MainWindow class. My XmlView takes 1 parameter which is the MainWindow type.
So, within my XmlView, to update my ErrorList, I would write code similar to
_mainWindow.ErrorList.Add(//newError)
But this now means my XmlView knows about my MainWindow which I thought was undesired.
So, finally, my question! Is my design poor or is this OK?
You should consider using an MVVM framework if you are doing MVVM.
It would depend on whose responsibility it was to load the XML, but I would suggest the XmlViewModel, not the MainViewModel.
In that case the MainViewModel should just be a conductor of other view models. In your first case, it would instantiate the XmlViewModel, passing the file path and set it as its current view.
The XmlViewModel would be responsible for loading and validating the XML. It too could have a child view model which displays the validation errors. It should load the XML asynchronously, with some form of busy notification.
The MainViewModel is likely to want to conduct many view models, so if you were going to use a framework such as Caliburn.Micro, this would be a conductor type.
I want to implement navigating a tree structure like it is done in e.g. the WinRT file picker. I then want to be able to drop this behavior as part of any page.
My current attempt, is to try and register a secondary FrameAdapter/INavigationService in the container and use that for a frame that is different from the app root frame. So far, I could not get it to work.
My motivation behind that, is, that I do not want to reimplement sth. that the INavigationService already provides.
Basic structure:
ShellView that represents the general app layout (header, footer, navigation) and is currently an OneActive conductor.
Frame control (x:Name="ActiveItem") on the ShellView inside which the hierarchical navigation should occur
The chosen conductor has no relevance yet, since I'll probably have to nest the FrameControl inside another view later to really set up a MDI interface. I'll will want to have multiple screens that should be able to hierarchically navigate
I could not find a CM WP7 example of such a scenario
Can you help me out here?
My problems so far:
How do I access the container from a view code-behind without resorting to using the Application.Current. I figured, it is in the code-behind where I would want to setup the secondary FrameAdapter, since it is here that I have access to the FrameControl
How do I setup the INavigationService so that the initial loading by CM (populating the ActiveItem) is registered with it. There does not seem to be a navigation event for this initial display of the ActiveItem.
Many thanks in advance,
Tobias
PS: I have cross-posted to the Caliburn Micro discussions (Discussion over at CodePlex CM)
I am looking for a good pattern for performing basic property editing via a modal view on the iPhone.
Assume I am putting together an application that works like the Contacts application. The "detail" view controller displays all of the contact's properties in a UITableView. When the UITableView goes into edit mode a disclosure icon is displayed in the cells. Clicking a cell causes a modal "editor" view controller to display a view that allows the user to modify the selected property. This view will often contain only a single text box or picker. The user clicks Cancel/Save and the "editor" view is dismissed and the "detail" view is updated.
In this scenario, which view is responsible for updating the model?
The "editor" view could update the property directly using Key-Value Coding. This appears in the CoreDataBooks example. This makes sense to me on some level because it treats the property as the model for the editor view controller.
However, this is not the pattern suggested by the View Controller Programming Guide. It suggests that the "editor" view controller should define a protocol that the "detail" controller adopts. When the user indicates they are done with the edit, the "detail" view controller is called back with the entered value and it dismisses the "editor" view. Using this approach the "detail" controller updates the model. This approach seems problematic if you are using the same "editor" view for multiple properties since there is only a single call-back method.
Would love to get some feedback on what approach works best.
I don't think any of the Apple examples (or anyone else's) actually show you how to structure an entire real world application. Instead, each example is just a test harness which shows you how to use one particular feature of the API but pays no attention to how that feature really integrates with anything else. Data models are given particularly short shift.
I prefer a design in which the data model is the spine of the application upon which hands all the view-controller/view pairs. The view controllers do not communicate with each other directly but instead communicate through the data model. The data model tracks what information the app is currently working on and therefore what data each particular view controller needs at any given time.
Let's use a contact manager type apps as an example. The basic of the data model would be a list of contact objects each of which in turn would hold attributes of a contact. The data model would completely control access to the data and monitors which data was currently being used. The UI is hierarchal such that the user first sees a list of all contacts in a masterView, then the details of each contact in a contactDetailView and then can edit each contact attribute in a custom attribute edit view for each type of data so there is a nameEditView, a phoneDetailView, an emailEditView etc. Each has a paired view controller masterVC, contactDetailVC, nameEditVC etc.
The to build it's tableview, the masterVC ask the data model for the number of contacts, their divisions into sections and then request each particular contact object at each particular index path so it can display a table. When the user selects a table row, the masterVC tells the data model which contact object was selected by sending it the index. Then the masterVC pushes the contactDetailVC. It does nothing else.
When the contactDetailVC activates, it ask the data model for the currently active Contact object. It doesn't know or care how the current contact was selected nor even which view/VC preceded it. The data model returns the currently active contact. When the user selects a field, the contactDetailVC tells the data model which attribute of the contact was selected and then pushes the proper editorVC onto the stack.
When the editorVC loads it ask for the data model for the current contact and the current attribute being edited. It doesn't know or care how the current contact attribute was selected nor even which view/VC preceded it. When the user makes a change, it ask the data model to save the change (the data model can refuse if verification fails for some reason) and then pops itself.
Internally, I like to implement the data model in two parts each managed by separate object. One is the abstracted data manager itself in this case a Core Data stack. The second is an user defaults manager that tracks the actual state of operations in the data model and saves them to user defaults. A master object holds these objects as attributes and serves as the interface of the data model.
This type of model makes it easy to suspend, resume or restart the application back to its previous state. Since each view/VC is self contained, when you restart the app, you just push all the views on the stack without animation and the last one pushed pops up fully populated with data even though the user chose nothing in the previous views and indeed did not even see them.
It also protects the user from data loss in the event of a crash since each VC saves its data and the app state every time it pushes or pops. It's easy to add additional view/VC because each VC only has to know about and communicate with the data model instead of bunch of other VC. It makes the components of the app easy to use in different versions of the app or in different apps altogether.
Edit:
Do you just hard code some if
statements to pair up the attributes
with the correct editor, or are you
doing something more dynamic based on
the entity/attribute metadata?
In most the most common design, the Contact entity could have a variable number of phone#s, emails or other data fields so each attribute would actually be a separate entity and the Contact would have a relationships pointing to those entities. When the user selected that contact to edit in the ContactDetailView, the data-model would simply mark the NSManagedObject representing the desired attribute of the contact. It could do so by setting an attribute like "lastChosenAttribute" or storing the URI for the object. When the editor view loaded it would be hard coded to ask the data-model for the "lastChosenAttribute" and would receive an NSManagedObject for a phone#, email etc. The editor would make changes to that object and they would be automatically saved back into the contact.
For data that is singular, such as a name or name components, the data-model would provide the editorVC with the contact entity and the editorVC would be hard coded to ask the contact object for that specific attribute.
It's a tough call--the View Controller Guide recommendation seems cleaner conceptually, but the other method can be easier, especially if you're using Core Data. To give a blanket generalized opinion, I would say use your first method if you're using Core Data, since managed objects inherently have their own context and can update themselves (and classes such as NSFetchedResultsController can automatically respond to updates).
If you're not using Core Data, I would go with the "official" recommendation, since it makes it easier to manage updated properties manually. As to the concern about multiple properties, it's certainly possible to have multiple delegate methods and call the appropriate one. For instance:
//if property is an address
if ([self.delegate respondsToSelector:#selector(editorView:didUpdateAddress:)])
[self.delegate editorView:self didUpdateAddress:theAddress];
//if property is a name
if ([self.delegate respondsToSelector:#selector(editorView:didUpdateName:)])
[self.delegate editorView:self didUpdateName:theName];
This could get hard to manage, though--you'd probably want to have an abstract superclass for properties, or something along those lines.