Search code examples
jsrenderjsviews

JsViews: Reinitialize Template Using Deep Copy Object: ~root issue


I am trying to create an undo process (reinitialize template) using a deep copy of a data-linked object. This may not be an ideal method, but I cannot use View Models, merge() and unmap() right now.

mainObject renders templateA (mainObject derived from AJAX request, Type: json)

mainObject = $.extend(true, mainObject, addObject); adds additional objects for editing purposes, renders templateB (addObject derived from AJAX request, Type: json)

I have been using the two above templates and objects without issues for quite some time.

Now...

I want to make a deep copy/clone of mainObject in the case the user cancels editing (two-way data-linked).

When I create mainObjectClone = JSON.parse(JSON.stringify(mainObject)) and re-render using the same template, templateB, I am getting errors related to context ~root.

It is my understanding that JSON.parse() returns the same type of object as AJAX request, Type: json.

Either the object is not returned or errors like: Uncaught TypeError: Cannot read property '0' of undefined.  +((v=view.ctxPrm("root").myPhotos[j._sq(0)])!=null?v:"")

With debug turned on, all parts of template render fine except where ~root is used.

The strange thing is, ~root does not work where I have defined it in the template...but in further testing, if I add ~root to a 'different' top level object (one that doesn't need context), it works there strangely.

I have tried these and all break on ~root:

  • myObjectClone = $.extend(true, {}, mainObject)
  • Lodash deep copy
  • Also reversing object order mainObject, addObject to mainObject = $.extend(true, addObject, mainObject) breaks ~root access too (first object gets extended).

I have tried but failed to reproduce the issue in jsfiddle...although with limited data/template.

Console log shows all objects as correctly formed.

How does ~root get created/initialized...on some or all objects?

What code conflicts might alter the ~root context?

Any tips or pointers would be appreciated.


UPDATE

Although I have commented out all helpers, etc, to bare-bones template + data (with debug on)...the ~root issue still remained. This 'finally' led me to believe this had nothing to do with the template or data.

So here is my scenario:

  • TemplateA is rendered using link() with DataA (no issues)
  • TemplateB is rendered using link() with deep copy/clone object (DataA + DataB) (~root issue)
  • Templates A & B share common two-way data-linked items, although from different data sources.

If I change TemplateA render method to render() (instead of link()); and then render TemplateB, ~root context works.

Another scenario: instead of rendering TemplateB with deep copy/clone object (DataA + DataB)...I combined the data on the server and rendered against that.  This also produced the ~root context issue.

I then thought maybe there was an issue when link() is called from multiple templates that their objects with same name would conflict.  So I isolated one of the common objects, renamed it and rendered a test template with name changes....~root issue remained.

That's all I know for now ;-)


Solution

  • Answering my own question here...

    There was no problem with JsViews here...the issue with ~root was of my own creation ;-)

    The problem was cause by injecting link() templateB into link() templateA...thus creating a conflict with ~root.

    My data sources and templates used to be combined...I split them for testing and wasn't thinking clearly.

    Essentially I was doing this:

    <script id="templateA" type="text/x-jsrender">
        <div id="userView">...</div>
        <div id="editView"></div>
    </script>
    
    <script id="templateB" type="text/x-jsrender">
        ...
    </script>
    
    
    $.templates("#templateA").link("#userView", dataA)
    
    onClick openEditView...
    
    $.templates("#templateB").link("#editView", $.extend(true, dataA, dataB))
    

    The fix is pretty easy, something like this:

    <script id="templateA" type="text/x-jsrender">
        <div id="userView">...</div>
        {{if editable}}
             {{include tmpl="templateB" /}}
        {{/if}}
    </script>
    
    <script id="templateB" type="text/x-jsrender">
        ...
    </script>
    
    
    $.templates("#templateA").link("#userView", dataA)
    
    onClick openEditView...
    
    $.extend(true, dataA, dataB))
    

    This solves the ~root issue and also deep copy object methods now work.