Skip to content

Commit

Permalink
mixin and components (#491)
Browse files Browse the repository at this point in the history
* mixin and components

* Add mixin doc

* Apply feedback
  • Loading branch information
Janny authored Oct 30, 2017
1 parent 0f8ab82 commit 38760d5
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 1 deletion.
9 changes: 9 additions & 0 deletions _data/sidebars/lb4_sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
60 changes: 60 additions & 0 deletions pages/en/lb4/Creating-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends Class<any>>(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<any>) {
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.).
Expand Down
13 changes: 13 additions & 0 deletions pages/en/lb4/Language-related-concepts.md
Original file line number Diff line number Diff line change
@@ -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.
124 changes: 124 additions & 0 deletions pages/en/lb4/Mixin.md
Original file line number Diff line number Diff line change
@@ -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<T extends Class<any>> (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<T extends Class<any>> (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
2 changes: 1 addition & 1 deletion pages/en/lb4/Using-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit 38760d5

Please sign in to comment.