Browse Source

Merge branch 'dev' of https://github.com/abpframework/abp into dev

pull/3602/head
Alper Ebicoglu 7 years ago
parent
commit
8398455bad
  1. 53
      docs/en/UI/Angular/Dom-Insertion-Service.md
  2. 21
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  3. 15
      npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts
  4. 4
      npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts
  5. 10
      npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts
  6. 27
      npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts
  7. 2
      templates/app/angular/package.json
  8. 2
      templates/app/angular/start.ps1

53
docs/en/UI/Angular/Dom-Insertion-Service.md

@ -35,17 +35,19 @@ class DemoComponent {
constructor(private domInsertionService: DomInsertionService) {}
ngOnInit() {
this.domInsertionService.insertContent(
const scriptElement = this.domInsertionService.insertContent(
CONTENT_STRATEGY.AppendScriptToBody('alert()')
);
}
}
```
In the example above, `<script>alert()</script>` element will place at the **end** of `<body>`.
In the example above, `<script>alert()</script>` element will place at the **end** of `<body>` and `scriptElement` will be an `HTMLScriptElement`.
Please refer to [ContentStrategy](./Content-Strategy.md) to see all available content strategies and how you can build your own content strategy.
> Important Note: `DomInsertionService` does not insert the same content twice. In order to add a content again, you first should remove the old content using `removeContent` method.
### How to Insert Styles
If you pass a `StyleContentStrategy` instance as the first parameter of `insertContent` method, the `DomInsertionService` will create a `<style>` element with given `content` and place it in the designated DOM position.
@ -60,27 +62,68 @@ class DemoComponent {
constructor(private domInsertionService: DomInsertionService) {}
ngOnInit() {
this.domInsertionService.insertContent(
const styleElement = this.domInsertionService.insertContent(
CONTENT_STRATEGY.AppendStyleToHead('body {margin: 0;}')
);
}
}
```
In the example above, `<style>body {margin: 0;}</style>` element will place at the **end** of `<head>`.
In the example above, `<style>body {margin: 0;}</style>` element will place at the **end** of `<head>` and `styleElement` will be an `HTMLStyleElement`.
Please refer to [ContentStrategy](./Content-Strategy.md) to see all available content strategies and how you can build your own content strategy.
> Important Note: `DomInsertionService` does not insert the same content twice. In order to add a content again, you first should remove the old content using `removeContent` method.
### How to Remove Inserted Scripts & Styles
If you pass the inserted `HTMLScriptElement` or `HTMLStyleElement` element as the first parameter of `removeContent` method, the `DomInsertionService` will remove the given element.
```js
import { DomInsertionService, CONTENT_STRATEGY } from '@abp/ng.core';
@Component({
/* class metadata here */
})
class DemoComponent {
private styleElement: HTMLStyleElement;
constructor(private domInsertionService: DomInsertionService) {}
ngOnInit() {
this.styleElement = this.domInsertionService.insertContent(
CONTENT_STRATEGY.AppendStyleToHead('body {margin: 0;}')
);
}
ngOnDestroy() {
this.domInsertionService.removeContent(this.styleElement);
}
}
```
In the example above, `<style>body {margin: 0;}</style>` element **will be removed** from `<head>` when the component is destroyed.
## API
### insertContent
```js
insertContent(contentStrategy: ContentStrategy): void
insertContent<T extends HTMLScriptElement | HTMLStyleElement>(
contentStrategy: ContentStrategy<T>,
): T
```
- `contentStrategy` parameter is the primary focus here and is explained above.
- returns `HTMLScriptElement` or `HTMLStyleElement` based on given strategy.
### removeContent
```js
removeContent(element: HTMLScriptElement | HTMLStyleElement): void
```
- `element` parameter is the inserted `HTMLScriptElement` or `HTMLStyleElement` element, which was returned by `insertContent` method.
## What's Next?

21
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs

@ -55,7 +55,14 @@ namespace Volo.Abp.Cli.ProjectModification
if (IsAngularProject(fileDirectory))
{
await CreateNpmrcFileAsync(Path.GetDirectoryName(file));
if (includePreviews)
{
await CreateNpmrcFileAsync(Path.GetDirectoryName(file));
}
else if (switchToStable)
{
await DeleteNpmrcFileAsync(Path.GetDirectoryName(file));
}
}
RunYarn(fileDirectory);
@ -69,11 +76,21 @@ namespace Volo.Abp.Cli.ProjectModification
}
}
private async Task DeleteNpmrcFileAsync(string directoryName)
{
var fileName = Path.Combine(directoryName, ".npmrc");
if (File.Exists(fileName))
{
File.Delete(fileName);
}
}
private async Task CreateNpmrcFileAsync(string directoryName)
{
var fileName = Path.Combine(directoryName, ".npmrc");
var abpRegistry = "@abp:registry:https://www.myget.org/F/abp-nightly/npm";
var abpRegistry = "@abp:registry=https://www.myget.org/F/abp-nightly/npm";
var voloRegistry = await GetVoloRegistryAsync();
if (File.Exists(fileName))

15
npm/ng-packs/packages/core/src/lib/services/dom-insertion.service.ts

@ -6,12 +6,23 @@ import { generateHash } from '../utils';
export class DomInsertionService {
readonly inserted = new Set<number>();
insertContent(contentStrategy: ContentStrategy) {
insertContent<T extends HTMLScriptElement | HTMLStyleElement>(
contentStrategy: ContentStrategy<T>,
): T {
const hash = generateHash(contentStrategy.content);
if (this.inserted.has(hash)) return;
contentStrategy.insertElement();
const element = contentStrategy.insertElement();
this.inserted.add(hash);
return element;
}
removeContent(element: HTMLScriptElement | HTMLStyleElement) {
const hash = generateHash(element.textContent);
this.inserted.delete(hash);
element.parentNode.removeChild(element);
}
}

4
npm/ng-packs/packages/core/src/lib/strategies/content.strategy.ts

@ -10,11 +10,13 @@ export abstract class ContentStrategy<T extends HTMLScriptElement | HTMLStyleEle
abstract createElement(): T;
insertElement() {
insertElement(): T {
const element = this.createElement();
this.contentSecurityStrategy.applyCSP(element);
this.domStrategy.insertElement(element);
return element;
}
}

10
npm/ng-packs/packages/core/src/lib/tests/content.strategy.spec.ts

@ -1,9 +1,9 @@
import {
CONTENT_SECURITY_STRATEGY,
CONTENT_STRATEGY,
StyleContentStrategy,
ScriptContentStrategy,
DOM_STRATEGY,
CONTENT_SECURITY_STRATEGY,
ScriptContentStrategy,
StyleContentStrategy,
} from '../strategies';
import { uuid } from '../utils';
@ -26,8 +26,8 @@ describe('StyleContentStrategy', () => {
domStrategy.insertElement = jest.fn((el: HTMLScriptElement) => {}) as any;
const strategy = new StyleContentStrategy('', domStrategy, contentSecurityStrategy);
const element = strategy.createElement();
strategy.insertElement();
strategy.createElement();
const element = strategy.insertElement();
expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element);
expect(domStrategy.insertElement).toHaveBeenCalledWith(element);

27
npm/ng-packs/packages/core/src/lib/tests/dom-insertion.service.spec.ts

@ -9,7 +9,7 @@ describe('DomInsertionService', () => {
beforeEach(() => (spectator = createService()));
afterEach(() => styleElements.forEach(element => element.remove()));
afterEach(() => (document.head.innerHTML = ''));
describe('#insertContent', () => {
it('should be able to insert given content', () => {
@ -19,6 +19,11 @@ describe('DomInsertionService', () => {
expect(styleElements[0].textContent).toBe('.test {}');
});
it('should set a hash for the inserted content', () => {
spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead('.test {}'));
expect(spectator.service.inserted.has(1437348290)).toBe(true);
});
it('should insert only once', () => {
expect(spectator.service.inserted.has(1437348290)).toBe(false);
@ -37,9 +42,25 @@ describe('DomInsertionService', () => {
expect(spectator.service.inserted.has(1437348290)).toBe(true);
});
it('should be able to insert given content', () => {
spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead('.test {}'));
it('should return inserted element', () => {
const element = spectator.service.insertContent(
CONTENT_STRATEGY.AppendStyleToHead('.test {}'),
);
expect(element.tagName).toBe('STYLE');
});
});
describe('#removeContent', () => {
it('should remove inserted element and the hash for the content', () => {
expect(document.head.querySelector('style')).toBeNull();
const element = spectator.service.insertContent(
CONTENT_STRATEGY.AppendStyleToHead('.test {}'),
);
expect(spectator.service.inserted.has(1437348290)).toBe(true);
spectator.service.removeContent(element);
expect(spectator.service.inserted.has(1437348290)).toBe(false);
expect(document.head.querySelector('style')).toBeNull();
});
});
});

2
templates/app/angular/package.json

@ -3,7 +3,7 @@
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"start": "ng serve --open",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",

2
templates/app/angular/start.ps1

@ -0,0 +1,2 @@
yarn
yarn start
Loading…
Cancel
Save