angular2-grid
angular2-grid copied to clipboard
[Feat] Serialisation inexistent or lack docs?
Currently the docs (README) states nothing about serialisation of the current state of the grid, is inexistent or just lacks docs?
This is an important feature, at least should be in the README.
[ngGridItem]
's col
, row
, sizex
and sizey
are all that is needed to serialize the layout. If we look at the boxes example we can persist and read these properties from each element as follows:
<div [ngGrid]="{'resizeable': false, 'margins': [5, 10]}">
<div *ngFor="#box of boxes" [ngGridItem]="{'dragHandle': '.title', col:box.initialGridPosition.col, row:box.initialGridPosition.row, sizex:box.initialGridPosition.sizex, sizey:box.initialGridPosition.sizey}">
<div class="title">{{box.title}}</div>
<p>{{box.text}}</p>
</div>
</div>
We can also listen to the itemChange
event to persist the new values when they change. Unfortunately, we cannot persist the values directly to box.initialGridPosition
as in some situations itemChange
is fired after the change detection so Angular will throw an Expression ... has changed after it was checked.
exception in dev mode. To work around this we can either manually trigger change detection in our itemChange
listener which is not recommended or store the values in a separate box.currentGridPosition property and sync them with box.initialGridPosition when necessary. Since both options are suboptimal I would suggest adding two-way data binding support for [ngGridItem]
so we can simply bind the position/size properties to it with [(ngGridItem)]
.
@revov is correct. Since there is currently no way to access the directive directly (that I know of), having a function to call to get the serialized items. I've been meaning to create a component demo for the items which will update its own properties and then you can create a method from there to get the data at will. To expand on the above comment, there is a new method changeAny
which you should be able to access to update properties without worrying about the exception. I'll look into the two-way binding as well. Shouldn't be too hard (he says...)
@revov i think your solution is good enough for me, i just need serialization to save the new positions and sizes for next login, no need to persist that data to the initial grid, however i do think 2-way data binding is the way to go.
OK, I've added two-way data binding to the NgGridItem. You can update the items by adjusting config and it'll bounce back any changes made by cascading. Main demo at https://bmorton.co.uk/angular/ now uses this to display item details. Think that'll do @Luchillo?
Omg just what i needed, btw seeing the example how do you make the drag area just the header? i have one with a table inside with resizable columns and the thing just resize and move at the same time.
If that needs a new issue just tell me, closing this for now.
Heeyy let me ask you, in which version is this available? i have a package that uses still Ng2-beta.16 and upgrading is not an option.
I can't remember the exact version but it was one of the 0.6.x releases. The only change in 0.7.0 is support for the Angular 2 release candidate
@revov i'm going for the (ngGridItemChange)
route for the moment until primeng updates to Angular 2-beta.17, the main issue is the amount of times that function gets called, it get's called each time one item is movet TWICE
, and if i move one and that affects other, those too will trigger their respective event, i need a way to throttle those calls to serialize to not saturate the api server, i was thinking in Rxjs subjects with the throttle functionality, how's that?
@BTMorton I see, so i can do 2-way data bind to the original data object, can i add both the 2-way
and the (ngGridItemChange)
so that the binding will keep the original data updated and i can throttle the event to send the data to server?
@BTMorton Do you have available the code to https://bmorton.co.uk/angular/ example? i get unexpected behaviour out of:
<div class="grid-item" *ngFor="let widget of widgets"
([ngGridItem])="{
col: widget.x,
row: widget.y,
sizex: calcValueWidth(widget.data[0].value, widget.width),
sizey: widget.height
}"
[ngStyle]="widget.item_config && widget.item_config.style"
[ngClass]="widget.classes">
....
</div>
For some reason it just stacks like if it were in a mobile responsive layout and doesn't drag any more.
Oh wait maybe is because in sizex
i`m using a function instead of the object variable, i will have to set that from the api response.
@Luchillo, I think the two-way data binding is all you need. You can then sync the state with the server when the user clicks save or if you want kind of an auto-save feature just sync with the server on the ngGrid
's onItemChange
event.
Also, I believe for the two-way data binding to work you will need to bind the whole ngGridItem
to a property of your widget
. Then this property will always have the up-to-date state that you can persist whenever you wish.
That won't be possible, the user shouldn't need a save button, the idea is that the user can add, remove, reorder and resize on real time, i took care of adding and removing, the idea is that if they move the grid, this one is automatically persisted for the next time he opens the app.
@revov I misread your response, if i understand correctly i should 2-way
the grid items and for the event to throttle use the onItemChange
binding on the grid container?
@BTMorton I'm still getting the stacking behaviour out of this code, is it right?:
<div class="grid-item" *ngFor="let widget of widgets"
[(ngGridItem)]="{
col: widget.x,
row: widget.y,
sizex: widget.width,
sizey: widget.height
}"
[ngStyle]="widget.item_config && widget.item_config.style"
[ngClass]="widget.classes">
....
</div>
Is it because i'm binding an anonymous object instead of a component variable?
Ok i still had to bind with onItemChange
since the widget properties are named different, so i use it to map the values.
@Luchillo you're correct in that you can't two-way bind to an anonymous property. What you can do though, if you have a class for the widgets, you can create a getter and setter to handle it. If you go that method,I recommend implementing a simple setTimeout buffer/throttle so you don't get 1001 save requests. The reason multiple change events happen from the two-way bind is because of the cascading. It could probably be a little smarter but I don't get as much time as I'd like to work on this.
I think the reason it's stacking is also because you're binding to an anonymous object. In theory it should handle it OK but I don't know the internals of the key value differ to guarantee it. Try using the getter to return that or store it as a property of the widget object.
Finally, the onItemChange event might not be the best to bind to. It triggers any time a new value is set in the item. Try binding to onChangeStop instead which will only trigger after a drag/resize event.
It's late here now and I'm not at my PC, but I'll try to throw together an example over the weekend. The demo at /Angular/ is in /src/app.ts in the repo
@BTMorton The stacking was actually happening because i was binding wrong, doing ([])
instead of [()]
which was triggering the data binding backwards so at the start the properties were being binded to undefined which i assume traduce in your plugin to 100% width, also because i was binding to that variable when i fixed the proper binding it returned a template error.
At the end what i will do is bind (onItemChange)
to a function with the widget and the $event
as parameters, then in there call an Rxjs subject with a throttle function to delay the serialization.
Finally i checked when the (onItemChange)
event was fired and it is when i move the item, but it triggers twice, maybe it is as you say, so i should bind to (onChangeStop)
? is that an event of each item or the grid container?
@BTMorton I think i need to use onItemChange
instead of onChangeStop
, according to the docs:
onDragStop(item) // When an item stops being dragged. Returns reference to corresponding NgGridItem
onResizeStop(item) // When an item stops being resized. Returns reference to corresponding NgGridItem
onItemChange(items) // When any item stops being dragged or resized. Returns an array of NgGridItemEvents in the order in which each item was added to the grid
If i bind to the drag and resize ones i will have to do it with both, also if i do it like that will that be called only for the item that moved or also for the other items that were cascaded in positions because of the first item being moved or resized?
Basically the onItemChange
event let me serialise each item in both size and position, also triggers with each item that has moved or resized regardless of human interaction or as triggered by another component being moved and resized.
onChangeStop
is on the items themselves and encompasses the onDragStop
and onResizeStop
events. Depending on how you use the emitted data, that could help with the serialization. IIRC the grid's onItemChange
is called after the cascade so it should include all changes.
Ok i'm serialising ok but now i have an error where if i don't set the cascade
to off
the positions of the items don't matter, only the order of them in array of items, this happens in version 0.6.3
.
On the other hand if i do set the cascade
to off
the serialisation suffers since if i had an item and it has position (1,1)
and height of 4
and new item has position (2,1)
it would start in the middle of first one, thus freezing the app and not even showing the items.
@Luchillo I'm not sure if I follow. Are you saying that the order of the items aren't restored correctly?
At least I'm having this problem. It uses the original order of the array, when I restore the grid. By original order I mean the order in which the items were added.
Thus, to avoid this issue, I sort the array by row and col before persisting. Does anyone have this issue as well? Is there a better workaround? Or am I doing something wrong?
Here's my comparator for the sort:
this.dashboard.grid.sort((a, b) => {
const aRow = a.config.row,
bRow = b.config.row;
if (aRow < bRow) {
return -1;
} else if (aRow === bRow) {
if (a.config.col < b.config.col) {
return -1;
}
}
return 1;
});
@kelvinlouis That's exactly the issue, if i don't deactivate the cascade the grid takes in account only the array order and not the col
and row
property.
My fix const of deactivating cascade and managing carefully the col
and row
property of each item, while yours just reorder the array depending on those properties.