2012-09-22 162 views
2

我有一个树状的设置,其中每个节点都包含可能包装在父节点模板中的自己的Mustache模板。小胡子和嵌套模板

var templates = { 
    secondTmpl: Mustache.compile("<i>Yet another template.. Here's some text: {{text}}</i>  {{date}}"), 
    template: Mustache.compile("<b>{{val}}</b><p>{{{outlet}}}</p><ul>{{#list}}<li>{{.}}   </li>{{/list}}</ul> {{test}}") 
}; 

var tree = [ 
    { 
     template: "template", 
     data: { val: "yup!!", list: [1,2,3,4,5], test: function() { return new Date(); } }, 
     children: [ 
      { 
       view: "Main", 
       template: "secondTmpl", 
       data: { text: "Some Value", date: function() { return new Date(); } } 
      } 
     ] 
    } 
]; 

function MainView(options) { 
    this.template = options.template; 
    this.data = options.data; 
    this.childViews = options.childViews; 
    this.el = document.createElement("div"); 
} 

MainView.prototype.render = function() { 
    View.prototype.render.call(this); 
    this.postRender(); 
    return this; 
}; 

MainView.prototype.postRender = function() { 
    this.el.getElementsByTagName("i")[0].style.border = "1px dotted red"; 
}; 

function View(options) { 
    this.template = options.template; 
    this.data = options.data; 
    this.childViews = options.childViews; 
    this.el = document.createElement("div"); 
} 

View.prototype.render = function(renderChildren) { 
    if(this.childViews) { 
     this.data.outlet = "<div class=\"outlet\"></div>"; 
    } 

    this.el.innerHTML = this.template(this.data); 

    if(this.childViews) { 
     this.childViews.forEach(function(view) { 
      this.el.getElementsByClassName("outlet")[0].appendChild(view.render().el); 
     }, this); 
    } 

    return this; 
}; 

function traverse(node) { 
    var viewOptions = { 
     template: templates[node.template], 
     data: node.data 
    }; 

    if(node.children) { 
     viewOptions.childViews = node.children.map(function(n) { 
      return traverse(n); 
     }); 
    } 

    return node.view ? new window[node.view + "View"](viewOptions) : new View(viewOptions); 
} 

function init() { 
    tree.forEach(function(node) { 
     var view = traverse(node); 
     document.body.appendChild(view.render().el); 
    }); 
} 

window.onload = init;​ 

这里举例:http://jsfiddle.net/b4fTB/

,我在树中的数据的原因是因为嵌套模板取决于用户的数据而改变,因为许多模板可以被包裹在不同的模板。

我不知道我在这里做什么是愚蠢的,但它允许我从C#渲染模板,这是相当不错的。

所以 - 问题(对上述评论当然是受欢迎的)。处理嵌套模板的模板时,最好有一个简单的函数,它只返回与实际模板相关的dom元素 - 而不是来自嵌套模板的dom元素。这甚至有可能吗?这是否有可能允许深度嵌套模板而不会失去大量的性能?换句话说,我有两个模板,其中一个嵌套在jsfiddle的另一个模板中。在处理dom时,不必担心父视图中的嵌套视图。

+0

我从jsFiddle的代码编辑。有时小提琴链接会下降。 – Andrey

回答

0

好的,我想我已经找到了一种方法。

下面的代码需要mustachejscomposejs

var noop = function() {}; 

var templates = { 
    secondTmpl: Mustache.compile("<i>Yet another template.. {{{outlet}}}Here's some text: {{text}}</i> {{date}}"), 
    template: Mustache.compile("<b>{{val}}</b><p>{{{outlet}}}</p><ul>{{#list}}<li>{{.}}</li>{{/list}}</ul> {{test}}") 
}; 

var tree = [ 
    { 
     view: "Main", 
     template: "template", 
     data: { val: "yup!!", list: [1, 2, 3, "Four", 5], test: function() { return new Date(); } }, 
     children: [ 
      { 
       template: "secondTmpl", 
       data: { text: "Some Value", date: function() { return new Date(); } } 
      }, 
      { 
       view: "Test", 
       template: "secondTmpl", 
       data: { text: "ANOTHER TEMPLATE", date: function() { return new Date(); } }, 
       children: [ 
        { 
         template: "template", 
         data: { val: "Pretty nested template", list: [56, 52, 233, "afsdf", 785], test: "no datetime here" } 
        } 
       ] 
      } 
     ] 
    } 
]; 

var View = Compose(function(options) { 
    Compose.call(this, options); 
    this.el = document.createElement(this.tag); 
}, { 
    tag: "div", 
    render: function() { 
     if(this.childViews) { 
      this.data.outlet = "<div class=\"outlet\"></div>"; 
     } 

     this.el.innerHTML = this.template(this.data); 

     this.didRender(); 

     if(this.childViews) { 
      var lastEl; 

      this.childViews.forEach(function(view) { 
       if(!lastEl) { 
        var outlet = this.el.getElementsByClassName("outlet")[0]; 
        lastEl = view.render().el; 
        outlet.parentNode.replaceChild(lastEl, outlet); 
       } else { 
        var el = view.render().el; 
        lastEl.parentNode.insertBefore(el, lastEl.nextSibling); 
        lastEl = el; 
       } 
      }, this); 
     } 

     this.didRenderDescendants(); 

     return this; 
    }, 
    didRender: noop, 
    didRenderDescendants: noop 
}); 

var TestView = View.extend({ 
    didRender: function() { 
     var nodes = this.el.querySelectorAll("*"); 
     for(var i = 0; i < nodes.length;i++) 
      nodes[i].style.border = "2px dotted red"; 
    } 
}); 

var MainView = View.extend({ 
    didRender: function() { 
     var nodes = this.el.querySelectorAll("*"); 
     for(var i = 0; i < nodes.length;i++) 
      nodes[i].style.backgroundColor = "lightgray"; 
    } 
}); 

function traverse(node) { 
    var viewOptions = { 
     template: templates[node.template], 
     data: node.data 
    }; 

    if(node.children) { 
     viewOptions.childViews = node.children.map(function(n) { 
      return traverse(n); 
     }); 
    } 

    return node.view ? new window[node.view + "View"](viewOptions) : new View(viewOptions); 
} 

function init() { 
    tree.forEach(function(node) { 
     var view = traverse(node); 
     window["view"] = view; 
     document.body.appendChild(view.render().el); 
    }); 
} 

window.onload = init; 

关键是要替换的第一个孩子视图,而不是附加给它的div.outlet。然后是将其他子视图彼此相邻插入的问题。