Skip to content

Commit

Permalink
💥 ViewModel not recognize postConstruct any more, instead Store would
Browse files Browse the repository at this point in the history
  • Loading branch information
kuitos committed Sep 19, 2018
1 parent ddb4597 commit 5213519
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 63 deletions.
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class App extends Component {
* `inject(ViewModel, 10, 'kuitos') viewModel;` initialized with static parameters for `ViewModel` constrcutor.
* `inject(ViewModel, instance => instance.router.props) viewModel;` initialized with dynamic instance props for `ViewModel` constructor.

**Notice that all the `Store` decorated classes are singleton and that was the default behavior in mmlpx di system**, if you wanna make your state live around the component lifecycle, always decorated them with `ViewModel` decorator.
**Notice that all the `Store` decorated classes are singleton by default so that the dynamic initial params injection would be ignored by di system**, if you wanna make your state live around the component lifecycle, always decorated them with `ViewModel` decorator.

##### instantiate

Expand Down Expand Up @@ -275,9 +275,16 @@ class UserStore {
const users = await this.loader.getUsers();
this.users = users;
}

@postConstruct
onInit() {
observe(this, 'users', () => {})
}
}
```

*Method decorated by `postConstruct` will be invoked when `Store` initialized by DI system.*

### ViewModel

Page interaction logic definition, live around the component lifecycle, `ViewModel` instance can not be stored in ioc container.
Expand Down Expand Up @@ -305,18 +312,10 @@ class AppViewModel {
@action
setLoading(loading: boolean) {
this.loading = loading;
}

@postConstruct
async onInit() {
await this.userStore.loadUsers();
this.setLoading(false);
}
}
}
```

*Method decorated by `postConstruct` will be invoked when `ViewModel` initialized by DI system.*

### Loader

Data accessor for remote or local data fetching, converting the data structure to match definited models.
Expand Down
54 changes: 27 additions & 27 deletions src/core/dependency-inject/decorators/__tests__/inject-Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,70 @@
* @since 2017-08-26
*/

import { SinonSpy, spy } from 'sinon';
import inject from '../inject';
import postConstruct from '../postConstruct';
import Store from '../Store';

let StoreClass: any = null;
let initSpy: SinonSpy;
let constructorSpy: SinonSpy;

beforeEach(() => {

initSpy = spy();
constructorSpy = spy();

@Store
class Klass {

name: string;

@postConstruct
empty = null;
constructor() {
this.name = 'kuitos';
constructorSpy();
}

constructor(name: string) {
this.name = name;
@postConstruct
onInit() {
initSpy(this.name);
}
}

StoreClass = Klass;
});

test('inject store with init params', () => {
test('inject store with dynamic params will throw exception', () => {

class Controller {

name = 'kuitos';

@inject(StoreClass, 'kuitos')
store: any = null;

changeStore() {
this.store.name = 'x';
}
}

// eslint-disable-next-line no-unused-vars
const controller = new Controller();

expect(controller.store.name).toBe('kuitos');
controller.changeStore();
expect(controller.store.name).toBe('x');
expect(() => controller.store).toThrow(SyntaxError);
});

test('inject store with dynamic params', () => {

function init(this: any) {
return [this.name];
}
test('postConstruct should invoke after store constructor while injecting', () => {

class Controller {

name = 'kuitos';

@inject(StoreClass, init)
@inject(StoreClass)
store: any = null;
}

// eslint-disable-next-line no-unused-vars
const controller = new Controller();
// tslint:disable-next-line
const unused = (controller.store.name, (controller.store.name));
expect(constructorSpy.called).toBeTruthy();
expect(constructorSpy.callCount).toBe(1);
expect(initSpy.calledAfter(constructorSpy)).toBeTruthy();
expect(initSpy.calledWith(controller.store.name)).toBeTruthy();

expect(controller.store.name).toBe('kuitos');
});

test('auto inject through field type definition', () => {
test('auto inject through typescript way', () => {

class UserStore {
age = 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@

import { spy } from 'sinon';
import inject from '../inject';
import postConstruct from '../postConstruct';
import ViewModel from '../ViewModel';

let spyFn: any;
let onInitSpy: any;
let ViewModelClass: any = null;

beforeEach(() => {

spyFn = spy();
onInitSpy = spy();

@ViewModel
class Klass {
Expand All @@ -29,17 +26,12 @@ beforeEach(() => {
this.age = age;
spyFn(name);
}

@postConstruct
onInit() {
onInitSpy(this.name);
}
}

ViewModelClass = Klass;
});

test('injected viewModel will only construct at initial period and postConstruct will exec after', () => {
test('injected viewModel will only construct at initial period', () => {

expect(spyFn.called).toBe(false);

Expand All @@ -55,7 +47,6 @@ test('injected viewModel will only construct at initial period and postConstruct
const unused = (controller.viewModel, controller.viewModel);
expect(spyFn.called).toBe(true);
expect(spyFn.callCount).toBe(1);
expect(onInitSpy.calledAfter(spyFn)).toBe(true);

});

Expand All @@ -73,7 +64,6 @@ test('inject viewModel with static params', () => {

expect(controller.viewModel.name).toBe(name);
expect(spyFn.calledWith(name)).toBe(true);
expect(onInitSpy.calledWith(name)).toBe(true);

});

Expand Down
22 changes: 12 additions & 10 deletions src/core/dependency-inject/initializers/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@
* @since 2017-09-13
*/

import { flatten, isFunction } from 'lodash';
import Injector, { Scope } from '../Injector';
import { IMmlpx, modelNameSymbol } from '../meta';
import execPostConstruct from './execPostConstruct';

export default function initialize<T extends IMmlpx<T>>(this: T, injector: Injector, Store: T, ...args: any[]) {

let constructorParams = args;

// if the first argument is a function, we can initialize it with the invoker instance `this`
if (isFunction(args[0])) {
constructorParams = flatten([args[0].call(this, this)]);
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
console.warn('It looks like you are use Store with a non-singleton mode, which was deprecated!!', Store.name);
// store should not dynamic initialize while injecting
if (args && args.length) {
if (process.env.NODE_ENV === 'test') {
throw new SyntaxError(`${Store.name}: As a singleton recipe, you should not instantiate Store with dynamic arguments!`);
} else if (process.env.NODE_ENV !== 'production') {
/* istanbul ignore next */
console.error(`${Store.name}: As a singleton recipe, you should not instantiate Store with dynamic arguments!`);
}
}

const name = Store[modelNameSymbol];
const store = injector.get(Store, { scope: Scope.Singleton, name }, ...constructorParams);
const store = injector.get(Store, { scope: Scope.Singleton, name });

execPostConstruct(store);

return store;
}
6 changes: 1 addition & 5 deletions src/core/dependency-inject/initializers/viewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import { flatten, isFunction } from 'lodash';
import Injector, { Scope } from '../Injector';
import { IMmlpx, modelNameSymbol } from '../meta';
import execPostConstruct from './execPostConstruct';

export default function initialize<T extends IMmlpx<T>>(this: T, injector: Injector, ViewModel: T, ...args: any[]) {

Expand All @@ -19,9 +18,6 @@ export default function initialize<T extends IMmlpx<T>>(this: T, injector: Injec
}

const name = ViewModel[modelNameSymbol];
const viewModel = injector.get(ViewModel, { scope: Scope.Prototype, name }, ...constructorParams);

execPostConstruct(viewModel);

return viewModel;
return injector.get(ViewModel, { scope: Scope.Prototype, name }, ...constructorParams);
}

0 comments on commit 5213519

Please sign in to comment.