Я пытаюсь написать модуль Drag and Drop, который использует директивы, прикрепленные к различным элементам.Пока что я могу правильно перемещать объекты и связываться с событиями click, drag и drop (я их создал).Сейчас я пытаюсь ограничить область, в которую можно перетаскивать объекты (директива подвижной области).Если я присоединяю это к div и у меня есть вложенные div с помощью директивы movable, я смогу получить вложенные div с декоратором ContentChildren.Как вы можете видеть, в коде директивы подвижной области в ngAfterContentInit я записываю в консоль значения, найденные ContentChildren.Это всегда выводит массив длины 0. Я не уверен, что мне не хватает.Использование всех пакетов Angular 6.1.8 и cli для компиляции и обслуживания.Любая помощь приветствуется ] 1 (я ожидаю, что захваченное окно останется в коробке).
app.component.html
<div class="jumbotron">
<h1 class="display-4">Drag and Drop Module</h1>
<p class="lead">This page is designed to show you how to use the features of the drag and drop module.</p>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h6>Draggable Directive</h6>
</div>
</div>
<div class="row">
<div class="col-12">
<p>The Draggable Directive is simply used as a base directive to let everyone know when the following events occur: Clicked, Dragging, Scrolling While Dragging, Dropped.</p>
</div>
</div>
<div class="row">
<div class="col-12">
<p>Status: {{DraggableStatus}}</p>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="box" appdraggable (dragStart)="DraggableStatus = 'Clicked'" (dragMove)="DraggableStatus = 'Dragging'" (dragScroll)="DraggableStatus = 'Scrolling While Dragging'" (dragEnd)="DraggableStatus = 'Dropped'">
Drag Me
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h6>Movable Directive</h6>
</div>
</div>
<div class="row">
<div class="col-12">
<p>The Movable Directive allows the object to move around the screen. Options include stickyX, stickyY, and reset</p>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="box" appmovable>
No Inputs
</div>
<div class="box" [stickyX]="true" appmovable>
Sticky X
</div>
<div class="box" [stickyY]="true" appmovable>
Sticky Y
</div>
<div class="box" [stickyY]="true" [stickyX]="true" appmovable>
Sticky Both?
</div>
<div class="box" [appMovableReset]="true" appmovable>
Reset
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h6>Movable Area</h6>
</div>
</div>
<div class="row">
<div class="area" appmovablearea>
<div appmovable>
Trapped
</div>
</div>
</div>
</div>
app.component.ts
import { AfterContentInit, ContentChildren, Directive, ElementRef, QueryList, ViewChildren, AfterViewInit, ContentChild, Component, AfterViewChecked } from '@angular/core';
import { MovableDirective } from './Directives/DragAndDrop/Directives/Movable.Directive';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
DraggableStatus: string = 'None';
trappedBoxes = ['Trapped 1', 'Trapped 2'];
}
movablearea.directive.ts
import { AfterContentInit, ContentChildren, Directive, ElementRef, QueryList } from '@angular/core';
import { MovableDirective } from './movable.directive';
import { Subscription } from 'rxjs';
interface Boundaries {
minX: number;
maxX: number;
minY: number;
maxY: number;
}
@Directive({
selector: '[appmovablearea]'
})
export class MovableAreaDirective implements AfterContentInit {
@ContentChildren(MovableDirective) movables: QueryList<MovableDirective>;
private boundaries: Boundaries;
private subscriptions: Subscription[] = [];
constructor(private element: ElementRef) { }
ngAfterContentInit(): void {
console.log(this.movables);
this.movables.changes.subscribe(() => {
this.subscriptions.forEach(s => s.unsubscribe());
this.movables.forEach(movable => {
this.subscriptions.push(movable.dragStart.subscribe(() => this.measureBoundaries(movable)));
this.subscriptions.push(movable.dragMove.subscribe(() => this.maintainBoundaries(movable)));
});
});
this.movables.notifyOnChanges();
}
private measureBoundaries(movable: MovableDirective) {
const viewRect: ClientRect = this.element.nativeElement.getBoundingClientRect();
const movableClientRect: ClientRect = movable.element.nativeElement.getBoundingClientRect();
this.boundaries = {
minX: viewRect.left - movableClientRect.left + movable.position.x,
maxX: viewRect.right - movableClientRect.right + movable.position.x,
minY: viewRect.top - movableClientRect.top + movable.position.y,
maxY: viewRect.bottom - movableClientRect.bottom + movable.position.y
};
}
private maintainBoundaries(movable: MovableDirective) {
movable.position.x = Math.max(this.boundaries.minX, movable.position.x);
movable.position.x = Math.min(this.boundaries.maxX, movable.position.x);
movable.position.y = Math.max(this.boundaries.minY, movable.position.y);
movable.position.y = Math.min(this.boundaries.maxY, movable.position.y);
}
}
movable.directive.ts
import { Directive, ElementRef, HostBinding, HostListener, Input } from '@angular/core';
import { DraggableDirective } from './draggable.directive';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
interface Position {
x: number,
y:number
}
@Directive({
selector: '[appmovable]'
})
export class MovableDirective extends DraggableDirective {
//Variables
position: Position = { x: 0, y: 0 };
startPosition: Position;
startingScroll: number = window.pageYOffset;
@Input('appMovableReset') reset = false; //Resets objects position if dragged.
@Input('stickyX') stickyX = false; //Doesnt move horizontally
@Input('stickyY') stickyY = false; //Doesnt move vertically.
constructor(private sanitizer: DomSanitizer, public element:ElementRef) {
super(element);
}
@HostBinding('style.transform') get transform(): SafeStyle { //This method actually does the moving with a CSS transform
return this.sanitizer.bypassSecurityTrustStyle(
`translateX(${this.position.x}px) translateY(${this.position.y}px)`
);
}
@HostListener('dragStart', ['$event']) onDragStart(event: PointerEvent): void { //Listens to the events emitted by Draggable Directive.
this.startPosition = {
x: event.clientX - this.position.x,
y: event.clientY - this.position.y
}
}
@HostListener('dragMove', ['$event']) onDragMove(event: PointerEvent): void {
this.position.x = (this.stickyX ? this.position.x : event.clientX - this.startPosition.x); //If sticky is set, don't change that value.
this.position.y = (this.stickyY ? this.position.y : event.clientY - this.startPosition.y);
}
@HostListener('dragEnd', ['$event']) onDragEnd(event: PointerEvent): void {
if (this.reset) {
this.position = { x: 0, y: 0 };
}
}
@HostListener('dragScroll', ['$event']) onDragScroll(event: Event): void {
this.position.x = this.position.x;
this.position.y = (this.stickyY ? this.position.y : (this.startingScroll + window.pageYOffset) - this.startPosition.y);
this.startingScroll = window.pageYOffset;
}
}
angular.json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"demo": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/demo",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "demo:build"
},
"configurations": {
"production": {
"browserTarget": "demo:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "demo:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"styles.css"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"demo-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "demo:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "demo"
}