Skip to content

Commit

Permalink
feat(store): add standalone features
Browse files Browse the repository at this point in the history
  • Loading branch information
arturovt committed Jul 7, 2023
1 parent 1541078 commit 4c76702
Show file tree
Hide file tree
Showing 38 changed files with 786 additions and 274 deletions.
26 changes: 0 additions & 26 deletions integration/app/app-routing.module.ts

This file was deleted.

31 changes: 18 additions & 13 deletions integration/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { ComponentFixtureAutoDetect, discardPeriodicTasks } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { take } from 'rxjs/operators';
import { Store } from '@ngxs/store';
import { Store, provideStore } from '@ngxs/store';
import { withNgxsFormPlugin } from '@ngxs/form-plugin';

import { AppComponent } from '@integration/app.component';
import { AppModule } from '@integration/app.module';
import { Pizza, Todo } from '@integration/store/todos/todos.model';
import { AppComponent } from './app.component';
import { Pizza, Todo } from './store/todos/todos.model';
import { TodosState } from './store/todos/todos.state';
import { TodoState } from './store/todos/todo/todo.state';

describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
Expand All @@ -22,8 +22,8 @@ describe('AppComponent', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppModule, RouterTestingModule, FormsModule, ReactiveFormsModule],
providers: [{ provide: ComponentFixtureAutoDetect, useValue: true }]
imports: [RouterTestingModule],
providers: [provideStore([TodosState, TodoState], withNgxsFormPlugin())]
});

fixture = TestBed.createComponent(AppComponent);
Expand Down Expand Up @@ -54,7 +54,10 @@ describe('AppComponent', () => {
});
});

it('should set toppings using form control', fakeAsync(() => {
it('should set toppings using form control', fakeAsync(async () => {
fixture.detectChanges();
await fixture.whenStable();

component.pizzaForm.patchValue({ toppings: 'oli' });
tick(200);
let flag = false;
Expand All @@ -80,12 +83,15 @@ describe('AppComponent', () => {
expect(flag).toBe(true);
}));

it('should set toppings prefix', fakeAsync(() => {
it('should set toppings prefix', fakeAsync(async () => {
fixture.detectChanges();
await fixture.whenStable();

component.pizzaForm.patchValue({ toppings: 'cheese' });
tick(200);
tick(100);
component.onPrefix();
let flag = false;
tick(200);
tick(100);

component.pizza$.pipe(take(1)).subscribe((pizza: Pizza) => {
flag = true;
Expand All @@ -95,7 +101,6 @@ describe('AppComponent', () => {
});

expect(flag).toBe(true);
discardPeriodicTasks();
}));

it('should load data in pizza form', () => {
Expand Down
18 changes: 15 additions & 3 deletions integration/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { FormArray, FormBuilder, FormControl } from '@angular/forms';
import { Component, OnInit } from '@angular/core';
import {
FormArray,
FormBuilder,
FormControl,
FormsModule,
ReactiveFormsModule
} from '@angular/forms';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { Store } from '@ngxs/store';
import { NgxsFormDirective } from '@ngxs/form-plugin';
import { Observable } from 'rxjs';

import { TodoState } from '@integration/store/todos/todo/todo.state';
Expand All @@ -11,7 +20,10 @@ import { Extras, Pizza, Todo } from '@integration/store/todos/todos.model';

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
templateUrl: './app.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [CommonModule, RouterModule, FormsModule, ReactiveFormsModule, NgxsFormDirective]
})
export class AppComponent implements OnInit {
readonly allExtras: Extras[] = [
Expand Down
23 changes: 0 additions & 23 deletions integration/app/app.module.ts

This file was deleted.

4 changes: 2 additions & 2 deletions integration/main.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ if (environment.production) {
enableProdMode();
}

export { AppServerModule } from './app/app.server.module';
export { renderModuleFactory } from '@angular/platform-server';
export { AppComponent } from './app/app.component';
export { APP_ID_VALUE, SHARED_PLATFORM_PROVIDERS } from './shared-platform-providers';
10 changes: 7 additions & 3 deletions integration/main.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideAnimations } from '@angular/platform-browser/animations';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { AppComponent } from './app/app.component';
import { SHARED_PLATFORM_PROVIDERS } from './shared-platform-providers';

if (environment.production) {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule);
bootstrapApplication(AppComponent, {
providers: [provideAnimations(), ...SHARED_PLATFORM_PROVIDERS]
});
45 changes: 32 additions & 13 deletions integration/server.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
import 'zone.js/node';

import { readFile } from 'fs/promises';
import { join } from 'path';
import * as express from 'express';
import { Provider } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { renderApplication } from '@angular/platform-server';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';

import { AppServerModule } from './main.server';
import { AppComponent, APP_ID_VALUE, SHARED_PLATFORM_PROVIDERS } from './main.server';

interface RenderOptions {
req: express.Request;
providers: Provider[];
}

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const distFolder = join(process.cwd(), 'dist-integration');
const indexHtml = existsSync(join(distFolder, 'index.original.html'))
? 'index.original.html'
: 'index';
let indexHtmlContent: string | null = null;

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModule
}) as any
async (
path: string,
options: object,
callback: (error: Error | null, content: string) => void
) => {
const { req, providers } = options as RenderOptions;

indexHtmlContent ||= await readFile(path, { encoding: 'utf-8' });

const html = await renderApplication(AppComponent, {
providers,
appId: APP_ID_VALUE,
document: indexHtmlContent,
url: `${req.baseUrl}${req.url}`
});

callback(null, html);
}
);

server.set('view engine', 'html');
Expand All @@ -40,9 +58,10 @@ export function app() {

// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, {
res.render('index', {
req,
providers: [
...SHARED_PLATFORM_PROVIDERS,
{ provide: APP_BASE_HREF, useValue: req.baseUrl },
{ provide: REQUEST, useValue: req },
{ provide: RESPONSE, useValue: res }
Expand Down
50 changes: 50 additions & 0 deletions integration/shared-platform-providers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { APP_ID } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideStore, withNgxsOptions } from '@ngxs/store';
import { withNgxsFormPlugin } from '@ngxs/form-plugin';
import { withNgxsLoggerPlugin } from '@ngxs/logger-plugin';
import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin';
import { withNgxsRouterPlugin } from '@ngxs/router-plugin';
import { withNgxsStoragePlugin } from '@ngxs/storage-plugin';

import { TodosState } from '@integration/store/todos/todos.state';
import { TodoState } from '@integration/store/todos/todo/todo.state';
import { TODOS_STORAGE_KEY } from '@integration/store/todos/todos.model';

import { environment } from './environments/environment';

export const APP_ID_VALUE = 'integration-app';

export const SHARED_PLATFORM_PROVIDERS = [
{ provide: APP_ID, useValue: APP_ID_VALUE },

provideRouter([
{ path: '', pathMatch: 'full', redirectTo: '/list' },
{
path: 'list',
loadChildren: () => import('@integration/list/list.module').then(m => m.ListModule)
},
{
path: 'detail',
loadChildren: () => import('@integration/detail/detail.module').then(m => m.DetailModule)
},
{
path: 'counter',
loadChildren: () =>
import('@integration/counter/counter.module').then(m => m.CounterModule)
}
]),

provideStore(
[TodosState, TodoState],
withNgxsOptions({
developmentMode: !environment.production,
selectorOptions: {}
}),
withNgxsFormPlugin(),
withNgxsLoggerPlugin({ logger: console, collapsed: false, disabled: true }),
withNgxsReduxDevtoolsPlugin({ disabled: environment.production }),
withNgxsRouterPlugin(),
withNgxsStoragePlugin({ key: [TODOS_STORAGE_KEY] })
)
];
1 change: 1 addition & 0 deletions integration/tsconfig.editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "./tsconfig.json",
"include": ["**/*.ts"],
"compilerOptions": {
"module": "esnext",
"types": ["jest", "node"]
}
}
3 changes: 2 additions & 1 deletion integration/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false
},
"angularCompilerOptions": {
"strictInjectionParameters": true,
Expand Down
17 changes: 16 additions & 1 deletion packages/devtools-plugin/src/devtools.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NgModule, ModuleWithProviders, InjectionToken } from '@angular/core';
import { NGXS_PLUGINS } from '@ngxs/store';
import { NGXS_PLUGINS, NgxsStoreFeature, withNgxsPlugin } from '@ngxs/store';

import { NgxsDevtoolsOptions, NGXS_DEVTOOLS_OPTIONS } from './symbols';
import { NgxsReduxDevtoolsPlugin } from './devtools.plugin';
Expand Down Expand Up @@ -39,3 +39,18 @@ export class NgxsReduxDevtoolsPluginModule {
};
}
}

export function withNgxsReduxDevtoolsPlugin(options?: NgxsDevtoolsOptions): NgxsStoreFeature {
return [
withNgxsPlugin(NgxsReduxDevtoolsPlugin),
{
provide: USER_OPTIONS,
useValue: options
},
{
provide: NGXS_DEVTOOLS_OPTIONS,
useFactory: devtoolsOptionsFactory,
deps: [USER_OPTIONS]
}
];
}
2 changes: 1 addition & 1 deletion packages/devtools-plugin/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { NgxsReduxDevtoolsPluginModule } from './devtools.module';
export { NgxsReduxDevtoolsPluginModule, withNgxsReduxDevtoolsPlugin } from './devtools.module';
export { NgxsReduxDevtoolsPlugin } from './devtools.plugin';
export * from './symbols';
4 changes: 2 additions & 2 deletions packages/form-plugin/src/directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
UpdateFormValue
} from './actions';

@Directive({ selector: '[ngxsForm]' })
export class FormDirective implements OnInit, OnDestroy {
@Directive({ selector: '[ngxsForm]', standalone: true })
export class NgxsFormDirective implements OnInit, OnDestroy {
@Input('ngxsForm')
path: string = null!;

Expand Down
15 changes: 9 additions & 6 deletions packages/form-plugin/src/form.module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { NGXS_PLUGINS } from '@ngxs/store';
import { NGXS_PLUGINS, NgxsStoreFeature, withNgxsPlugin } from '@ngxs/store';

import { NgxsFormPlugin } from './form.plugin';
import { ReactiveFormsModule } from '@angular/forms';
import { FormDirective } from './directive';
import { NgxsFormDirective } from './directive';

@NgModule({
imports: [ReactiveFormsModule],
declarations: [FormDirective],
exports: [FormDirective]
imports: [NgxsFormDirective],
exports: [NgxsFormDirective]
})
export class NgxsFormPluginModule {
static forRoot(): ModuleWithProviders<NgxsFormPluginModule> {
Expand All @@ -23,3 +22,7 @@ export class NgxsFormPluginModule {
};
}
}

export function withNgxsFormPlugin(): NgxsStoreFeature {
return [withNgxsPlugin(NgxsFormPlugin)];
}
4 changes: 2 additions & 2 deletions packages/form-plugin/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { NgxsFormPluginModule } from './form.module';
export { NgxsFormPluginModule, withNgxsFormPlugin } from './form.module';
export { NgxsFormPlugin } from './form.plugin';
export { FormDirective as ɵFormDirective } from './directive';
export { NgxsFormDirective } from './directive';
export * from './actions';
Loading

0 comments on commit 4c76702

Please sign in to comment.