2012-09-22 163 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。然後是將其他子視圖彼此相鄰插入的問題。