Skip to content

Commit

Permalink
Merge pull request #4 from kreuzerk/feature/supportAsyncPipe
Browse files Browse the repository at this point in the history
Feature/support async pipe
  • Loading branch information
nivekcode authored Apr 28, 2019
2 parents daa80b3 + d8aa7d4 commit ee489ea
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Import the ```NgsgModule``` in your ```AppModule```.
```

## Apply the directive
Loop over your elements with *ngFor. 🛎️ the items needs to be an array.
Loop over your elements with *ngFor. 🛎️ the items needs to be an array. Alternate you can also use the async pipe to pass in your items.

![Grid demo](https://raw.githubusercontent.com/kreuzerk/ng-sortgrid/master/projects/ng-sortgrid-demo/src/assets/gs1.png)

Expand Down
7 changes: 6 additions & 1 deletion projects/ng-sortgrid-demo/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<div class="container">
<h1>1. Getting started</h1>
<ngsg-demo-step title="Loop over your elements with *ngFor. 🛎️ the items needs to be an array." image="gs1.png"></ngsg-demo-step>
<ngsg-demo-step title="Loop over your elements with *ngFor. 🛎️ the items needs to be an array. Alternate you can also use the async pipe - see below" image="gs1.png"></ngsg-demo-step>
<ngsg-demo-step title="Apply the ngSortgridItem directive" image="gs2.png"></ngsg-demo-step>
<ngsg-demo-memory></ngsg-demo-memory>

Expand All @@ -22,6 +22,11 @@ <h1>3. Group sortgrids</h1>
one group in another group.</p>
<ngsg-demo-step title="Pass in a unique name to the ngSortGridGroup input" image="gs5.png"></ngsg-demo-step>
<ngsg-demo-groups-memory></ngsg-demo-groups-memory>

<hr class="chaptor-separator"/>
<h1>4. Use the async pipe</h1>
<ngsg-demo-step title="Use the async pipe to loop over the items and to pass in the ngSortGridItems" image="gs6.png"></ngsg-demo-step>
<ngsg-demo-async></ngsg-demo-async>
</div>

<footer class="py-5 bg-dark">
Expand Down
4 changes: 3 additions & 1 deletion projects/ng-sortgrid-demo/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {StepComponent} from './examples/step/step.component';
import {ReactOnChangesMemoryComponent} from './examples/react-on-changes/react-on-changes-memory.component';
import {GroupsMemoryComponent} from './examples/groups/groups-memory.component';
import {CardComponent} from './examples/card/card.component';
import {AsyncPipeMemoryComponent} from './examples/async-pipe/async-pipe-memory.component';

@NgModule({
declarations: [
Expand All @@ -20,7 +21,8 @@ import {CardComponent} from './examples/card/card.component';
StepComponent,
ReactOnChangesMemoryComponent,
GroupsMemoryComponent,
CardComponent
CardComponent,
AsyncPipeMemoryComponent
],
imports: [BrowserModule, NgsgModule],
providers: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.spinner {
width: 40px;
height: 40px;

position: relative;
margin: 100px auto;
}

.double-bounce1, .double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #333;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;

-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
animation: sk-bounce 2.0s infinite ease-in-out;
}

.double-bounce2 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}

@-webkit-keyframes sk-bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
}

@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<h5 style="margin-bottom: 20px">4. Load items and use them with the async pipe</h5>
<button style="margin-bottom: 20px" class="btn btn-primary" (click)="loadItems()">Load items</button>
<div class="card border-primary mb-3">
<div class="card-body">
<h1 class="card-title">Sort order</h1>
<h2 class="card-text">{{ sortOrder }}</h2>
</div>
</div>
<div class="example-container">
<div *ngIf="loading" class="spinner">
<div class="double-bounce1"></div>
<div class="double-bounce2"></div>
</div>
<ngsg-card *ngFor="let item of item$ | async"
ngSortgridItem
ngSortGridGroup="async-items"
[ngSortGridItems]="item$ | async"
[item]="item"
(sorted)="applyOrder($event)"
class="example-box">
</ngsg-card>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {Component} from '@angular/core';
import {Observable, of} from 'rxjs';
import {delay, tap} from 'rxjs/operators';

@Component({
selector: 'ngsg-demo-async',
templateUrl: './async-pipe-memory.component.html',
styleUrls: ['./async-pipe-memory.component.css', '../memory-demo.css']
})
export class AsyncPipeMemoryComponent {

item$: Observable<number[]>;
loading = false;
public sortOrder: number[];

public loadItems(): void {
this.loading = true;
this.item$ = of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).pipe(
delay(1500),
tap(() => this.loading = false)
);
}

public applyOrder(newOrder: number[]): void {
this.sortOrder = newOrder;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ <h5 style="margin-bottom: 20px">3. Drag the items around - hold CMD or Control a
<div class="example-container">
<ngsg-card *ngFor="let item of items"
ngSortgridItem
ngSortGridGroup="getting-started"
[ngSortGridItems]="items"
[item]="item"
class="example-box">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ <h2 class="card-text">{{ sortOrder }}</h2>
<div class="example-container">
<ngsg-card *ngFor="let item of items"
ngSortgridItem
ngSortGridGroup="react-on-changes"
[item]="item"
[ngSortGridItems]="items"
(sorted)="applyOrder($event)"
Expand Down
Binary file added projects/ng-sortgrid-demo/src/assets/gs6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 76 additions & 25 deletions projects/ng-sortgrid/src/lib/ngsg-item.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,34 @@ import createSpy = jasmine.createSpy;
import {NgsgElementsHelper} from './ngsg-elements.helper';

describe('NgsgItemDirective', () => {

let sut: NgsgItemDirective;

const elementRef = {nativeElement: {}};
const ngsgSortService = createSpyObj<NgsgSortService>('ngsgSortService', ['initSort', 'sort', 'endSort']);
const ngsgSelectionService = createSpyObj<NgsgSelectionService>('ngsgSelectionService',
['selectElementIfNoSelection', 'updateSelectedDragItem']);
const ngsgSelectionService = createSpyObj<NgsgSelectionService>('ngsgSelectionService', [
'selectElementIfNoSelection',
'updateSelectedDragItem'
]);
const ngsgReflectService = createSpyObj<NgsgReflectService>('ngsgReflectService', ['reflectChanges']);
const ngsgStore = createSpyObj<NgsgStoreService>('ngsgStore',
['initState', 'hasSelectedItems', 'resetSelectedItems']);
const ngsgStore = createSpyObj<NgsgStoreService>('ngsgStore', [
'initState',
'hasSelectedItems',
'resetSelectedItems',
'hasGroup',
'hasItems',
'setItems'
]);
const ngsgEventService = new NgsgEventsService();

beforeEach(() => {
sut = new NgsgItemDirective(elementRef, ngsgSortService, ngsgSelectionService,
ngsgReflectService, ngsgStore, ngsgEventService);
});

it('should log a warning if we do not pass in sort grid items', () => {
const consoleWarnSpy = spyOn(global.console, 'warn');
sut.ngOnInit();
expect(consoleWarnSpy).toHaveBeenCalledWith(
`Ng-sortgrid: No items provided - please use [sortGridItems] to pass in an array of items -
otherwhise the ordered items will not be emitted in the (sorted) event`);
});

it('should init the store with the sortGridGroup, the ngSortGridItems and the classes', () => {
const sortGridGroup = 'sortgridgroup';
const sortGridItems = ['item one', 'item two', 'item three'] as any;
sut.ngSortGridItems = sortGridItems;
sut.ngSortGridGroup = sortGridGroup;

sut.ngOnInit();
expect(ngsgStore.initState).toHaveBeenCalledWith(sortGridGroup, sortGridItems, {});
sut = new NgsgItemDirective(
elementRef,
ngsgSortService,
ngsgSelectionService,
ngsgReflectService,
ngsgStore,
ngsgEventService
);
});

it('should set the draggable attribute on the elment', () => {
Expand Down Expand Up @@ -133,6 +128,7 @@ describe('NgsgItemDirective', () => {

it('should sort if the group contains selectedItems', () => {
ngsgStore.hasSelectedItems.and.returnValue(true);
ngsgStore.hasItems.and.returnValue(true);
sut.drop({target: {matches: () => true}});
expect(ngsgSortService.endSort).toHaveBeenCalled();
});
Expand Down Expand Up @@ -169,6 +165,7 @@ describe('NgsgItemDirective', () => {
const reflectedChanges = ['item two', 'item one', 'item three'];

ngsgStore.hasSelectedItems.and.returnValue(true);
ngsgStore.hasItems.and.returnValue(true);
ngsgReflectService.reflectChanges.and.returnValue(reflectedChanges);
sut.ngSortGridGroup = group;

Expand Down Expand Up @@ -212,4 +209,58 @@ describe('NgsgItemDirective', () => {
expect(ngsgSelectionService.updateSelectedDragItem).toHaveBeenCalledWith(group, host, true);
});

it(`should init the state with empty items if group has yet not been
initialized and the currentValue is null`, () => {
const group = 'test-group';
const changes = {
ngSortGridItems: {
currentValue: null
}
} as any;
sut.ngSortGridGroup = group;
ngsgStore.hasGroup.and.returnValue(false);

sut.ngOnChanges(changes);
expect(ngsgStore.initState).toHaveBeenCalledWith(group, []);
});

it('should init the state with items from the currentValue if group has yet not been initialized', () => {
const group = 'test-group';
const changes = {
ngSortGridItems: {
currentValue: null
}
} as any;
sut.ngSortGridGroup = group;
ngsgStore.hasGroup.and.returnValue(false);

sut.ngOnChanges(changes);
expect(ngsgStore.initState).toHaveBeenCalledWith(group, []);
});

it('should set the items if the group has allready been initialized', () => {
const group = 'test-group';
const items = ['Item one', 'item two'];
const changes = {
ngSortGridItems: {
currentValue: items
}
} as any;
sut.ngSortGridGroup = group;
ngsgStore.hasGroup.and.returnValue(true);

sut.ngOnChanges(changes);
expect(ngsgStore.setItems).toHaveBeenCalledWith(group, items);
});

it('should log a warning message if you drop and you did not provide any items', () => {
const expectedWarniningMessage =
`Ng-sortgrid: No items provided - please use [sortGridItems] to pass in an array of items -
otherwhise the ordered items can not be emitted in the (sorted) event`;
const consoleWarnSpy = spyOn(console, 'warn');
ngsgStore.hasItems.and.returnValue(false);

sut.drop(event);
expect(consoleWarnSpy).toHaveBeenCalledWith(expectedWarniningMessage);
});
});
30 changes: 22 additions & 8 deletions projects/ng-sortgrid/src/lib/ngsg-item.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
EventEmitter,
HostListener,
Input,
NgZone, OnDestroy,
NgZone, OnChanges, OnDestroy,
OnInit,
Output
Output, SimpleChanges
} from '@angular/core';

import {NgsgReflectService} from './ngsg-reflect.service';
Expand All @@ -19,11 +19,12 @@ import {NgsgElementsHelper} from './ngsg-elements.helper';
import {NgsgEventsService} from './ngsg-events.service';
import {Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {group} from '@angular/animations';

const selector = '[ngSortgridItem]';

@Directive({selector})
export class NgsgItemDirective implements OnInit, AfterViewInit, OnDestroy {
export class NgsgItemDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy {
@Input() ngSortGridGroup = 'defaultGroup';
@Input() ngSortGridItems;

Expand All @@ -43,16 +44,22 @@ export class NgsgItemDirective implements OnInit, AfterViewInit, OnDestroy {
}

ngOnInit(): void {
if (!this.ngSortGridItems) {
console.warn(`Ng-sortgrid: No items provided - please use [sortGridItems] to pass in an array of items -
otherwhise the ordered items will not be emitted in the (sorted) event`);
}
this.ngsgStore.initState(this.ngSortGridGroup, this.ngSortGridItems, {});
this.ngsgEventService.dropped$.pipe(
takeUntil(this.destroy$)
).subscribe(() => this.selected = false);
}

ngOnChanges(changes: SimpleChanges): void {
const sortGridItemChanges = changes.ngSortGridItems;
const sortGridItems = sortGridItemChanges.currentValue ? sortGridItemChanges.currentValue : [];

if (!this.ngsgStore.hasGroup(this.ngSortGridGroup)) {
this.ngsgStore.initState(this.ngSortGridGroup, sortGridItems);
return;
}
this.ngsgStore.setItems(this.ngSortGridGroup, sortGridItems);
}

ngAfterViewInit(): void {
this.el.nativeElement.draggable = true;
}
Expand Down Expand Up @@ -94,6 +101,13 @@ export class NgsgItemDirective implements OnInit, AfterViewInit, OnDestroy {
if (!this.ngsgStore.hasSelectedItems(this.ngSortGridGroup)) {
return;
}

if (!this.ngsgStore.hasItems(this.ngSortGridGroup)) {
console.warn(`Ng-sortgrid: No items provided - please use [sortGridItems] to pass in an array of items -
otherwhise the ordered items can not be emitted in the (sorted) event`);
return;
}

this.sortService.endSort();
const element = !this.occuredOnHost(event) ? NgsgElementsHelper.findHost(event.target, selector) : event.target;
const reflectedChanges = this.reflectService.reflectChanges(this.ngSortGridGroup, element);
Expand Down
31 changes: 31 additions & 0 deletions projects/ng-sortgrid/src/lib/ngsg-store.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,37 @@ describe('NgsgStoreService', () => {
expect(sut.getItems(group)).toEqual([]);
});

it('should return false if the group does not contain items', () => {
const group = 'testgroup';
sut.initState(group);

const hasItems = sut.hasItems(group);
expect(hasItems).toBeFalsy();
});

it('should return true if the group contains items', () => {
const group = 'testgroup';
sut.initState(group, ['item one', 'item two']);

const hasItems = sut.hasItems(group);
expect(hasItems).toBeTruthy();
});

it('should return false if the current group has yet not been initialized', () => {
const group = 'testgroup';

const hasGroup = sut.hasGroup(group);
expect(hasGroup).toBeFalsy();
});

it('should return true if the current group has been initialized', () => {
const group = 'testgroup';
sut.initState(group);

const hasGroup = sut.hasGroup(group);
expect(hasGroup).toBeTruthy();
});

it('should add the classes to the group', () => {
const group = 'testgroup';
const items = ['Item1', 'Item2'];
Expand Down
Loading

0 comments on commit ee489ea

Please sign in to comment.