Send initial data to a component in Angular Universal
Tags: tutorial, angular
Published: 2023-03-26 11:19:50

## Steps

Setting up a simple test angular universal, I wanted to try to send some initial data to the components, and have it server render all the data.  To test it I used postman as it does not run any JavaScript

To get up and running call `ng new univesaltest1` and to enable universal called `ng add @nguniversal/express-engine`

To have some components to test with run `ng generate component test --module app.module.ts` and `ng generate component test2 --module app.module.ts`

Then making a simple message model and an injection token

```
import { InjectionToken } from "@angular/core";

export class MessageModel {
  constructor(
    public message: string
  ) { }
}


// injecton token
export const MESSAGE = new InjectionToken<MessageModel>('message');
```
In the server.ts file set up the initial model and some query string code. Then provide the model using the MESSAGE injection token.

```
  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    const message:string = req.query['message']?.toString() ?? 'hello from server.ts';
    const messageModel = new MessageModel(message);

    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }, {provide: MESSAGE, useValue: messageModel}] });
  });

```

In the test.component.ts inject platform id and on the server run code inject the message model using the MESSAGE injection token. I need the transfer state as the rendering on the client will set the message, overriding the server rendered message. If I was to disable JavaScript in the browser, I do not need the transfer state. 

```
import { isPlatformServer } from '@angular/common';
import { Component, inject, Inject, PLATFORM_ID } from '@angular/core';
import { makeStateKey, StateKey, TransferState } from '@angular/platform-browser';
import { MESSAGE, MessageModel } from 'src/models/message.model';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.css']
})
export class TestComponent {
  // transfare state key
  private messageStateKey:StateKey<MessageModel> = makeStateKey<MessageModel>('message');

  public message: MessageModel = this.state.get(this.messageStateKey, new MessageModel('hello from test.component.ts'));

  constructor(@Inject(PLATFORM_ID) private platformId:object,  private state:TransferState)
  {
    // check if we are on the server
    if(isPlatformServer(this.platformId)) {
      const serverMessage = inject(MESSAGE);
      this.message = serverMessage;
      // set the message in the state
      this.state.set(this.messageStateKey, serverMessage);
    }
  }
}
```

To make it more interesting input the message model into test2 component using this code 

test.component.html

```
<div >
  <app-test2 [message]="message"></app-test2>
</div>
```

test2.component.ts

```
import { Component, Input } from '@angular/core';
import { MessageModel } from 'src/models/message.model';

@Component({
  selector: 'app-test2',
  templateUrl: './test2.component.html',
  styleUrls: ['./test2.component.css']
})
export class Test2Component {
     @Input() message: MessageModel = new MessageModel('hello from test2.component.ts');
}

```

test2.component.html

```
<p>{{message.message}}</p>
```
Now we are all set. To test the code start the universal version using `npm run dev:ssr`

Calling it postman and all the html with the model data is rendered on the server

Request: localhost:4200?message="Hello from query string"
```
<body>
	<app-root _nghost-sc3="" ng-version="15.2.4" ng-server-context="ssr">
		<app-test _ngcontent-sc3="" _nghost-sc2="">
			<div _ngcontent-sc2="">
				<app-test2 _ngcontent-sc2="" _nghost-sc1="" ng-reflect-message="[object Object]">
					<p _ngcontent-sc1="">"Hello from query string"</p>
				</app-test2>
			</div>
		</app-test>
	</app-root>
	<script src="runtime.js" type="module"></script>
	<script src="polyfills.js" type="module"></script>
	<script src="vendor.js" type="module"></script>
	<script src="main.js" type="module"></script>

	<script id="serverApp-state" type="application/json">
		{&q;message&q;:{&q;message&q;:&q;\&q;Hello from query string\&q;&q;}}
	</script>
</body>
```


## The Road

I did some mistakes when trying to get this to work. In the first try I tried to inject the Message model using the @Inject in the constructor. The problem then is that the client part of the application is not able to make sense of the provided message model. 

I go the error 

```
R3InjectorError(AppModule)[InjectionToken message -> InjectionToken message -> InjectionToken message]: 
  NullInjectorError: No provider for InjectionToken message!
```
Trying to provide the injection token in the app.module.ts using 

```
  providers: [{provide: MESSAGE, useValue: null }],
```
and then

```
 @Inject(MESSAGE) private messageModel:MessageModel
```
The client app model overrides the provided message with null. So you could se the message blink before the client set it to null. 

The solution was to not try to mix the server injected code with the clint part of the code.