diff --git a/public/docs/_examples/cb-jquery-plugin/e2e-spec.ts b/public/docs/_examples/cb-jquery-plugin/e2e-spec.ts new file mode 100644 index 0000000000..77584edcb8 --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/e2e-spec.ts @@ -0,0 +1,34 @@ +'use strict'; + +import { browser, element, by } from 'protractor'; + +/* tslint:disable:quotemark */ +describe('Drag and Drop', function () { + + beforeAll(function () { + browser.get(''); + }); + + it('should drag hero to assignment', function () { + + let assignment1 = element.all(by.css('.assignment')).get(0); + + let hero1 = element.all(by.css('.hero')).get(0); + + browser.actions() + .dragAndDrop(hero1 as any as webdriver.WebElement, + assignment1 as any as webdriver.WebElement) + .perform(); + + let heroAssignment = element.all(by.xpath('//div[text()="Help Granny cross the street"]/following-sibling::ul/li[text()="Mr. Nice"]')); + expect(heroAssignment.count()).toBe(1); + + let doneButton = element(by.xpath('//div[@data-hero="Mr. Nice"]/div/button')); + + // Remove Mr. Nice + doneButton.click().then(function(){ + let remainingHeroes = element.all(by.css('.hero')); + expect(remainingHeroes.count()).toBe(3); + }); + }); +}); diff --git a/public/docs/_examples/cb-jquery-plugin/ts/.gitignore b/public/docs/_examples/cb-jquery-plugin/ts/.gitignore new file mode 100644 index 0000000000..cf44e148ba --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/.gitignore @@ -0,0 +1 @@ +**/*.js \ No newline at end of file diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/app.component.ts b/public/docs/_examples/cb-jquery-plugin/ts/app/app.component.ts new file mode 100644 index 0000000000..45b5ad354b --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/app.component.ts @@ -0,0 +1,33 @@ +// #docregion +import { Component } from '@angular/core'; + +@Component({ + selector: 'my-app', + template: ` +
+

Hero Assignments

+
+ +
+
+
+ +
+
+
+ ` +}) +export class AppComponent { + heroes = ['Mr. Nice', + 'Bombasto', + 'Celeritas', + 'Tornado']; + + assignments = ['Help Granny cross the street', + 'Rescue village from dragon(s)', + 'Rescue princess from tower']; + + removeHero(heroToRemove: string): void { + this.heroes = this.heroes.filter(hero => hero !== heroToRemove); + } +} diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/app.module.ts b/public/docs/_examples/cb-jquery-plugin/ts/app/app.module.ts new file mode 100644 index 0000000000..462169b5ea --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/app.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; +import { HeroAssignmentComponent } from './hero-assignment.component'; +import { HeroComponent } from './hero.component'; + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ AppComponent, HeroAssignmentComponent, HeroComponent ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/hero-assignment.component.html b/public/docs/_examples/cb-jquery-plugin/ts/app/hero-assignment.component.html new file mode 100644 index 0000000000..ca7d47cab9 --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/hero-assignment.component.html @@ -0,0 +1,7 @@ + +
+
{{title}}
+ +
diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/hero-assignment.component.ts b/public/docs/_examples/cb-jquery-plugin/ts/app/hero-assignment.component.ts new file mode 100644 index 0000000000..085df48f3c --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/hero-assignment.component.ts @@ -0,0 +1,26 @@ +// #docregion +import { Component, Input, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; + +declare var jQuery: any; + +@Component({ + selector: 'cb-assignment', + templateUrl: 'app/hero-assignment.component.html' +}) +export class HeroAssignmentComponent implements AfterViewInit { + @Input() title: string; + @ViewChild('assignment') assignment: ElementRef; + + assignedHeroes: string[] = []; + + // #docregion add-plugin + ngAfterViewInit(): void { + jQuery(this.assignment.nativeElement).droppable({drop : (event: any, ui: any) => { + let heroName = ui.draggable.data('hero'); + if (this.assignedHeroes.indexOf(heroName) === -1) { + this.assignedHeroes = [...this.assignedHeroes, heroName]; + } + }}); + } + // #enddocregion add-plugin +} diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/hero.component.html b/public/docs/_examples/cb-jquery-plugin/ts/app/hero.component.html new file mode 100644 index 0000000000..e7ee941c44 --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/hero.component.html @@ -0,0 +1,5 @@ + +
+ {{name}} +
+
diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/hero.component.ts b/public/docs/_examples/cb-jquery-plugin/ts/app/hero.component.ts new file mode 100644 index 0000000000..ce787b0a3e --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/hero.component.ts @@ -0,0 +1,25 @@ +// #docregion +import { Component, Input, Output, AfterViewInit, ViewChild, ElementRef, EventEmitter } from '@angular/core'; + +// #docregion declare-jquery +declare var jQuery: any; +// #enddocregion declare-jquery +@Component({ + selector: 'cb-hero', + templateUrl: 'app/hero.component.html' +}) +export class HeroComponent implements AfterViewInit { + @Input() name: string; + @Output() remove = new EventEmitter(); + @ViewChild('hero') hero: ElementRef; + + // #docregion add-plugin + ngAfterViewInit(): void { + jQuery(this.hero.nativeElement).draggable({revert: 'invalid'}); + } + // #enddocregion add-plugin + + done(): void { + this.remove.emit(this.name); + } +} diff --git a/public/docs/_examples/cb-jquery-plugin/ts/app/main.ts b/public/docs/_examples/cb-jquery-plugin/ts/app/main.ts new file mode 100644 index 0000000000..9be7775f4d --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/app/main.ts @@ -0,0 +1,5 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app.module'; + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-jquery-plugin/ts/example-config.json b/public/docs/_examples/cb-jquery-plugin/ts/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/public/docs/_examples/cb-jquery-plugin/ts/index.html b/public/docs/_examples/cb-jquery-plugin/ts/index.html new file mode 100644 index 0000000000..15225ec140 --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/index.html @@ -0,0 +1,34 @@ + + + + JQuery Plugin + + + + + + + + + + + + + + + + + + + + + + + + + Loading app... + + + diff --git a/public/docs/_examples/cb-jquery-plugin/ts/plnkr.json b/public/docs/_examples/cb-jquery-plugin/ts/plnkr.json new file mode 100644 index 0000000000..f88a446b78 --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/plnkr.json @@ -0,0 +1,9 @@ +{ + "description": "jQuery Plugin", + "files":[ + "!**/*.d.ts", + "!**/*.js", + "!**/*.[1].*" + ], + "tags":["cookbook"] +} diff --git a/public/docs/_examples/cb-jquery-plugin/ts/sample.css b/public/docs/_examples/cb-jquery-plugin/ts/sample.css new file mode 100644 index 0000000000..28504dc555 --- /dev/null +++ b/public/docs/_examples/cb-jquery-plugin/ts/sample.css @@ -0,0 +1,30 @@ +.hero{ + background: lightblue; + border: 1px solid black; + width: 100px; + color:black; + height: 50px; + margin-bottom: 20px; + text-align: center; + padding-top: 30px; +} + +.assignment{ + background: darkblue; + width: 250px; + height: 150px; + color:white; + margin-right: 20px; + text-align: center; + padding-top: 10px; + float: left; + margin-bottom: 30px; +} + +.assignment li{ + text-align: left; +} + +.heroList{ + clear:both; +} \ No newline at end of file diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json index 78b159e40c..60f95be92c 100644 --- a/public/docs/ts/latest/cookbook/_data.json +++ b/public/docs/ts/latest/cookbook/_data.json @@ -51,6 +51,10 @@ "intro": "Translate the app's template text into multiple languages" }, + "jquery-plugin": { + "title": "jQuery Plugin Integration", + "intro": "Integrate with jQuery plugins" + }, "set-document-title": { "title": "Set the Document Title", "intro": "Setting the document or window title using the Title service." diff --git a/public/docs/ts/latest/cookbook/jquery-plugin.jade b/public/docs/ts/latest/cookbook/jquery-plugin.jade new file mode 100644 index 0000000000..46a10d7945 --- /dev/null +++ b/public/docs/ts/latest/cookbook/jquery-plugin.jade @@ -0,0 +1,104 @@ +include ../_util-fns + +:marked + Using jQuery for direct DOM access is not recommended in Angular projects, but we do see value in supporting integration with third party jQuery plugins. + + In this cookbook we show how to integrate `draggable` and `droppable` from jQuery UI to make it easier for our admins to assign new adventures to our brave heroes. + + Before a hero can set out on a new quest, one of our admins will give the hero a new assignment by dragging and dropping the hero's name on top of the assignment. +:marked + **See the [live example](/resources/live-examples/cb-jquery-plugin/ts/plnkr.html)**. + + +:marked + ## Table of contents + + [Add jQuery](#jquery-add) + + [Drag](#drag) + + [Drop](#drop) + +.l-main-section + +:marked + ## Add jQuery + + Before we can start we have to add the necessary script references to load the jQuery library and the two plugins, `draggable` and `droppable`, from jQuery UI. + ++makeExample('cb-jquery-plugin/ts/index.html', 'jquery', 'index.html (add jquery scripts)')(format=".") + +:marked + jQuery declares a global `jQuery` variable, but this variable is not know to TypeScript. In order to reference `jQuery` from TypeScript we have to declare a corresponding TypeScript variable as well. + + In this case we don't need to access the jQuery api, so it's unnecessary to add additional typings for jQuery. Instead we will just declare `jQuery` as an `any` variable. + + If we don't declare `jQuery`, the TypeScript compiler will give us an error. + ++makeExample('cb-jquery-plugin/ts/app/hero.component.ts', 'declare-jquery', 'app/hero.component.ts (declare jquery)')(format=".") + +.l-main-section + +:marked + ## Drag + + `draggable` is a jQuery plugin that allows us to move an element on the screen. In the UI our admins will "drag" heroes and drop them on assignments. + + `HeroComponent` is created to represent "draggable" hero elements. + ++makeTabs( + `cb-jquery-plugin/ts/app/hero.component.ts, + cb-jquery-plugin/ts/app/hero.component.html`, + null, + `hero.component.ts, + hero.component.html` +) + +:marked + We want to be careful not to access the DOM directly, so we are using `@ViewChild` to access the target element for the `draggable` plugin. + + `@ViewChild('hero')` declares a reference to an element in the template with a matching `#hero` variable reference. + +:marked + Now, we can go ahead and apply the draggable plugin to `this.hero.nativeElement` using familiar `jQuery` syntax. ++makeExample('cb-jquery-plugin/ts/app/hero.component.ts', 'add-plugin', 'app/hero.component.ts (add plugin)')(format=".") + +:marked + We apply the plugin in the `AfterViewInit` lifecycle hook since we know the component view has been fully initialized at this point. + +.l-main-section + +:marked + ## Drop + + `droppable` is used in tandem with `draggable` to create a drop-zone for dragged elements. + + `HeroAssignmentComponent` represents an assignment that we can assign one or more heroes to using drag and drop. + ++makeTabs( + `cb-jquery-plugin/ts/app/hero-assignment.component.ts, + cb-jquery-plugin/ts/app/hero-assignment.component.html`, + null, + `hero-assignment.component.ts, + hero-assignment.component.html` +) + +:marked + Same as with `draggable`, we are applying the `droppable` plugin in `AfterViewInit`. + + `droppable` lets us specify a callback that executes when a hero is dropped on the assignment. We use this callback to manage an array of assigned heroes whenever a new hero is assigned. + ++makeExample('cb-jquery-plugin/ts/app/hero-assignment.component.ts', 'add-plugin', 'app/hero-assignment.component.ts (add plugin)')(format=".") + +:marked + The final UI looks like this. + + Admins can assign any given hero to multiple assignments by dragging and dropping. +figure.image-display + img(src="/resources/images/cookbooks/jquery-plugin/hero-assignments.png" alt="Hero Assignments") + +:marked + [Back to top](#top) + + + \ No newline at end of file diff --git a/public/resources/images/cookbooks/jquery-plugin/hero-assignments.png b/public/resources/images/cookbooks/jquery-plugin/hero-assignments.png new file mode 100644 index 0000000000..32d1cd8592 Binary files /dev/null and b/public/resources/images/cookbooks/jquery-plugin/hero-assignments.png differ