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 8, 2023
1 parent ae46594 commit c5f037c
Show file tree
Hide file tree
Showing 46 changed files with 2,608 additions and 1,307 deletions.
9 changes: 9 additions & 0 deletions integration/app/app-browser.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApplicationConfig } from '@angular/platform-browser';
import { provideAnimations } from '@angular/platform-browser/animations';

import { appConfig } from './app.config';

// TODO: use `mergeApplicationConfig` in v16.
export const appBrowserConfig: ApplicationConfig = {
providers: [provideAnimations(), ...appConfig.providers]
};
26 changes: 0 additions & 26 deletions integration/app/app-routing.module.ts

This file was deleted.

9 changes: 9 additions & 0 deletions integration/app/app-server.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApplicationConfig } from '@angular/platform-browser';
import { provideNoopAnimations } from '@angular/platform-browser/animations';

import { appConfig } from './app.config';

// TODO: use `mergeApplicationConfig` in v16.
export const appServerConfig: ApplicationConfig = {
providers: [provideNoopAnimations(), ...appConfig.providers]
};
19 changes: 0 additions & 19 deletions integration/app/app.browser.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
54 changes: 54 additions & 0 deletions integration/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { APP_ID } from '@angular/core';
import { provideRouter } from '@angular/router';
import { ApplicationConfig } from '@angular/platform-browser';
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 appConfig: ApplicationConfig = {
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] })
)
]
};
23 changes: 0 additions & 23 deletions integration/app/app.module.ts

This file was deleted.

12 changes: 0 additions & 12 deletions integration/app/app.server.module.ts

This file was deleted.

13 changes: 3 additions & 10 deletions integration/main.server.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
import { enableProdMode } from '@angular/core';

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

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

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

import { AppModule } from './app/app.module';
import { AppComponent } from './app/app.component';
import { appBrowserConfig } from './app/app-browser.config';
import { environment } from './environments/environment';

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

platformBrowserDynamic().bootstrapModule(AppModule);
bootstrapApplication(AppComponent, appBrowserConfig);
46 changes: 33 additions & 13 deletions integration/server.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
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 { APP_ID_VALUE, AppComponent, appServerConfig } 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)
// TODO: use `ngExpressEngine({bootstrap})` in v16.
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 +59,10 @@ export function app() {

// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, {
res.render('index', {
req,
providers: [
...appServerConfig.providers,
{ provide: APP_BASE_HREF, useValue: req.baseUrl },
{ provide: REQUEST, useValue: req },
{ provide: RESPONSE, useValue: res }
Expand Down
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
Loading

0 comments on commit c5f037c

Please sign in to comment.