{"id":1407,"date":"2022-08-30T15:16:16","date_gmt":"2022-08-30T15:16:16","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2013\/11\/15\/knockoutjs-rangeerror-maximum-call-stack-size-exceeded-collection-of-common-programming-errors\/"},"modified":"2022-08-30T15:16:16","modified_gmt":"2022-08-30T15:16:16","slug":"knockoutjs-rangeerror-maximum-call-stack-size-exceeded-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2022\/08\/30\/knockoutjs-rangeerror-maximum-call-stack-size-exceeded-collection-of-common-programming-errors\/","title":{"rendered":"KnockoutJS: RangeError: Maximum call stack size exceeded;-Collection of common programming errors"},"content":{"rendered":"<p>When registering a product, the user can customize the URL of it! As the user goes by typing the <code>Tipo de produto<\/code>, <code>Nome<\/code> or <code>Link<\/code>, the website will show you how will the URL for this product<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.stack.imgur.com\/jZg7G.png\" \/><\/p>\n<blockquote>\n<p>Full url: http:\/\/i.stack.imgur.com\/jZg7G.png<\/p>\n<\/blockquote>\n<p><strong>Note that the field &#8220;Tipo de produto&#8221; also modifies the URL!!<\/strong><\/p>\n<p>For this, I created a helper in KnockoutJS<\/p>\n<h2>Code<\/h2>\n<p><strong>KnockoutJS<\/strong><\/p>\n<pre><code>ko.bindingHandlers.url =\n    update: (element, valueAccessor, allBindingsAccessor, viewModel) -&gt;\n        link = ko.utils.unwrapObservable(valueAccessor())\n        if link\n            link = link.toLowerCase().trim().replaceAll(\" \", \"-\")\n            link = encodeURI(link)\n        else\n            link = \"\"\n        valueAccessor()(link)\n        $(element).nextAll(\".link-exibicao\").text(link).effect(\"highlight\", { color: \"#FDBB30\" }, 800 )\n<\/code><\/pre>\n<p>The only purpose of this helper is to generate a valid URL and display it in the span <code>.link-exibicao<\/code><\/p>\n<p><strong>ViewModel<\/strong><\/p>\n<pre><code>public class ProdutoViewModel\n{\n    [AdditionalMetadata(\"data-bind\", \"event: { change: function(data) { Link(data.Nome());  }}\")]\n    public string Nome { get; set; }\n\n    [DataType(DataType.Url)]\n    [AdditionalMetadata(\"Prefixo\", \"Produto\/\")]\n    public string Link { get; set; }\n\n    [Display(Name = \"Descri\u00e7\u00e3o\")]\n    [DataType(DataType.MultilineText)]\n    public string Descricao { get; set; }\n\n    public int? Ordem { get; set; }\n}\n<\/code><\/pre>\n<p><code>AdditionalMetadata<\/code> will add an attribute with that name and value. For example, the property <code>Name<\/code> will generate the HTML:<\/p>\n<pre><code>\n<\/code><\/pre>\n<p><strong>Url.cshtml<\/strong><\/p>\n<p>The next step would be to add the markup <code>data-bind=\"url: Link\"<\/code> in all fields of type URL:<\/p>\n<pre><code>@model string\n@{\n\n    var values = ViewData.ModelMetadata.AdditionalValues;\n    object objDatabind;\n    string data_bind = \"\";\n    if (values.TryGetValue(\"data-bind\", out objDatabind))\n    {\n        data_bind = objDatabind.ToString();\n    }\n\n    var nomeCampo = Html.IdForModel();\n\n    var objPrefixo = values[\"Prefixo\"];\n    string prefixo = objPrefixo.ToString();\n    string separador = \"\/\";\n    if (!string.IsNullOrWhiteSpace(prefixo))\n    {\n        if (prefixo.EndsWith(\"\/\") || prefixo.EndsWith(\"#\"))\n        {\n            separador = prefixo[prefixo.Length - 1].ToString();\n            prefixo = prefixo.Substring(0, prefixo.Length - 1);\n        }   \n    }\n}\n\n@Html.TextBoxFor(p =&gt; Model, new { data_bind = \"value: \" + nomeCampo + \", url: \" + nomeCampo + (string.IsNullOrWhiteSpace(data_bind) ? \"\" : \", \" + data_bind) })\n@Request.Url.Host\/@prefixo@separador\n<\/code><\/pre>\n<p><strong>ProdutoViewModel.cshtml<\/strong><\/p>\n<p>Finally, and most simple step would be to build the form =):<\/p>\n<pre><code>\n    Tipo de produto\n\n\n    \n\n\n\n    @Html.LabelFor(p =&gt; p.Nome)\n\n\n    @Html.EditorFor(p =&gt; p.Nome)\n    @Html.ValidationMessageFor(p =&gt; p.Nome)\n\n\n\n    @Html.LabelFor(p =&gt; p.Link)\n\n\n    @Html.EditorFor(p =&gt; p.Link)\n    @Html.ValidationMessageFor(p =&gt; p.Link)\n\n\n\n    @Html.LabelFor(p =&gt; p.Descricao)\n\n\n    @Html.EditorFor(p =&gt; p.Descricao)\n    @Html.ValidationMessageFor(p =&gt; p.Descricao)\n\n\n\n    @Html.LabelFor(p =&gt; p.Ordem)\n\n\n    @Html.EditorFor(p =&gt; p.Ordem)\n    @Html.ValidationMessageFor(p =&gt; p.Ordem)\n\n<\/code><\/pre>\n<h2>Problem<\/h2>\n<p>Whenever typed simple words like: &#8220;my product name&#8221; everything works perfectly!<br \/>\nBut words like <code>meu prod\u00fato c\u00f4m a\u00e7\u00eanto<\/code> the error below is displayed!<\/p>\n<pre><code>Uncaught Error: Unable to parse bindings.\nMessage: RangeError: Maximum call stack size exceeded;\nBindings value: value: Link, url: Link\n<\/code><\/pre>\n<ol>\n<li>\n<p>Your bindingHandler is causing recursive updates, as you are accessing the value:<\/p>\n<p><code>link = ko.utils.unwrapObservable(valueAccessor())<\/code><\/p>\n<p>and later setting it:<\/p>\n<p><code>valueAccessor()(link)<\/code><\/p>\n<p>If <code>link<\/code> ends up being identical to its current value, then the chain would stop (observables don&#8217;t notify on identical (===) values).<\/p>\n<p>When you pass: <code>meu prod\u00fato c\u00f4m a\u00e7\u00eanto<\/code><\/p>\n<p>It becomes: <code>meu-prod%C3%BAto%20c%C3%B4m%20a%C3%A7%C3%AAnto<\/code><\/p>\n<p>When setting the observable it re-triggers the same binding. So, it calls <code>encodeURI<\/code> again and now it is double-encoded like:<\/p>\n<p><code>meu-prod%25C3%25BAto%2520c%25C3%25B4m%2520a%25C3%25A7%25C3%25AAnto<\/code><\/p>\n<p>The observable is set again and since this value is new it triggers it again (and again and again) until you get the call stack error.<\/p>\n<p>Some options to handle this would be to not write back to the observable and just use the binding to encode the URL.<\/p>\n<p>Otherwise a good choice would be to use a <code>writeable computed observable<\/code> to intercept writes to the value and manipulate it in the model.<\/p>\n<\/li>\n<\/ol>\n<p id=\"rop\"><small>Originally posted 2013-11-15 09:09:49. <\/small><\/p>","protected":false},"excerpt":{"rendered":"<p>When registering a product, the user can customize the URL of it! As the user goes by typing the Tipo de produto, Nome or Link, the website will show you how will the URL for this product Full url: http:\/\/i.stack.imgur.com\/jZg7G.png Note that the field &#8220;Tipo de produto&#8221; also modifies the URL!! For this, I created [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1407","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/1407","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=1407"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/1407\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=1407"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=1407"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=1407"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}