views/dom/domdiff-view/parsetree/TemplateRefParseNode.js

class TemplateRefParseNode extends ScopedParseNode {
    constructor(templateElement){
        super(templateElement); 
        this.children.push(new TemplateInstanceParseNode(templateElement));
    }    
    
    getView(targetDocument, scope){
        if (DOMView.DEBUG) console.log("instantiating template-ref for ", this.templateElement);
        return super.getView(targetDocument, scope);
    }
    
    // Look at the TEMPLATE-NAME attribute and generate our scope(s)
    generateScopes(view){
        let self = this;
        let templateQuery = this.templateElement.getAttribute("template-name");
        let templateHookType = this.templateElement.getAttribute("template-hook");
        if ((templateQuery!==null) && templateQuery.trim().length>0){
            // Need to monitor a list of templates
            view.templateUpdatingEvaluation = new UpdatingEvaluation(templateQuery, view.scope, async function templateNameAttributeChanged(templateName){
                try {                    
                    // Find the template and create corresponding scopes
                    let templates = document.querySelectorAll("varv-template[name='" + templateName+"']");
                    let localScopes = [];

                    if (templates.length===0) throw new Error("Template with name '"+templateName+"' does not exist (yet?)");
                    switch (templateHookType){
                        case "all":
                            templates.forEach((template)=>{
                                localScopes.push([new TemplateBinding(template)]);
                            });
                            break;
                        case "first":
                            localScopes.push([new TemplateBinding(templates[0])]);
                            break;
                        case "last":
                        default:
                            localScopes.push([new TemplateBinding(templates[templates.length - 1])]);
                    }
                    
                    self.onScopesUpdated(view, localScopes);
                } catch (ex){
                    self.showError(view, "Template-ref='"+templateQuery+"': "+ex, ex);
                    return;            
                }
            });
            view.addCleanup(()=>{
                view.templateUpdatingEvaluation.destroy();
            });
        }      
    }    
}
window.TemplateRefParseNode = TemplateRefParseNode;

/**
 * A node that dynamically parses and renders a template from the scope all during getView rather than construction
 * @type type
 */
class TemplateInstanceParseNode extends ParseNode {
    getView(targetDocument, scope){
        let templateBinding = scope[scope.length-1];
        if (!templateBinding instanceof TemplateBinding) throw new Error("STUB: Currently TemplateBinding MUST be the last element on the scope stack when rendering template instances");
        
        // Dynamically parse and render the template now
        let referencedTemplateElement = templateBinding.getTemplateElement()
        if (DOMView.DEBUG) console.log("parsing template-instance for ", this.templateElement, referencedTemplateElement);       
        let view = new ViewParticle(targetDocument.createProcessingInstruction("varv-template-anchor", {}), this, scope);
        let parseNodes = [];
        for (let childNode of referencedTemplateElement.childNodes){
            let parseChild = this.parseTemplateNode(childNode);
            if (parseChild){
                // If this actually needs parsing, add it
                parseNodes.push(parseChild);
            }
        }        
        
        if (DOMView.DEBUG) console.log("creating template-instance view for ", view, parseNodes);
        view.childViews = [];
        parseNodes.forEach((parseChild)=>{
            view.childViews.push(parseChild.getView(targetDocument, scope));
        });
        
        // Also destroy the template view children when our view is destroyed
        view.addCleanup(()=>{
            view.childViews.forEach((childView)=>{
                childView.destroy();
            });
        });
        
        // When we are mounted into the document, think about the children!
        view.addOnMountedCallback(()=>{
            view.childViews.forEach((childView)=>{
                // Insert them before our anchor node
                childView.mountInto(view.getNode().parentNode, view.getNode());
            });
        });
        
        return view;
    }    
};
window.TemplateInstanceParseNode = TemplateInstanceParseNode;