Universal: Rendering Document Error: TypeError: undefined is not a function or str.replace is not a function

Created on 15 Jul 2016  路  23Comments  路  Source: angular/universal

Note: for support questions, please use one of these channels: https://github.com/angular/universal/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.

  • I'm submitting a ...
  • [x] bug report
  • [ ] feature request
  • [ ] support request => Please do not submit support request here, see note at the top of this template.
  • What modules are related to this pull-request
  • [ ] express-engine
  • [ ] grunt-prerender
  • [ ] gulp-prerender
  • [ ] hapi-engine
  • [ ] universal-preview
  • [x] universal
  • [ ] webpack-prerender
  • Do you want to request a _feature_ or report a _bug_?

Report a bug

  • What is the current behavior?

Note: This is purely guessing, as the error message isn't descriptive enough for us to solve it.
An error is raised in the browser sometime: Rendering Document Error: TypeError: undefined is not a function or Rendering Document Error: TypeError: str.replace is not a function.
This (according to code), is probably angular universal trying to serialize an attribute with a falsy value, or not a string.

  • If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem by creating a github repo.

Clone universal-starter, replace home.template.html by:

<div class="home" [hidden]="foo">
  <form #loginForm="ngForm">
      <input type="text" placeholder="username" required><br>
      <button type="submit" [disabled]="!loginForm.form.valid">Login</button>
  </form>
</div>
  • What is the expected behavior?

No error

  • Please tell us about your environment:
  • Angular version: 2.0.0-rc4
  • Browser: all
  • Language: all
  • OS: all
  • Platform: NodeJS
  • Other information (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc)

Complete stacktraces:

Rendering Document Error: TypeError: str.replace is not a function
    at escapeString (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:27:10)
    at Serializer._serializeAttributes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:124:55)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:91:10)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:114:14)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:114:14)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:114:14)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
Rendering Document Error: TypeError: undefined is not a function
    at escapeString (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:27:10)
    at Serializer._serializeAttributes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:124:55)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:91:10)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:114:14)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:114:14)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
    at Serializer._serializeElement (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:114:14)
    at Serializer._serializeChildNodes (project\node_modules\angular2-universal\node_modules\parse5\lib\serialization\serializer.js:72:22)
investigation

Most helpful comment

The problem happens when the json document on the server is converted to a string and one of the elements has an attribute is not type string.
Also check the MetaService (if you're using it) to make sure the values are always string
cc @vikerman @MarkPieszak

All 23 comments

@gjuchault can you change [disabled] to [attr.disabled]

It works; but is it still a bug ?

it's a bug in our propToAttr abstraction that was meant to convert these elements to attributes and our service that converts our fake-json-html-elements on the server to a string (our serializer).

When rendering on the server we're converting an element to the string representation of that so for example. <input id="myInput" value="hi'> if I run window.myInput.view I'll get "hi" because the document has an API that will convert attributes into properties after converting the markup into the correct js type HTMLInputElement
if you change the value of myInput to "yolo" then view the element again you will notice that the attribute is still value="hi" yet the value is now "yolo". When converting these elements to string, when creating the index.html, we're only looking at the attributes since there are way too many properties for each native element.

For this case, the real problem is due to our serializer and propToAttr. disabled was returning disabled="false" where false is a boolean rather than a string. Our propToAttr was meant to remove these boolean attributes if they're false and if they're true only provide them without a value <input disabled=""> or <input disabled>

screen shot 2016-07-17 at 10 30 13 am

@gdi2290 Is there any fix coming to this?

We have a similar issue with "numbers" inside string where we have for example element like this:
<cms-authentication [contentId]="123456"></cms-authentication>

It gives us stacktrace:

Rendering Document Error: [TypeError: str.replace is not a function]
TypeError: str.replace is not a function
    at escapeString (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:27:10)
    at Serializer._serializeAttributes (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:124:55)
    at Serializer._serializeElement (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:91:10)
    at Serializer._serializeChildNodes (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:72:22)
    at Serializer._serializeElement (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:114:14)
    at Serializer._serializeChildNodes (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:72:22)
    at Serializer._serializeElement (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:114:14)
    at Serializer._serializeChildNodes (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:72:22)
    at Serializer._serializeElement (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:114:14)
    at Serializer._serializeChildNodes (/path/to/app/node_modules/parse5/lib/serialization/serializer.js:72:22)

Does it also work if you do attr.contentId ? @Zeetah
I have some time today I can look into it, I didn't realize it was happening with completely custom input attributes in components / etc.

@MarkPieszak, @Zeetah:
This HTML comes from our CMS system and it's not something that we can change for a brief testing. I did some tests by mutating the provided HTML. Mapping [contentId]="12345" => [attr.contentId]="12345" broke the whole application for some reason and I didn't have time to figure out why. Mapping [contentId]="12345" to [contentId]="'12345'" does help.

Very interesting, I tried reproducing it by making even similarly named Component, but couldn't reproduce it. Do you have some code somewhere I can look at so I can test with it? @teemuandersen

In #499 that's the only way I could get it to "break" if I used an alias and referenced it wrong in the template, is that maybe what's going on?

@MarkPieszak: Unfortunately this is a private repo. But I've tested this some more. It seems to be related to dynamic (experimental) functionality. We use the ComponentResolver to create new Components at runtime with CMS provided HTML as a template. These dynamic Components then trigger our Components to render the dynamic content.

This problem with tags/inputs (e.g. contentId) is encountered when tag/selector contains attributes/properties declared with property bind syntax (_[name]="value"_) that the Component doesn't need and therefore doesn't declare as inputs (no @Input for them) or there is no Component that matches the tag/selector. As a result during serialization _[contentId]="12345"_ is tried to escape but fails as it's value is a number.

There is a PR that can fix this issue, so we'll be all set soon :)

@teemuandersen That's exactly the issue I'm facing. In the code bellow, the model property 'quantidade' is a number, and node throws "Rendering Document Error: TypeError: str.replace is not a function at escapeString (C:...\node_modules\parse5\lib\serialization\serializer.js:27:10)"

<input [(ngModel)]="item.quantidade" class="qtdProduto" maxlength="3" />

Did you find any workaround for this issue?

@felipedrumond, This isn't fixed yet (I'm on parental leave so I'm not that uptodate with this issue)? In our case we fetched HTML from CMS so our temp fix was simply to use regexp to escape unused numeric fields as strings. But I don't think that helps you.

@felipedrumond @teemuandersen This should of been fixed in Master now but it might not of been published. When the latest and greatest comes out for rc5/6 then it will definitely be there!

This is still an issue with Angular 4.1.3 and server side rendering. Serializer.escapeString is the culprit. If the str comes through as null then it errors (attrMode = true)

The problem is that there is virtually no way to actually debug this and find out what one attribute in an application has the issue and fix it so it's wack a mole.

This is happening with platform-server you're saying right? @JohnGalt1717

There looks to be a test for null here, what is the exact scenario you're running into?

Are you doing some type of [hidden]="thisValueCanBeNull" ? Where when thisValueCanBeNull = null it's erroring out?

Yes. I'm using your template for .net and we're getting this on sub pages in our site. I did as above and changed [disabled] to [attr.disabled] and it didn't make a difference so we're trying to figure out where there is a null somewhere that it sees on nodejs.

We can't hook up a nodejs debugger because that's broken currently (issue opened in your git repo) so we have no way of getting more of the stack than this:

Microsoft.AspNetCore.NodeServices:Error: TypeError: Cannot read property 'replace' of null
at Function.Serializer.escapeString (d:*\Clientdist\main-server.js:111335:9)
at module.exports.Serializer._serializeAttributes (d:*
\Clientdist\main-server.js:111409:32)
at module.exports.Serializer._serializeElement (d:*\Clientdist\main-server.js:111387:10)
at module.exports.Serializer._serializeChildNodes (d:*
\Portal\Clientdist\main-server.js:111368:22)
at module.exports.Serializer._serializeElement (d:*\Clientdist\main-server.js:111399:14)
at module.exports.Serializer._serializeChildNodes (d:*
\Clientdist\main-server.js:111368:22)
at module.exports.Serializer._serializeElement (d:*\Clientdist\main-server.js:111399:14)
at module.exports.Serializer._serializeChildNodes (d:*
\Clientdist\main-server.js:111368:22)
at module.exports.Serializer.serialize (d:*\Clientdist\main-server.js:111353:10)
at Object.exports.serialize (d:*
\Clientdist\main-server.js:64476:23)

The problem happens when the json document on the server is converted to a string and one of the elements has an attribute is not type string.
Also check the MetaService (if you're using it) to make sure the values are always string
cc @vikerman @MarkPieszak

Thanks for reopening!

In general all of these errors need to actually point to the line of your code that causes the error as well. The stack traces are basically useless when you're using webpack as they are right now because you don't get what originally caused the problem. (this is true all over Angular)

I can confirm this is still an issue on 4.4.3. I didn't have a problem with [disabled], but the meta service was giving me trouble. Thanks for the tip @gdi2290

I found news!
If I put Tag.addTag()or Tag.anything in constructor() {} function, it success. But if I put this.Tag.addTags() in ngOnInit() {}function it causing Cannot read property 'replace' of undefined error.
So be careful :)

Is this still an issue on the latest versions?

Nope, not really. Things like [hidden] still fail as do other random issues because of the use of parse5 that refuses to put in a simple null check. There are ways around it but the error message it still useless and doesn't tell you what binding is causing the error so it's guess work to find it.

Thanks for that @JohnGalt1717
I'll close this for now but if you have any consistencies between the Server and the Browser feel free to open an issue.

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings