diff --git a/_data/sidebars/lb4_sidebar.yml b/_data/sidebars/lb4_sidebar.yml index 61823a57b..94278a81e 100644 --- a/_data/sidebars/lb4_sidebar.yml +++ b/_data/sidebars/lb4_sidebar.yml @@ -98,6 +98,15 @@ children: url: Crafting-LoopBack-4.html output: 'web, pdf' +- title: 'Language-related Concepts' + url: Language-related-concepts.html + output: 'web, pdf' + children: + + - title: 'Mixin' + url: Mixin.html + output: 'web, pdf' + - title: 'Reference' url: Reference.html output: 'web, pdf' diff --git a/pages/en/lb4/Creating-components.md b/pages/en/lb4/Creating-components.md index 10df8badf..8e199c3b9 100644 --- a/pages/en/lb4/Creating-components.md +++ b/pages/en/lb4/Creating-components.md @@ -228,6 +228,66 @@ export class AuthenticationProvider { } ``` +## Extends Application with Mixin + +When binding a component to an app, you may want to extend the app with the component's +properties and methods. +This can be achieved by using mixins. + +If you are not familiar with the mixin concept, check [Mixin](Mixin.htm) to learn more. + +An example of how a mixin leverages component would be `RepositoryMixin`: +Suppose an app has multiple components with repositories bound to each of them, +you can use function `RepositoryMixin` to mount those repositories to application level context. + +The following snippet is an abbreviated function +[`RepositoryMixin`](https://github.com/strongloop/loopback-next/blob/master/packages/repository/src/repository-mixin.ts): + +{% include code-caption.html content="mixins/src/repository-mixin.ts" %} +```js +export function RepositoryMixin>(superClass: T) { + return class extends superClass { + constructor(...args: any[]) { + super(...args); + ... ... + // detect components attached to the app + if (this.options.components) { + for (const component of this.options.components) { + this.mountComponentRepository(component); + } + } + } + } + mountComponentRepository(component: Class) { + const componentKey = `components.${component.name}`; + const compInstance = this.getSync(componentKey); + + // register a component's repositories in the app + if (compInstance.repositories) { + for (const repo of compInstance.repositories) { + this.repository(repo); + } + } + } +} +``` + +Then you can extend the app with repositories in a component: + +{% include code-caption.html content="index.ts" %} + +```js +import {RepositoryMixin} from 'mixins/src/repository-mixin'; +import {Application} from '@loopback/core'; +import {FooComponent} from 'components/src/Foo'; + +class AppWithRepoMixin extends RepositoryMixin(Application) {}; +let app = new AppWithRepoMixin({components: [FooComponent]}); + +// `app.find` returns all repositories in FooComponent +app.find('repositories.*'); +``` + ## Configuring components More often than not, the component may want to offer different value providers depending on the configuration. For example, a component providing Email API may offer different transports (stub, SMTP, etc.). diff --git a/pages/en/lb4/Language-related-concepts.md b/pages/en/lb4/Language-related-concepts.md new file mode 100644 index 000000000..b237688f7 --- /dev/null +++ b/pages/en/lb4/Language-related-concepts.md @@ -0,0 +1,13 @@ +--- +lang: en +title: 'Language-related Concepts' +keywords: LoopBack 4.0, LoopBack 4 +tags: +sidebar: lb4_sidebar +permalink: /doc/en/lb4/Language-related-concepts.html +summary: +--- + +A module that exports JavaScript/TypeScript concept related functions. + +- [**Mixin**](Mixin.html): Add properties and methods to a class. \ No newline at end of file diff --git a/pages/en/lb4/Mixin.md b/pages/en/lb4/Mixin.md new file mode 100644 index 000000000..aafe87b1f --- /dev/null +++ b/pages/en/lb4/Mixin.md @@ -0,0 +1,124 @@ +--- +lang: en +title: 'Mixin' +keywords: LoopBack 4.0, LoopBack 4 +tags: +sidebar: lb4_sidebar +permalink: /doc/en/lb4/Mixin.html +summary: +--- + +It is a commonly used JavaScript/TypeScript strategy to extend a class with new properties and methods. + +A good approach to apply mixins is defining them as sub-class factories. +Then declare the new mixed class as: + +```js +class MixedClass extends MixinFoo(MixinBar(BaseClass)) {}; +``` + +Check article [real mixins with javascript classes](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/) +to learn more about it. + +## Define Mixin + +By defining a mixin, you create a mixin function that takes in a base class, +and returns a new class extending the base class with new properties and methods mixed to it. + +For example you have a simple controller which only has a greeter function prints out 'hi!': + +{% include code-caption.html content="Controllers/myController.ts" %} + +```ts +class SimpleController { + constructor() { + + } + greet() { + console.log('hi!'); + } +} +``` + +Now let's add mixins to it: + +- A time stamp mixin that adds a property `createdAt` to a record when a +controller instance is created. + +- A logger mixin to provide logging tools. + +Define mixin `timeStampMixin`: + +{% include code-caption.html content="Mixins/timeStampMixin.ts" %} + +```ts +import {Class} from "@loopback/repository"; + +export function timeStampMixin> (baseClass: T) { + return class extends baseClass { + // add a new property `createdAt` + public createdAt: Date; + constructor(...args: any[]) { + super(args); + this.createTS = new Date(); + } + printTimeStamp() { + console.log('Instance created at: ' + this.createdAt); + } + } +} +``` + +And define mixin `loggerMixin`: + +{% include code-caption.html content="Mixins/loggerMixin.ts" %} + +```ts +import {Class} from "@loopback/repository"; + +function loggerMixin> (baseClass: T) { + return class extends baseClass { + // add a new method `log()` + log(str: string) { + console.log('Prints out a string: ' + str); + }; + } +} +``` + +Now you can extend `SimpleController` with the two mixins: + +{% include code-caption.html content="Controllers/myController.ts" %} + +```ts +import {timeStampMixin} from 'Mixins/timeStampMixin.ts'; +import {loggerMixin} from 'Mixins/loggerMixin.ts'; + +class SimpleController { + constructor() { + + } + greet() { + console.log('hi!'); + } +} + +class AdvancedController extends loggerMixin(timeStampMixin(SimpleController)) {}; + +// verify new method and property are added to `AdvancedController`: +let aControllerInst = new AdvancedController(); +aControllerInst.printTimeStamp(); +// print out: Instance created at: Tue Oct 17 2017 22:28:49 GMT-0400 (EDT) +aControllerInst.logger('hello world!'); +// print out: Prints out a string: hello world! +``` + +## References + +Here are some articles explaining ES2015 and TypeScript mixins in more details: + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Mix-ins + +- http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/ + +- https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html \ No newline at end of file diff --git a/pages/en/lb4/Using-components.md b/pages/en/lb4/Using-components.md index 92e933a6d..e06573c2d 100644 --- a/pages/en/lb4/Using-components.md +++ b/pages/en/lb4/Using-components.md @@ -35,4 +35,4 @@ In general, components can contribute the following items: In the future (before the GA release), components will be able to contribute additional items: - Models - - [Repositories](Repositories.html) + - [Repositories](Repositories.html) \ No newline at end of file