I am attempting to us Angular Material cdkDragDrop.
The list is attempting to be populated from a nested object. I am able to display the lists, but when attempting to move an item, it does not appear in the 'Added Details' list
Code: https://stackblitz.com/edit/angular-awxclk?file=src%2Fapp%2Fcdk-drag-drop-connected-sorting-example.html
In your code, you are using moveItemInArray and transferArrayItem which expects (as the name suggests) first parameter to be an array. In your case you are pointing it to an object, so the method throws internally.
What you want to do is to use actual collections in as the [cdkDropListData] in both containers. Then, you iterate those same collection in the *ngFor directive.
So, in your constructor / onInit you transform the object into array and use that array as [cdkDropListData]. Then, if you want to, you can transform the array back into object and update it at the end of your drop method.
Related
Working on a project that will display hierarchies of "tasks". I'm running into a problem where it will not allow for multiple entries of the same object. From what I can tell, the "duplicate" item is under a different parent.
The domain data allows for this - a given task may appear in lots of places.
It would seem that this is intentional (maybe), but is there a way around this?
It's intentional to a degree; each Grid and TreeGrid data item is expected to be unique. You could work around this by creating your own implementation of the hierarchical DataProvider class (for example extend AbstractHierarchicalDataProvider) which overrides the getId method along with the other required methods. The return value of this method needs to be unique per item, as it's used as a hash key.
Well, this is probably not the best solution, but it works.
I added a field to the abstract super class that is initialized with the current time (long ms). When I am adding items to the tree grid, I check to see if the tree contains the item and if so, I randomize the field and then add it. The new field is marked #Transient so it's not persisted.
I have an OData model which I successfully use to fill eg. a table in a SAPUI5 app:
<Table items="{globalSettings>/APP_GLOBAL_PREFERENCES/}">
etc.
The entity set APP_GLOBAL_PREFERENCES consists of entities each having a KEY and VALUE property, with KEY being the, well, key.
Now, I'm trying to bind the textproperty of a <Text>control to a property of a specfic instance of the entity:
<Text text="The base URL is '{globalSettings>/APP_GLOBAL_PREFERENCES('BASE_URL')/VALUE}'" />
In order for this to work, I have to create an element binding, as suggested here. In my case, I do it in the lifecycle method onBeforeRendering of the associated controller:
onBeforeRendering: function () {
this.getView().bindElement({
path: '/APP_GLOBAL_PREFERENCES',
model: 'globalSettings'
});
}
I don't understand why this step is necessary, and I would like to avoid it.
Can anyone explain why this step is required, or does anybody have a clue how to get by without it?
In UI5, there are 3 types of bindings:
Aggregation bindings can be used with aggregation (for example table or list items). This requires an array of objects from data point of view. In this case, you have to configure a so-called template, which will be repeated for every instance of the aggregation. Here, you need to use relative binding, so you don't have include the array reference itself in the binding path.
Element bindings are used to bind objects from the model to a control (views are also controls!). If the control has any child elements, these child elements can be bound to any property of this object using relative binding.**
Property bindings are the elementary parts of a binding procedure; each control has more or less properties, and you can assign model values to these properties using property binding.
** This means, that in your Text control you have to use a relative binding, so just use {VALUE}. However, make sure that if APP_GLOBAL_PREFERENCES in your model points to an array, you have to select a specific item in your bindElement (for example:
this.getView().bindElement({
path: '/APP_GLOBAL_PREFERENCES('BASE_URL')',
model: 'globalSettings'
});).
Edit:
Ok, now I see your problem! Answering your question: it's not necessary.
If you use the this.getView().bindElement(..) property, you can use relative path in your text control (assuming that you include the selection of the proper item in this segment). My examples are based on JSONModel:
this.getView().bindElement({
path: '/APP_GLOBAL_PREFERENCES/0',
model: 'globalSettings'
});
Then you can do this in your view:
<Text text="{globalSettings>text}" />
If you skip the bindElement (but you've assigned the model to the view), you need to use the following path:
<Text text="{globalSettings>/APP_GLOBAL_PREFERENCES/0/text}" />
If you are implementing a detail view, which gets the ID of the selected item as an URL parameter, it's recommended to use the bindElement with the selection to the given parameter; then in your XML view, you can use relative binding and you don't have to care about the ID of the item.
How do I remove a row from a container/item/grid/db/entity/bean/class/object/ID?
Also, what's the difference between all these?
Everyone seems to say these as if they were interchangeable.
Can I get a simple explanation of how these all work together?
I've been through dozens of youtube videos and tutorials, but I still can't see the big picture.
Simple task: Delete one row from a grid.
But then it starts getting bigger and more complex with nested beanitem container property field entities and I just can't make sense of it.
Thank you for all of your help in advance!
The Grid, Table or any other Vaadin Component used to present set of data use some implementation of the Container to store your data. A Component is a part of your User Interface, the <div> in your DOM which is seen by your end user. The Vaadin Containers contains your objects. The most widely used containers are:
IndexedContainer - default container for Grid and Table. You usually add items by calling addItem method on either container or related component. The disadvantage of using this type of container is that you are usually obligated to set appropriate properties (think of columns) on both items and the container itself,
BeanContainer - is able to receive Java objects that follows JavaBean convention. Thus it is able to automatically infer properties of your component,
SQLContainer - contains data stored in database. Constructed using SQL query. Can be setup to automatically update your database based on changes made by user in UI.
Items and IDs
Adding single items to some containers may look a bit complicated. There are a few ways to do this. They are described very well on a Vaadin website. Basically the ID is an unique object that you use to access corresponding Item. The Item is represents the single row in your component. Every Item have properties. You can access and make changes to your items in container using their IDs i.e.
table.getItem("uniqueId");
Usually, you don't operate directly on containers. The components expose basic Container interface methods via their API. In example implementation of AbstractSelect.getItem() component currently (Vaadin 7.5.9):
protected Container items;
public Item getItem(Object itemId) {
return items.getItem(itemId);
}
(AbstractSelect is a super class of other Vaadin components like Table and Grid)
It gets more complicated with properties of the items. Every Item have some properties (columns). And every Property has its corresponding ID. Using property ID you can access the value of the item in the specific column. The following code presents above - it adds one item with one property and sets its value:
Table table = new Table();
table.addContainerProperty("column1", String.class, "defaultValue");
Item item = table.addItem("uniqueId");
item.getItemProperty("column1").setValue("Black Friday");
Notice that it is perfectly safe to use String literals as IDs since underneath they are compared using equals()
How can we pass a list from JSP to the action in Struts 2?
The list is a list of strings set from the same action when the JSP page gets loaded (there is a hidden field in the JSP which is being set).
All that I need is when again the form is submitted and the control goes to the action, I need that list again.
Make an array of strings and submit it using json or make a # seperated string of all strings and associate it with some hidden variable and submit it to action class. then in action class you needs to parse it back to original form.
you can use the hidden tag name attribute to refer to the list name that is being used in the class. Now when the form is submitted it will automatically map to the list in your action.
You need to have list as your instance variable in the action class
If you are setting a single hidden field with the contents of a list then you'll need to parse it back into a list again. Whether or not this is a good idea depends on the contents of the list, and how good you are at parsing.
Another option is to use Struts 2's default list-building mechanism and use multiple hidden fields and OGNL's array notation, e.g., name="foo[0]", name="foo[1]", and so on.
I'd start, however, by examining the need to reconstruct the list from the JSP like this.
If you're just serializing/deserializing the same list, why bother? Either keep it in session, or reconstruct it on the Java side. If it's backed by a DB then your caching mechanism should reduce any overhead.
Following this post and the other posts that it links to, I have successfully built a page with a variable legnth editable list.
Now, I need to do another page with similar functionality, but going a step further. Basically I need to create a variable length list of objects as I have already done, but in this case each object in the list needs to have its own variable length editable sub list.
I can build the view with no problem but I guess I am lost as to how I will handle this in my POST controller method to save the objects.
Will my controller take 2 IEnumerable parameters?
I.E.
public ActionResult SaveList(IEnumerable<MainList>, IEnumerable<Sublist>)
{
}
It seems like this would work but I have no way of associatiing the objects in the sublist collection with the appropriate object in the main list collection.
What am I missing?
Summing up the results of our comment-discussions as an answer:
The sublists should be members of your MainList elements. In the View you will have a nested foreach loop to work with the Sublist elements. In the controller you just accept the MainList as a parameter (it should already have the sublists as members). In order for the ModelBinder to pick up the sublists correctly, the names of the inputs should include the names of main list first (and not just the properties inside the sublist item).
I think you need to build a ModelBinder class to accept these parameters the way you want into your controller object.