Universal Support
crebuh opened this issue · 26 comments
Hy guys,
great module! Are you planning to implement a way to use the module also on the server side.
So that the cookies are read from the intial request captured by node?
Hey there :)
We did not plan on doing that. However, if you have some experience with this and would like to submit a pull request, we’d be very happy to accept it.
If you need that functionality right now, check out the ngx-cookie package. It already has a backend service.
Cheers from Berlin
Ok what a pity. But I will have a look into it.
The question is have can I easily extend your cookieService. I see no way in doing something like:
MyBackendService extends CookieService
so that I can use the DI of Angular to provide MyBackendService on the server-side and CookieService on the client-side like e.g.
providers: [ { provide: CookieService, useClass: MyBackendService } ],
Where do you see that ngx-cookie is supporting the universal approach? The old module did, but it is not compatible with ng4.
Cheers from Austria
Currently the service has a strong dependency on document.cookie. As I see it an universal cookie service would have to abstract from it and use getter/setter for the cookie-string and instead work with the request object on the backend. That would require some refactoring and just extending the service as it is would not be feasible
We got the idea about the ngx-cookie package’s support from this file: ngx-cookie/src/cookie-backend.service.ts. We didn’t actually test it, though...
export * from './cookie-service/cookie.service';
is throwing error on node (angular universal)
node_modules/ngx-cookie-service/index.js:1
(function (exports, require, module, __filename, __dirname) { export * from './cookie-service/cookie.service';
SyntaxError: Unexpected token export
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:152:10)
at Module._compile (module.js:624:28)
at Object.Module._extensions..js (module.js:671:10)
at Module.load (module.js:573:32)
at tryModuleLoad (module.js:513:12)
at Function.Module._load (module.js:505:3)
at Module.require (module.js:604:17)
at require (internal/module.js:11:18)
at Object. (/Users/usmanali/projects/sivvi-server/dist/server.js:1:120927)
TypeError: Cannot read property 'create' of undefined
at /Users/usmanali/projects/sivvi-server/node_modules/@angular/core/bundles/core.umd.js:5531:78
at ZoneDelegate.invoke (/Users/usmanali/projects/sivvi-server/node_modules/zone.js/dist/zone-node.js:388:26)
at Object.onInvoke (/Users/usmanali/projects/sivvi-server/node_modules/@angular/core/bundles/core.umd.js:4792:33)
at ZoneDelegate.invoke (/Users/usmanali/projects/sivvi-server/node_modules/zone.js/dist/zone-node.js:387:32)
at Zone.run (/Users/usmanali/projects/sivvi-server/node_modules/zone.js/dist/zone-node.js:138:43)
at NgZone.run (/Users/usmanali/projects/sivvi-server/node_modules/@angular/core/bundles/core.umd.js:4609:69)
at PlatformRef.bootstrapModuleFactory (/Users/usmanali/projects/sivvi-server/node_modules/@angular/core/bundles/core.umd.js:5529:23)
at Object.renderModuleFactory (/Users/usmanali/projects/sivvi-server/node_modules/@angular/platform-server/bundles/platform-server.umd.js:1809:39)
at View.engine (/Users/usmanali/projects/sivvi-server/dist/server.js:1:1700)
at View.render (/Users/usmanali/projects/sivvi-server/node_modules/express/lib/view.js:135:8)
Hello everyone,
we are currently working with Vue and not with Angular. This will not affect this package's support, of course. However, I have no experience with Universal and that will probably not change in the foreseeable future. I am going to mark this as help-wanted
. If anyone would like to submit a pull request (with tests and an example environment), we would be very happy to accept it.
Cheers
Just for the record. I deleted two comments that have absolutely nothing to do with this issue. The author also opened an issue with the exact same text, see #35
It looks like there is already existed solution for both (client and server side), for those who are using Angular Universal: https://www.npmjs.com/package/@ngx-utils/cookies
Angular Universal
(function (exports, require, module, __filename, __dirname) { export * from './cookie-service/cookie.service';
SyntaxError: Unexpected token export
at new Script (vm.js:51:7)
at createScript (vm.js:136:10)
at Object.runInThisContext (vm.js:197:10)
at Module._compile (module.js:613:28)
at Object.Module._extensions..js (module.js:660:10)
at Module.load (module.js:561:32)
at tryModuleLoad (module.js:501:12)
at Function.Module._load (module.js:493:3)
at Module.require (module.js:593:17)
at require (internal/module.js:11:18)
Hi, use this
https://gist.github.com/bastienlemaitre/52bdf8146e368bf1759b883e742316a2
Then include it into your app.server.module.ts
import { AppModule } from './app.module';
import { CookieService } from 'ngx-cookie-service';
import { CookieServerService } from './cookie-server.service';
@NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule,
ModuleMapLoaderModule,
FlexLayoutServerModule
],
providers: [
{
provide: CookieService,
useClass: CookieServerService
}
],
bootstrap: [AppComponent]
})
export class AppServerModule { }
@bastienlemaitre
Can you provide more detail about the implementation as this will still lead to document undefined errors
ok, so we need a solution for the Document object, right?
We solved that creating a class and inyecting the RESPONSE and REQUEST, but we passes that in server.ts... so we can use this.request.headers.cookie and this.response.cookie. So this solution probably is not valid for all people.
in the server.ts where you render we passes that:
res.render('index', {
req: req,
res: res,
providers: [
{
provide: REQUEST, useValue: (req),
},
{
provide: RESPONSE, useValue: (res),
},
],
})
now in the cookie.server.ts we injected:
constructor(
@Optional() @Inject(REQUEST) private request: Request,
@Optional() @Inject(RESPONSE) private response: Response,
) {
}
And now we can implement the methods.
this.request.headers.cookie
this.response.cookie
hm, maybe there is a more simple solution?
I hope! haha our problem was that we need to use the cookie from the request and the response... Don't know other solution know...
That is our cookie-server.service.ts
import { Inject, Injectable, Optional } from '@angular/core'
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'
import { Request, Response } from 'express'
@Injectable({
providedIn: 'root',
})
export class CookieServerService {
constructor(
@Optional() @Inject(REQUEST) private request: Request,
@Optional() @Inject(RESPONSE) private response: Response,
) {
}
check(name: string): boolean {
const cookies = this.getAll()
return !!cookies[name]
}
delete(name: string, path?: string, domain?: string) {
this.response.cookie(name, '', {expires: new Date('1999-01-01'), path: path})
}
get(name: string): string {
const cookies = this.getAll()
return cookies[name] || ''
}
getAll() {
return typeof this.request.headers.cookie === 'string'
? decodeURIComponent(this.request.headers.cookie).split(';').reduce((cookies, cookie) => {
const val = cookie.split('=')
return {...cookies, [val[0].trim()]: val[1] }
}, {})
: {cookies: this.request.headers.cookie.map(decodeURIComponent)}
}
set(name: string, value: string, expires?: Date, path?: string, domain?: string, secure?: boolean, sameSite?: 'Lax' | 'None' | 'Strict') {
this.response.cookie(name, value, {expires, path, domain, secure, sameSite})
}
}
I hope that can help you! If you need something more tell me :)
Edit: Important Thing! this.response.cookie is encodeUri by default! hahaha i had problem with this xD
What I got from here - https://angular.io/guide/universal
That I only need to replace window.document
to DOCUMENT
Probably that is the solution, but i try to use DOCUMENT and i get this error ERROR Error: NotYetImplemented
Probably I don't used correctly hehe, i will try later to remplace my RESPONSE and REQUEST to DOCUMENT.
And one more thing... i think is better option create a server service than replace the original service with Document. If you replace in the original service you need to use isPlatformBroswser (or server) in every place.
Thanks for your Work @stevermeister ! :)
Used @DavidCMeier 's solution! Men that was clean! Almost lost hope since universal said there won't be any plans for cookie on server side. Only had to modify some functions and it was good to go! :)
I just realized that I'm not sure what is expected behavior for cookie service on server side.
@stevermeister I think we should implement this. I do see many projects using SSR. I might be able to implement this.
@pavankjadda we can think about own way, but are there any standard for server-side cookies?
@stevermeister honestly I have no idea about standard. But I can tell you this. In one of my recent NextJS (React) projects, I used modified version of my library react-cookie-service. With simple changes I was able to get it working. I can try to do that same here, test it with sample projects. Perhaps we can people in this thread to test it.
but I conceptually don't understand it.
we have cookies only on the client side.
how do you want to get them on the server?
obviously, we can return an async object and wait till we have data on the client, but it will be not very consistent
but I conceptually don't understand it.
we have cookies only on the client side.
how do you want to get them on the server?
obviously, we can return an async object and wait till we have data on the client, but it will be not very consistent
You don't have to. We still store them in browser but you just have to make sure document
is available before accessing it. Would you mind enabling GitHub discussions for this project? We can discuss it over there.
@stevermeister I created sample repo that demonstrates this. Clone the project and run as per instructions in Readme.md file
@pavankjadda I used your code and got "Cannot read property 'getHeader' of null"