The issue
I have a popup button (NSPopUpButton) that is bound to an NSArrayController. This array controller handles parent objects that each have a collection of child objects. I have an NSTableView in which I need to show these children for the selected item in popup. In addition, the list of children needs to be manipulated (add/remove).
I've tried to accomplish this in many ways but always run into some thing that complicates the solution. What is the best way to implement this?
The data is managed here by Core Data and thus, the collections are NSSets. I've tried adding a conversion method in the parent to return a sorted NSArray (in order to bind it with NSArrayController) but this approach prevents the KVO and the array controller is not updated properly.
Thank you in advance.
An example
To clarify, here's a hypothetic example:
Let's say I have a list of countries that is maintained elsewhere. One of these countries is selected in a popup button. Each country has a set of cities. When a country is selected a table view is populated by it's cities.
There is a solution for this without the delegate/datasource setup.
My context is this:
CoreData model with Parents and Children, one Parent has multiple Children via a relationship named children. Both have a attribute name.
The two Entities must be available as classes (each with a .m and a .h). (Xcode will write them for you if you go to File-New-File-CoreData-NSManagedObjectSubclass.) Now the ChildObjects of a ParentObject can be accessed by ParentObject.children
Two NSArrayControllers: ParentArrayController and ChildArrayController.
Two NSTableViews: ParentTable and ChildTable, each with one column for name. (It should not matter whether you use a Popup or a table as long as it's controlled by a NSArrayController.)
The steps to take are as follows:
Connect both NSArrayControllers to the MangagedObjectContext as usual and set them to Mode: Entity Name with their respective Entity (Parent or Child)
Bind both TableViews (their columns) to their NSArrayController as usual.
Now comes the magic: In the ChildArrayControllers binding section under ControllerContent-ContentSet bind to the ParentArrayController with ControllerKey: selection and ModelKeyPath: children.
Done. If you select a ParentObject in the ParentTable the ChildTable shows its children.
To add and remove children to parents you can use the (void)addChildrenObject:(Child *)value; method that Xcode wrote for you in the Parents.m class file.
I didn't find any way to implement this by simply with dragging and dropping. I had to implement a delegate and data source for the table of cities (from the example). The window controller is notified of the selection changes in the popup button and this updates the content on the table view delegate / data source.
I actually feel this is little bit better way to implement the issue (than with bindings and array controllers) since it gives more control over special cases.
Related
I am a bit confused how to use the component <sw-entity-multi-select>. I understand that the difference between this component and the <sw-entity-multi-id-select> is that the first one returns the entities and the latter one returns just the id of the selected entities. But from the structure and the props they are totally different.
I am confused, because I mainly use the component as this:
<sw-entity-multi-select
entityName="language"
:entity-collection="languages"
:criteria="salesChannelLanguageCriteria"
:label="Language"
#change="selectLanguage"
>
</sw-entity-multi-select>
I could remove the entityName here, as the name is retrieved from the collection as well. But when I dig into the core, I see that inside selectLanguage I should do this:
selectLanguage(languages) {
this.languageIds = languages.getIds();
this.languages = languages;
}
I now understand that languageIds are kind of the v-model that determine, which entities should be selected in the component. Is this true? Why do I have to set the this.languages here again then? To me it's kind of magic if languageIds have this role here, because it's not referenced anywhere on the component. How does it work and how do I tell the component which items are selected - is using languageIds the correct way?
I now understand that languageIds are kind of the v-model that determine, which entities should be selected in the component. Is this true?
No. This example probably just extracts the IDs for some other use, e.g. for adding associations of language to another entity. One could arguably that if this is the only purpose of the selection sw-entity-multi-id-select might be the better component to use.
Why do I have to set the this.languages here again then?
Because you want to store the updated entity collection to persist the selection. Whatever is selected within the multi select is derived from that collection. So, let's say, initially you start out with an empty entity collection. You select some entities and the change is emitted with the updated collection containing the selected entities. Given we have :entity-collection="languages" we then want this.languages to be this updated collection, so the selection persists. So we kinda complete a loop here.
On another note, you could also use the collection with v-model="languages". In that case any additions or removals within the selection would be applied reactively to the collection and you wouldn't need to set this.languages after each change and you could also remove :entity-collection="languages". So basically, which of these approaches you use depends on whether you want your changes applied reactively or not.
I have two forms project(Document) and comment(Response to Response) I want to create a view that displays all comments of selected Project
Any help would be appreciated.
I love doing this type of stuff in xPages.
For a moment, don't think about project and response. Think uniqueID. In order to make the project document reference the comment you add a unique ID to each of them that will reference each other. Don't use response docs in xPages, there is no need I see. When you save the comment, save the unique ID of the parent document to a field on the comment document by referencing this value where document1 is the datasource of the parent and document3 is the document of the child.
<xp:modifyField name="uid"
value="#{javascript:document1.getDocument().getUniversalID()}"
var="document3">
</xp:modifyField>
Now that you have a bunch of parent and children docs, you create a categorized view where you are only capturing the comments. Use a view selection formula to exclude the parent form. Make the first column the uniqueID that you previously captured. You now have a view that will keep all the comments categorized by the unique id of the parent.
Now in the xpage you now need to filter on the key value. You filter by computing the keys to the value of the parent document. Here document1 is the parent.
<xp:dominoView var="view2" viewName="rejections"
keys="#{javascript:document1.getDocument().getUniversalID()}">
</xp:dominoView>
The great thing about doing this is it will work in a viewPanel, dataView, or a repeat control since you are referencing the data not the UI.
If you wanted to do comments of comments there are a couple of approaches but I think they would all have the same structure. If you made it this far start looking at repeat controls they will let you break free of the normal notes views.
I moved away from using Parent and response documents in Notes years ago because they were just too much trouble. I also moved away fro using the UNID as the 'link' for several reasons. The first is that the UNID can change (resolve a save/rep conflict) Second archiving is all but impossible because again the UNID changes when the document is copied to another DB. So when I create a 'parent' document I store #Unique into a field I call LinkKey, and store that value in all 'response' documents. When I create the response then I store a new #Unique into a field rLinkKey. This way a response to a response knows it's originator (LinkKey) and it's direct parent (rLinKey). Now you can extend this any number of levels deep fairly easily.
This worked OK in native Notes with some kludge, but now in XPages with repeat controls the real power comes from nested Repeat Control. Something I have always wanted to do in Notes but was never able to is now a piece of cake.
The down side is that getDocumentByUNID() is going to be faster that getDocumentByKey(LinkKey, true) but the other advantages far out weigh the unnoticeable time difference (IMHO)
I am having a very hard time understanding what needs to be included in the Children Views in order to perform a fetch request and display the results in a table view when using Core Data. All the examples I have found are either only one layer deep (Random Dates), using the Root View Controller which always works, or using several view controllers with pictures and other attributes (Recipes) that make it confusing for me to follow.
An example of what I am looking for would be an Entity with three attributes. The entity is album and the three attributes are albumTitle, albumArtist and yearRecorded.
Now in my Navigation app my Root View Controller has three rows to choose from not using the Entity or Core Data at all. The three choices are "Title", "Artist" and "Year". When you click on one of the three rows it will push a new view controller and list all of the appropriate attributes in a new table view.
I believe it should be very simple and not require too much code but I can't get a handle on it. Any explanations or sample code is greatly appreciated.
You could just make a fetch request with no predicates to get all the objects from your Core Data store, so, you'll have an array of songs.
Than you just call, for example, [fetchedSongs valueForKeyPath:#"artist"]; to get an arry of artist and add it as a source to your tableview.
This question covncerns my lack of understanding of how to use the core data undo manager and how to restore a NSManagedObject to its state before editing was done.
I am just learning my way around Core Data. I have my NSManagedObject classes set up with their dynamic accessors. I perform a fetch that returns several NSManagedObject entity results. Content from each of these entity results (first name, last name) get put into a table view, and then the user picks one out of the table for detailed view and then editing.
The detail view controller receives a pointer to the selected NSManagedObject entity. As the user edits the fields, the corresponding property value in the NSManagedObject entity is updated. This seemed like the cleanest way to manage these changes.
Now, rather than committing the changes using save, I want to provide a cancel-editing feature that rolls back to what is in the data base for that entity. I really only want to restore the one entity and not perform the entire refetch.
I tried rollback and I tried NSUndoManager (with beginUndoGrouping and endUndoGrouping), and that is not working. I don't think I understand what rollback is really supposed to do.
But in any case, I still want to restore the property values in just that single entity (taking the lazy approach to only fetch what is needed, which is the one entity) so that my detail view controller can refill its view with the correct information. Right now it is using the NSManagedObject entity values, which contain the edited values, which were cancelled.
I suppose I could just start the edit process by creating a copy of the NSManagedObject. If the cancel-editing button is pressed, I could copy it back into the original. (I might even be able to just replace the original with the copy by moving the pointer. But since the pointer has actually been passed through several objects, I'm not sure how to manage the retain number on the copy.)
Does anyone have any other suggestions?
Thanks
Using rollback should accomplish what you want and I'm not sure what it doesn't. It is probably an implementation detail error.
You can find the specific managed object/s that were updated but not yet saved by calling the context's updatedObjects.
I have core data app with an entity OBSERVATION that has as one of its attributes DEALNAME.
I want to reference through Interface Builder or by making custom modifications to an NSArrayController a list of unique sorted dealnames so that I can use them in a pop-up.
I have attempted to use #distinctUnionOfSets (and #distinctUnionOfArrays) but am unable to locate the proper key sequence.
I can sort the ArrayController by providing a sort descriptor, but do not know how to eliminate duplicates.
Are the #distinct... keys the right methodology? It would seem to provide the easiest way to optimize the use of IB.
Is there a predicate form for removing duplicates?
Or do I need to use my custom controller to extract an NSSet of the specific dealnames, put them back in an array and sort it and reference the custom array from IB?
Any help would be appreciated. I am astounded that other have not tried to create a sorted-unique pop-up in tableviews.
You need to take a look at -[NSFetchRequest returnsDistinctResults]. That is the level you need to be handling the uniquing of data.
Although I do not have a definitive answer for you, I think there are two ways you can go about it.
The way you already started. You need to bind the contents array of the PopUp button, not just against the arrayController.arrangedObjects, but continue on the path and somehow filter only objects with distinct "DealName"s. This means - the arrayController presents ALL the entities (and may sort them for you) but the PopUp button will have its contents filter via some sophisticated binding to the array controller.
Make your filtering at the ArrayController level (as suggested in another answer here). Here it depends how you set up the array controller. If It is set up to use an "Entity" (vs. "Class") which means the array controller will fetch CoreData entities directly - you can modify its "Fetch" to only bring a subset of the "OBSERVATION" entities with distinct values of "DEALNAME". I don't know how to control WHICH entities are filtered out in this case. Otherwise, you can setup the arrayController to work with "Class" objects, and then you can fetch the entities yourself (in code) and populate the arrayController programmatically, with just the entities you like.
In the second option, the Popup button should be bound normally to the arrayController's arrangedObjects.