This project is read-only.

Unterminated call stack on Client Block

May 19, 2013 at 6:40 AM
Hi again

For my project I need to use a droplist to allow the user to input a list of strings for my model.

I decided to "copy" the "Client Block Complete Example" approach to do so, both with using the Client Block, the template and the inline transformation.

To get a better hold of the actual code needed, I checked the "NestedModels" downloadable, and basically copied the code back to back, just changing the name of the properties the name of the model/view, the name of the global javascript variable, and nothing else at all.

However when loading I get a "Maximum call stack size exceeded " error in 'knockout.all-2.4.0' like this:

"b.la
b.$
b.a.d
ko.bindingHandlers.options.update
ko.bindingHandlers.options.update
ko.bindingHandlers.options.update
...."
and so on and on.


(sometimes it puts "{anonymous function}" instead of b.la and the like)



I tried debugging it but I couldn't find the problem, mostly because I don't know where to look.

Is there anything that can cause that error that I missed? Or at least where can I look to debug said error?


From what I can imagine, the elements of the droplist are constantly being updated for some reason (and thus the options of the droplist are being updated as well), but the only way they can be updated is via the "addNewItem" method, so how can this method be called when the page is loaded and not when the button is pressed?

I am using the MVC4ControlsToolkit library this time :P
Coordinator
May 19, 2013 at 1:05 PM
Are you using jquery 1.9 or better? Becuase the knockout included in knockout all 2.4.0 is not compatible with 1.9.1. You have to download the 2.4.1 release of the same file that is availaable in the download area.

If this doesnt solve your problem, please show your view, and all javascript files included. The problem is due to the fact that the knocout binding is not working properly, probably because the template engine has problems, and the Html created is wrong.
May 19, 2013 at 7:57 PM
No, I'm using jquery-1.8.2.
I tried using the 2.4.1 release just in case but didn't work either.

Yes, it might be something about the js libraries. I'm not exactly sure which ones to use (which versions, etc),
Anyways, here are my views:

Layout.cshtml
 @Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/Scripts/jquery-1.8.2.js")
@RenderSection("scripts", required: false)
ComboboxTest.cshtml
@using CrearSitio.Models
@model ComboBoxModel

@{
    ViewBag.Title = "ComboboxTest";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>ComboboxTest</h2>

 <div>
    @Html.LabelFor(m => m.Nombre)
    @Html.EditorFor(m => m.Nombre)
    @Html.ValidationMessageFor(m => m.Nombre)
    @Html.LabelFor(m => m.Requerido)
    @Html.CheckBoxFor(m => m.Requerido)
    @Html.ValidationMessageFor(m=>m.Requerido)

</div>

    @Html.ValidationMessageFor(m=>m.PosiblesValores)
    <div id="ComboBoxViewModelContainer">
            @{
                var comboBoxValoresHelper =
                  Html.TransformedHelper(
                      m => m.PosiblesValores,
                      new ComboBoxViewModel());
                      }
                @Html.ValidationMessageFor(m => m.PosiblesValores)
 @comboBoxValoresHelper.TemplateFor(m => m, "Template/ComboBoxViewModelTemplate", null, true, "valoresHandling", "ComboBoxModelContainer", true)           
</div>

@section Scripts{
    <script src="~/Scripts/jquery.validate.js" type="text/javascript"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>   
    <script src="~/Scripts/knockout.all-2.4.0.min.js" type="text/javascript"></script>
    <script src="~/Scripts/MvcControlToolkit.Bindings-2.4.0.js" type="text/javascript"></script>
    <script src="~/Scripts/MvcControlToolkit.Utils-2.4.0.js" type="text/javascript"></script>
    <script src="~/Scripts/MVCControlToolkit.Controls.Core-2.4.0.js" type="text/javascript"></script>
    <script src="~/Scripts/MVCControlToolkit.Controls.Items-2.4.0.js" type="text/javascript"></script>        
    <script src="~/Scripts/MVCControlToolkit.Controls-2.4.0.js"  type='text/javascript' ></script>
  }
ComboBoxViewModelTemplate.cshtml
@using CrearSitio.Models
@using MVCControlsToolkit.Controls.Bindings
@model ComboBoxViewModel


@{var bindings = Html.ClientBindings();
            bindings
                .AddMethod("addNewItem",
                @"
                    function () {"+
                        bindings.VerifyFieldsValid (m => m.ItemToAdd) + @"
                        if (this.ItemToAdd() == null || this.ItemToAdd().length == 0) return;
                        this.PosiblesValores.push(this.ItemToAdd());
                        this.SelectedItems.push(this.ItemToAdd());
                        this.ItemToAdd('');  
                    }
                    ")
                .AddMethod("removeSelected",
                @"
                    function () {
                        this.PosiblesValores.removeAll(this.SelectedItems());
                        this.SelectedItems([]); // Reset selection to an empty set
                    }

                    ");
            }
            <div>
            @Html.LabelFor(m => m.SelectedItems)
            @Html.DropDownListFor(m => m.SelectedItems, new {style="min-width:100px"}, Html.CreateChoiceList(
                m => m.PosiblesValores,
                m => m,
                m => m, overridePrompt: "prova"))
            </div>
            <div>
                
               @Html.LabelFor(m => m.ItemToAdd)
               @Html.TypedTextBoxFor(
                    m => m.ItemToAdd, 
                    "watermark")
                
                @Html.ValidationMessageFor(m => m.ItemToAdd, "*")
                @{
                var addButtonBindings = bindings
                    .Click(m => m, "addNewItem")
                    .Get();
                var removeButtonBinding = bindings
                    .Click(m => m, "removeSelected")
                    .Enable(m => m.SelectedItems, "{0}.length > 0").Get();
                }
                <input type="button" value="Agregar" data-bind='@addButtonBindings'/>
                <input type="button" value="Remover seleccionados" data-bind='@removeButtonBinding'/>
            </div>
May 20, 2013 at 6:31 AM
Actually, this is false

I'm doing this in a "testing project" to figure out how to do this exactly for my actual project

In my actual project I have jquery-1.8.3 and jquery-1.7.2. I might try it in there, but I'd like to be sure it works in the "mock" project first. Hopefully the difference in jquery versions doesn't matter
Coordinator
May 20, 2013 at 9:59 AM
Actually the way you used all script includes is wrong. There are several errors:
  1. MVCControlToolkit.Controls.Core must be always the first include of all mvc Controls Toolkit files.
  2. MvcControlToolkit.Bindings-2.4.0.js is already included in knockout.all-2.4.0.min.js, so it must not be included again
  3. MVCControlToolkit.Controls-2.4.0 contains MVCControlToolkit.Controls.Core, MVCControlToolkit.Controls.Items-2.4.0.js and other js, so it is a duplicate.
Please refer to the script include section to learno more about which js files to include.

No the jQuery version doesnt matter. The only breaking difference is with jquery 1.9
May 20, 2013 at 6:52 PM
Yeah I tried to test a little bit the order and includes of the scripts, but no one worked.

If I put it "correctly" like this (took out the duplicates):
@Scripts.Render("~/Scripts/MVCControlToolkit.Controls-2.4.0.js")     
@Scripts.Render("~/Scripts/knockout.all-2.4.0.min.js")    
@Scripts.Render("~/Scripts/MvcControlToolkit.Utils-2.4.0.js")
It gives me this error:
"Uncaught Error: Unable to parse bindings.
Message: TypeError: Cannot read property 'length' of null;
Bindings value: click: removeSelected, enable: SelectedItems().length > 0
knockout.all-2.4.0.min.js:221 "

If I put Bindings afterwards like this:
@Scripts.Render("~/Scripts/MVCControlToolkit.Controls-2.4.0.js")     
@Scripts.Render("~/Scripts/knockout.all-2.4.0.min.js")    
@Scripts.Render("~/Scripts/MvcControlToolkit.Bindings-2.4.0.js")
@Scripts.Render("~/Scripts/MvcControlToolkit.Utils-2.4.0.js")
It doesn't give me that error anymore, but gives me the "maximum call stack exceeded" error from before
Coordinator
May 20, 2013 at 9:33 PM
The message Message: TypeError: Cannot read property 'length' of null;

make sense...something good is happening...but something is null.

Normally we have this problem when an IEnumerable that must me transferred to the client viewmodel is null. In this casse the knockout mapping function that creates automatically observables, is not able to understand it is an IEnumerable...so it doesnt create an observableArray....and this creates problem when you try to bind this property to something that requires an observableArray.


I suspect your SelectedItems are null.... NEVER leave IEnumerables properties null, always put an empty IEnumerable in them, so an empty array is created when the value is transformed into javascript....so knockout is able to understand it must create an observableArray.
May 21, 2013 at 1:49 AM
You are right I hadn't initialized SelectedItem, thanks!

Yet....

I'm sorry that I'm bothering you this much, but I new to your toolkit and it's a little hard to use at times :(

I have some new problems.

First I'll try setting some context.
I want to basically combine the "Heterogenous list with subclasses" example with the "Client box with inline transformations" example
I want a variable and editable list of different subclasses that belong to a specific superclass, and one of those specific subclasses uses a Client Box to edit a "List<string>" property of it (the above "combobox" type).
I want this to be posted in a form, and be validated.
Each type is validated in different ways, etc.

I tried doing 2 approaches to this:

1) Do a "mock project" (the above one) to test the Client Block for ComboBox

2)Ignore the ComboBox subclass (basically remove it, or at least remove the "List<string>" property that needs the Client Box) and try to see if it works that way.

I encountered problems in each, and I'm not sure exactly how to handle it.

In (1) this is basically the code:
@using (Html.BeginForm("BO_CrearSitioTest", "BackOffice"))
{

@Html.ValidationSummary(true)
<div>
<!-- bunch of unrelated properties of the model -->
</div>    
<div> 
    @Html.LabelFor(m => m.Tipos)
    @Html.ValidationMessageFor(m => m.Tipos)

@Html.SortableListFor(m => m.Tipos, 
        _S.H<TipoAtributoModel>(        
         @<text>

           @{HtmlHelper<BooleanoModel> hBooleano;
           HtmlHelper<EnteroModel> hEntero;
           HtmlHelper<ComboBoxModel> hComboBox;
           HtmlHelper<FechaModel> hFecha;
           HtmlHelper<RealModel> hReal;
           HtmlHelper<TextoModel> hTexto;}
                
        <strong>@item.SelectionButton("Booleano", "insertBooleano", item.PrefixedId("attributeType"), item.PrefixedId("BooleanoSelection"), ManipulationButtonStyle.Link)</strong>
        <strong>@item.SelectionButton("Entero", "insertEntero", item.PrefixedId("attributeType"), item.PrefixedId("EnteroSelection"), ManipulationButtonStyle.Link)</strong>
        <strong>@item.SelectionButton("ComboBox", "insertComboBox", item.PrefixedId("attributeType"), item.PrefixedId("ComboBoxSelection"), ManipulationButtonStyle.Link)</strong>
        <strong>@item.SelectionButton("Fecha", "insertFecha", item.PrefixedId("attributeType"), item.PrefixedId("FechaSelection"), ManipulationButtonStyle.Link)</strong>
        <strong>@item.SelectionButton("Real", "insertReal", item.PrefixedId("attributeType"), item.PrefixedId("RealSelection"), ManipulationButtonStyle.Link)</strong>
        <strong>@item.SelectionButton("Texto", "insertTexto", item.PrefixedId("attributeType"), item.PrefixedId("TextoSelection"), ManipulationButtonStyle.Link)</strong>
        <!-- Este es el template para el tipo Booleano -->
        <div id='@item.PrefixedId("insertBooleano")' class='AttributeListItem @item.PrefixedId("attributeType")'>
            @item.DescendatntCast(m => m).To(out hBooleano)
            <div>
                @hBooleano.LabelFor(m => m.Nombre) 
                @hBooleano.TextBoxFor(m => m.Nombre) 
                @hBooleano.ValidationMessageFor(m => m.Nombre)
            </div>
            <div>
                @hBooleano.LabelFor(m => m.Requerido) 
                @hBooleano.CheckBoxFor(m => m.Requerido) 
                @hBooleano.ValidationMessageFor(m => m.Requerido)
            </div>
        </div>
        <!-- Este es el template para el tipo Entero -->
        <div id='@item.PrefixedId("insertEntero")' class='AttributeListItem @item.PrefixedId("attributeType")'>
        @item.DescendatntCast(m => m).To(out hEntero)
            <div>
                @hEntero.LabelFor(m => m.Nombre)
                @hEntero.TextBoxFor(m => m.Nombre)
                @hEntero.ValidationMessageFor(m => m.Nombre)
            </div>
            <div>
                @hEntero.LabelFor(m => m.Requerido)
                @hEntero.CheckBoxFor(m => m.Requerido)
                @hEntero.ValidationMessageFor(m => m.Requerido)
            </div>
            <div>
                @hEntero.LabelFor(m => m.RangoInicio)
                @hEntero.TextBoxFor(m => m.RangoInicio)
                @hEntero.ValidationMessageFor(m => m.RangoInicio)
            </div>
            <div>
                @hEntero.LabelFor(m => m.RangoFin)
                @hEntero.TextBoxFor(m => m.RangoFin)
                @hEntero.ValidationMessageFor(m => m.RangoFin)
            </div>
        </div>

         <!-- Este es el template para el tipo ComboBox -->
        <div id='@item.PrefixedId("insertComboBox")' class='AttributeListItem @item.PrefixedId("attributeType")'>
        @item.DescendatntCast(m => m).To(out hComboBox)
            <div>
                @hComboBox.LabelFor(m => m.Nombre)
                @hComboBox.TextBoxFor(m => m.Nombre)
                @hComboBox.ValidationMessageFor(m => m.Nombre)
            </div>
            <div>
                @hComboBox.LabelFor(m => m.Requerido)
                @hComboBox.CheckBoxFor(m => m.Requerido)
                @hComboBox.ValidationMessageFor(m => m.Requerido)
            </div>
            
            <div id="ComboBoxViewModelContainer">
            {
                var comboBoxValoresHelper =
                  hComboBox.TransformedHelper(
                      m => m.PosiblesValores,
                      new ComboBoxViewModel());
                      }
                hComboBox.ValidationMessageFor(m => m.PosiblesValores)
                comboBoxValoresHelper.TemplateFor(m => m, "Template/ComboBoxViewModelTemplate", null, true, "valoresHandling", "ComboBoxModelContainer", true)
           
            </div> 
        </div>

        <!-- Este es el template para el tipo Fecha -->
        <div id='@item.PrefixedId("insertFecha")' class='AttributeListItem @item.PrefixedId("attributeType")'>
        @item.DescendatntCast(m => m).To(out hFecha)
            <div>
                @hFecha.LabelFor(m => m.Nombre)
                @hFecha.TextBoxFor(m => m.Nombre)
                @hFecha.ValidationMessageFor(m => m.Nombre)
            </div>
            <div>
                @hFecha.LabelFor(m => m.Requerido)
                @hFecha.CheckBoxFor(m => m.Requerido)
                @hFecha.ValidationMessageFor(m => m.Requerido)
            </div>
            <div>
                @hFecha.LabelFor(m => m.Formato)
                @hFecha.TextBoxFor(m => m.Formato)
                @hFecha.ValidationMessageFor(m => m.Formato)
            </div>
        </div>

        <!-- Este es el template para el tipo Real -->
        <div id='@item.PrefixedId("insertReal")' class='AttributeListItem @item.PrefixedId("attributeType")'>
        @item.DescendatntCast(m => m).To(out hReal)
            <div>
                @hReal.LabelFor(m => m.Nombre)
                @hReal.TextBoxFor(m => m.Nombre)
                @hReal.ValidationMessageFor(m => m.Nombre)
            </div>
            <div>
                @hReal.LabelFor(m => m.Requerido)
                @hReal.CheckBoxFor(m => m.Requerido)
                @hReal.ValidationMessageFor(m => m.Requerido)
            </div>
            <div>
                @hReal.LabelFor(m => m.RangoInicio)
                @hReal.TextBoxFor(m => m.RangoInicio)
                @hReal.ValidationMessageFor(m => m.RangoInicio)
            </div>
            <div>
                @hReal.LabelFor(m => m.RangoFin)
                @hReal.TextBoxFor(m => m.RangoFin)
                @hReal.ValidationMessageFor(m => m.RangoFin)
            </div>
        </div>


        <!-- Este es el template para el tipo Texto -->
        <div id='@item.PrefixedId("insertTexto")' class='AttributeListItem @item.PrefixedId("attributeType")'>
        @item.DescendatntCast(m => m).To(out hTexto)
            <div>
                @hTexto.LabelFor(m => m.Nombre)
                @hTexto.TextBoxFor(m => m.Nombre)
                @hTexto.ValidationMessageFor(m => m.Nombre)
            </div>
            <div>
                @hTexto.LabelFor(m => m.Requerido)
                @hTexto.CheckBoxFor(m => m.Requerido)
                @hTexto.ValidationMessageFor(m => m.Requerido)
            </div>
            <div>
                @hTexto.LabelFor(m => m.TamMinimo)
                @hTexto.TextBoxFor(m => m.TamMinimo)
                @hTexto.ValidationMessageFor(m => m.TamMinimo)
            </div>
            <div>
                @hTexto.LabelFor(m => m.TamMaximo)
                @hTexto.TextBoxFor(m => m.TamMaximo)
                @hTexto.ValidationMessageFor(m => m.TamMaximo)
            </div>
        </div> 
        @item.ViewList(item.PrefixedId("attributeType"), "attributeSelected", "insertTexto")
    
    @item.SortableListDeleteButton("Borrar Tipo", ManipulationButtonStyle.Link)
    </text>
        ),
        htmlAttributesItems: new Dictionary<string, object>{{"class", "AtributoListItem"}},
         itemContainer: ExternalContainerType.div,
         allItemsContainer: ExternalContainerType.div,
         canSort: false
         )

 </div>   
  <div class='ControlPanel'>
 @Html.SortableListAddButtonFor(m => m.Tipos, "Agregar Tipo") <input type='submit' value='Crear Sitio' />
 </div>
}

@section Scripts{   
    @Scripts.Render("~/Scripts/MVCControlToolkit.Controls-2.4.0.js")     
    @Scripts.Render("~/Scripts/knockout.all-2.4.0.min.js")        
    @Scripts.Render("~/Scripts/MvcControlToolkit.Utils-2.4.0.js")   
}
May 21, 2013 at 2:22 AM
When I run that, every time I hit the SortableList button to add a new line, I get this error:

"Uncaught Error: Unable to parse bindings.
Message: ReferenceError: addNewItem is not defined;
Bindings value: click: addNewItem "

If I load a line before passing it to the view (by initializing a new subclass in the constructor of the model of this view (and calling said constructor before passing it to the view), I can work with it, using both the SelectButtons and the Client Block perfectly
But when adding a new line it shows ALL the different templates for each subclass (instead of 1 and having the other ones hidden) and throws that error.


About my 2nd approach, I tried doing it on my actual project.
At first it works alright, the first time I go to the view. It binds attributes alright.
However, when I try testing server side validation (basically try passing "bad" parameters), it breaks.

The first time it does validation it does it perfectly...for the most part.
Before doing the initial POST, I fiddle around with the SelectButtons, adding new values, then changing the subclass, adding other attributes, forcing "bad" values (that will fail the validation), etc
When the 1st POST arrives at the controller, it does all [Required] and "normal" validation correctly
However I tried a "complex" validation (over the list of all subclasses, the part with "@Html.ValidationMessageFor(m => m.Tipos)"), and one time it didn't validate it, and at another time it did (when it should have validated them both...as in they were both wrong and it should have showed up in the ModelState).

Itt throws A LOT of "NullReferenceException" in this part:
<strong>@item.SelectionButton("Texto", "insertTexto", item.PrefixedId("attributeType"), item.PrefixedId("TextoSelection"), ManipulationButtonStyle.Link)</strong>
Not only that, but when I get the model back to the view, show the validation and let the user edit it, if I make changes to it (for instance use the SelectButtons to choose a new subclass where the error happened), or add a new subclass (with the SortedList button), or stuff like that, either the model binder makes the whole model null (when passing it to the controller), or it makes some of the parameters null when they actually weren't

The whole thing is too chaotic, at times it works and at times it doesn't, at times some validation works and at times it doesnt, all the while the NullReferenceException keeps being triggered, etc.

I even tried this new "fix" (initializing SelectedItems in ComboboxViewModel) and see if it worked with that, but instead of showing the ComboBoxViewModel template it shows the literal string "{v@{var bindings = Html.ClientBindings(); bindings .AddMethod("addNewItem",..." (that is inside the template)


I'm totally lost right now so I don't even know what to ask of you.
You are the lead developer so you shouldn't dwell with every single "technical support" problem ever either.

I just want to know if you can recommend a way for me to do this (Editable list of subclasses+Client Block+Validation+Free modifiable values after validation fails) in a simple way if possible without the need of too much configuration or things that could go wrong.
Do you know of any example of something like this? Maybe just imitating the code a little bit of something that actually works may be better than try it on my own, specially since I need to solve this quickly for my project :(


Sorry for the unending list of problems, errors and stuff >_>
The thing is that I tried for weeks to do this and couldn't find a single way to do so.
I tried normal MVC, and it was impossible to work (no "client-side" way to manipulate the subclasses and stuff)
I tried Knockout MVC and it didn't work either (had lots of problems and I couldn't fix them)
I tried using knockout.js directly, but it was impossible for me to map the subclasses and stuff to a javascript object, bind it and make it work

This is the first one that I think may actually work, so I really need it to :/

I can't believe it's so hard to do something like this in MVC >_>
Coordinator
May 21, 2013 at 2:02 PM
The point is that you are trying to do complex suffs without having first adquired experience and manuality on simple examples before. The learning time of all Mvc related stuffs is higher than that of the corresponding WebForms. However, Mvc is more flexible. Try to concentrate on each simple example without mixing several techniques in same part of the page. Once you have something working, and you have understood well how all stuffs work you may add more, and more complexity

1) When you pass several templates to the sortable list helper, they must be included into an array of templates, and each template is contained within a _S.H(....), while you put all Html templates one after the other. Please give a look to the multiple template example here:

2) You must have several add buttons each for each different template. Each of the add button helper nust contain the index of the template it must use when creating the new element(the index in the array of all templates)

3) you can't mix client blocks with server controls items controls. You may use both server controls and client blocks in the same page, but you can't nest them. Look at the documentation index. Items controls are listed into two separate classes: the one that work with client block, and the one that are pure server controls.

4) Client Blocks can't handle sub-classes (like in the heterogegenous list example) becau e the json desrializer has no information on which subclass to use when it deserialize json data (json data contai no type information).

As a conclusion if you want to mix sub-types you cant use client blocks, so you have to use a sortablelistfor helper...but passin the right templates
May 21, 2013 at 9:07 PM
Yeah I didn't know you couldn't do it

So to do something like this work I need to make EVERY item bound to client blocks?
Like those "client templates" and "client repeaters" and stuff (I just skimmed over)?

Yeah I don't have time to study those, I'll see if I find a way to do this on the server (like a wizard or something) and after this project is done I'll try studying this toolkit more deeply (it's still unbelievable cool)


I don't really believe something like this should be "complex" to do client side.
Yes, it's more complex than the usual "1 class model", but there should be an easy way to customize any type of model and manipulate it client-side (like with client blocks, knockout, etc), specially models with heritance, which in my mind are very common or at least in my experience.
Basically, if you can do it easily on WPF or Silverlight, etc, you should be able to do it easily in MVC (or without much more effort).
I don't see that happening with web applications and it makes me sad :(

Well, thanks for the advice!
Coordinator
May 22, 2013 at 8:57 AM
Edited May 22, 2013 at 8:59 AM
The limit of Web applications are due to the standards they must conform to.

For instance, returning to the sub-classing problem ALL Client Block Mvc Controls Toolkit controls, like the "client repeater" are able to handle several templates, so you may display different subclasses in different ways, if you have a way to recognize an object belong to a certain class (javascript has no type associated to an object, so you have to check, for instance the existence of a property).

HOWEVER the problem is with the serializer/deserializer that transform data from ad to jSon. It is not able to understand a json data structure represent a sub-class of the main class it is processing. Now this is not a technical problem, but just a matter of standards...json has no way to convey the sub-class information...according to the standards.

However, you may customize one of the existing serializers, by adding the type information to the json object (for instance a _type property)....However, if for some reason you want to use a different serializer...then your code will not run with the new serializer, because your modification doesn't conform to any standard.

For future projects give a look to the Data-Moving plugin for the Mvc Controls Toolkit, it has a lot of out of the box controls you may configure easily with a fluent interface. The RTM has been closed and we are preparing the documentation. It will be availble in a short time.