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) {} constructor(private domInsertionService: DomInsertionService) {}
ngOnInit() { ngOnInit() {
this.domInsertionService.insertContent( const scriptElement = this.domInsertionService.insertContent(
CONTENT_STRATEGY.AppendScriptToBody('alert()') 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. 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 ### 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. 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) {} constructor(private domInsertionService: DomInsertionService) {}
ngOnInit() { ngOnInit() {
this.domInsertionService.insertContent( const styleElement = this.domInsertionService.insertContent(
CONTENT_STRATEGY.AppendStyleToHead('body {margin: 0;}') 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. 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 ## API
### insertContent ### insertContent
```js ```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. - `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? ## 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)) 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); 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) private async Task CreateNpmrcFileAsync(string directoryName)
{ {
var fileName = Path.Combine(directoryName, ".npmrc"); 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(); var voloRegistry = await GetVoloRegistryAsync();
if (File.Exists(fileName)) 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 { export class DomInsertionService {
readonly inserted = new Set<number>(); readonly inserted = new Set<number>();
insertContent(contentStrategy: ContentStrategy) { insertContent<T extends HTMLScriptElement | HTMLStyleElement>(
contentStrategy: ContentStrategy<T>,
): T {
const hash = generateHash(contentStrategy.content); const hash = generateHash(contentStrategy.content);
if (this.inserted.has(hash)) return; if (this.inserted.has(hash)) return;
contentStrategy.insertElement(); const element = contentStrategy.insertElement();
this.inserted.add(hash); 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; abstract createElement(): T;
insertElement() { insertElement(): T {
const element = this.createElement(); const element = this.createElement();
this.contentSecurityStrategy.applyCSP(element); this.contentSecurityStrategy.applyCSP(element);
this.domStrategy.insertElement(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 { import {
CONTENT_SECURITY_STRATEGY,
CONTENT_STRATEGY, CONTENT_STRATEGY,
StyleContentStrategy,
ScriptContentStrategy,
DOM_STRATEGY, DOM_STRATEGY,
CONTENT_SECURITY_STRATEGY, ScriptContentStrategy,
StyleContentStrategy,
} from '../strategies'; } from '../strategies';
import { uuid } from '../utils'; import { uuid } from '../utils';
@ -26,8 +26,8 @@ describe('StyleContentStrategy', () => {
domStrategy.insertElement = jest.fn((el: HTMLScriptElement) => {}) as any; domStrategy.insertElement = jest.fn((el: HTMLScriptElement) => {}) as any;
const strategy = new StyleContentStrategy('', domStrategy, contentSecurityStrategy); const strategy = new StyleContentStrategy('', domStrategy, contentSecurityStrategy);
const element = strategy.createElement(); strategy.createElement();
strategy.insertElement(); const element = strategy.insertElement();
expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element); expect(contentSecurityStrategy.applyCSP).toHaveBeenCalledWith(element);
expect(domStrategy.insertElement).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())); beforeEach(() => (spectator = createService()));
afterEach(() => styleElements.forEach(element => element.remove())); afterEach(() => (document.head.innerHTML = ''));
describe('#insertContent', () => { describe('#insertContent', () => {
it('should be able to insert given content', () => { it('should be able to insert given content', () => {
@ -19,6 +19,11 @@ describe('DomInsertionService', () => {
expect(styleElements[0].textContent).toBe('.test {}'); 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', () => { it('should insert only once', () => {
expect(spectator.service.inserted.has(1437348290)).toBe(false); expect(spectator.service.inserted.has(1437348290)).toBe(false);
@ -37,9 +42,25 @@ describe('DomInsertionService', () => {
expect(spectator.service.inserted.has(1437348290)).toBe(true); expect(spectator.service.inserted.has(1437348290)).toBe(true);
}); });
it('should be able to insert given content', () => { it('should return inserted element', () => {
spectator.service.insertContent(CONTENT_STRATEGY.AppendStyleToHead('.test {}')); 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); 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", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve --open",
"build": "ng build", "build": "ng build",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",

2
templates/app/angular/start.ps1

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