Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): introduce support for Analog Server Components #1518

Merged
merged 1 commit into from
Dec 20, 2024

Conversation

brandonroberts
Copy link
Member

@brandonroberts brandonroberts commented Dec 20, 2024

PR Checklist

Closes #989

What is the new behavior?

Introduces Analog Server Components which are inspired by/similar to Nuxt Server Components and Astro Islands in that the component is only rendered on the server, with its HTML output being sent back to the client.

This is not the same as Incremental Hydration in Angular, where the client-side JS is loaded for the component(s) after the hydration trigger happens. The JS for these components is never sent to the client.

  • NOTES
    • No client-side interactivity: Rendered links will cause a page to reload upon clicking. We do have a directive that can be used to intercept these clicks and perform navigation, but that's not part of this initial effort. These components render heavy HTML content on the server without sending the JavaScript to the client.
    • Angular components and Analog SFCs are supported
    • Styles are supported

Examples

The server components are placed in the src/server/components directory without any trailing names like .component.ts:

// src/server/components/hello.ts
import { ChangeDetectionStrategy, Component, computed } from '@angular/core';
import { readFileSync } from 'node:fs';

import {
  injectStaticOutputs,
  injectStaticProps,
} from '@analogjs/router/server';

@Component({
  selector: 'app-hello',
  template: `
    <h3>Hello From the Server</h3>

    <p>Props: {{ json() }}</p>

    <p>Time: {{ Date.now().toString() }}</p>

    <p>readFileSync: {{ readFileSync }}</p>  
  `,
  styles: `
    h3 {
      color: blue;
    }
  `,
})
export default class HelloComponent {
  Date = Date;
  props = injectStaticProps();
  outputs = injectStaticOutputs<{ loaded: boolean }>();
  json = computed(() => JSON.stringify(this.props));
  readFileSync = !!readFileSync;

  ngOnInit() {
    this.outputs.set({ loaded: true });
  }
}
  • The injectStaticProps function provides the props sent from the client
  • The injectStaticOutputs provides a way to communicate "outputs" back to the client. They must be serializable as JSON.

For usage on the client-side, a ServerOnly component import is provided:

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { ServerOnly } from '@analogjs/router';

@Component({
  imports: [ServerOnly],
  template: `
    <h2>Client Component</h2>

    <ServerOnly component="hello" [props]="props()" (outputs)="log($event)" />

    <p>
      <button (click)="update()">Update</button>
    </p>
  `,
})
export default class ClientComponent {
  props = signal({ name: 'Brandon', count: 0 });

  update() {
    this.props.update((data) => ({ ...data, count: ++data.count }));
  }

  log($event: object) {
    console.log({ outputs: $event });
  }
}
  • The component input points to the server-only component path, minus the file extension.
  • The props input should be JSON serializable data.
  • The outputs are received from the server-only component.

Server-only components can also be used as top-level routes:

import { RouteMeta } from '@analogjs/router';

import { ServerOnly } from '@analogjs/router';

export const routeMeta: RouteMeta = {
  data: {
    component: 'hello',
  },
};

export default ServerOnly;

Setup

Add the server components path to the tsconfig.app.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "types": [],
    "target": "ES2022",
    "useDefineForClassFields": false
  },
  "files": ["src/main.ts", "src/main.server.ts"],
  "include": [
    "src/**/*.d.ts",
    "src/app/pages/**/*.page.ts",
    "src/server/components/**/*.ts",
    "src/server/middleware/**/*.ts"
  ],
  "exclude": ["**/*.test.ts", "**/*.spec.ts"]
}

Use the new render function in the main.server.ts:

import 'zone.js/node';
import '@angular/platform-server/init';
import { render } from '@analogjs/router/server';

import { config } from './app/app.config.server';
import { AppComponent } from './app/app.component';

export default render(AppComponent, config);

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Demo links:

https://deploy-preview-1518--analog-app.netlify.app/client - Client w/server component

https://deploy-preview-1518--analog-app.netlify.app/server - Server component as route

https://deploy-preview-1518--analog-app.netlify.app/goodbye - Analog SFCs

Additional uses cases and feedback can be provided here: #1517

[optional] What gif best describes this PR or how it makes you feel?

Copy link

netlify bot commented Dec 20, 2024

Deploy Preview for analog-blog ready!

Name Link
🔨 Latest commit 6103bb3
🔍 Latest deploy log https://app.netlify.com/sites/analog-blog/deploys/6765b1fbfc62ea0008516956
😎 Deploy Preview https://deploy-preview-1518--analog-blog.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Dec 20, 2024

Deploy Preview for analog-docs ready!

Name Link
🔨 Latest commit 6103bb3
🔍 Latest deploy log https://app.netlify.com/sites/analog-docs/deploys/6765b1fb710fa60008ecc3f5
😎 Deploy Preview https://deploy-preview-1518--analog-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Dec 20, 2024

Deploy Preview for analog-app ready!

Name Link
🔨 Latest commit 6103bb3
🔍 Latest deploy log https://app.netlify.com/sites/analog-app/deploys/6765b1fb29cd9300084cdc7b
😎 Deploy Preview https://deploy-preview-1518--analog-app.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

netlify bot commented Dec 20, 2024

Deploy Preview for analog-ng-app ready!

Name Link
🔨 Latest commit 6103bb3
🔍 Latest deploy log https://app.netlify.com/sites/analog-ng-app/deploys/6765b1fbbcd5780008f4827c
😎 Deploy Preview https://deploy-preview-1518--analog-ng-app.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@brandonroberts brandonroberts merged commit 44289b0 into beta Dec 20, 2024
24 checks passed
@brandonroberts brandonroberts deleted the feat-server-only-components branch December 20, 2024 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature: Server Only Components
1 participant