懒得吐槽了 view和data分开的话 你这个框架就不是mvvm 本身就是落后的技术了 mvvm的精髓就是binding 根据datacontext自动生成需要的view 只要把template写好 不管多复杂的数据传给根节点 多层级的view会自动生成
扫了一下你的文档 用js做页面脚本也是自己造轮子 nodejs上随便改一下middleware用js动态处理请求就好了 分明有原生的js服务器 你干嘛自己用java再造轮子
反正前端库五花八门 你那套东西你自己习惯就那你就自己用吧 我反正是觉得没什么用 所以你最好不要上来安利忽悠初学者了
下面是我用angular1做的树形结构实现 Template复用 可以根据根节点上的数据结构生成任意深度的子节点嵌套同时填充数据 整个网页可以完全用treeview结构来搭 只要在跟节点给一个多层结构的数据就行了 我是WPF转过来的 玩mvvm这套 微软WPF的ItemsControl是鼻祖 你写什么框架好歹先看看别的大公司都做了啥吧
<div tree="controller.koReferenceTree" template-selector="controller.koTemplateSelector" model-builder="controller.koModelBuilder">
<div class="inline">
<div presenter></div>
</div>
<template children="items">
<div class="inline">{</div>
<div class="btn btn-info" ng-click="data.switchMode()">@</div>
<div class="btn btn-warning" ng-click="data.switchNot()">{{data.not}}</div>
<input type="text" ng-pattern="/^K\d{5,5}$/i" style="width: 80px; display: inline;" class="form-control" ng-model="data.value" placeholder="K codes such as K01641" />
<div class="btn btn-danger" ng-show="data.parent" ng-click="data.remove()">-</div>
<div class="inline">}</div>
</template>
<template children="items">
<div class="inline">[</div>
<div class="btn btn-info" ng-click="data.switchMode()">{}</div>
<div class="btn btn-primary" ng-click="data.switchMethod()">{{data.method}}</div>
<div presenter class="inline"></div>
<div class="btn btn-info" ng-click="data.addCondition()">+</div>
<div class="btn btn-danger" ng-show="data.parent" ng-click="data.remove()">-</div>
<div class="inline">]</div>
</template>
</div>
export class TreeTemplate {
key: string;
path: string;
type: string;
url: string;
template: string;
children: string;
jQuery: JQuery;
}
export interface ITreeItemScope extends ng.IScope {
data: any; //data for the template;
controller: any; //user can specify the
model: any; //the model generated by modelbuilder
app: any; //the app, if the user wants to pass app to the content;
}
export interface ITreeScope extends ITreeItemScope {
tree: any[]; //data is an array;
modelBuilder: (data: any) => any; //a function that will create a viewmodel for a data;
childrenSelector: (item: any, parent: any, level: number) => string;// a fucntion that will tell the tree view which property to watch for sub items;
templates: TreeTemplate[];//tempaltes that is used to save all templates;
templateSelector: (data: any, templates: TreeTemplate[]) => TreeTemplate;
}
export interface ITreeDirectiveScope extends ng.IScope {
tree: string;
app: string;
controller: string;
modelBuilder: string;
childrenSelector: string;
templates: string;
templateSelector: string;
}
export interface ITreeTemplateSelector<TData> {
(data: TData, templates: ngstd.TreeTemplate[]);
}
export interface ITreeModelBuilder<TData> {
(data: TData, item: ngstd.TreeItemBase);
}
/**
* To use this directive, you must use the observable interface 'add(), remove(), removeAt(), clear()'
*/
export class TreeDirective extends ngstd.AngularDirective<ITreeDirectiveScope>{
constructor($compile: ng.ICompileService, $http: ng.IHttpService) {
super();
this.restrict = ngstd.DirectiveRestrict.A;
this.scope.tree = ngstd.BindingRestrict.Both;
this.scope.app = ngstd.BindingRestrict.Both;
this.scope.controller = ngstd.BindingRestrict.Both;
this.scope.modelBuilder = ngstd.BindingRestrict.Both;
this.scope.childrenSelector = ngstd.BindingRestrict.Both;
this.scope.templateSelector = ngstd.BindingRestrict.Both;
this.scope.templates = ngstd.BindingRestrict.OptionalBoth;
this.link = (scope: ITreeScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
if (!scope.templates) scope.templates = [];
if (!Array.isArray(scope.templates)) scope.templates = [];
//this will remove all the templates in the content;
element.children('template').each((index: number, elem: Element) => {
var $elem: JQuery = $(elem);
var template = new TreeTemplate();
template.key = $elem.attr('key');
template.path = $elem.attr('path');
template.type = $elem.attr('type');
template.url = $elem.attr('url');
template.children = $elem.attr('children');
template.jQuery = $elem;
if (template.url) {
//the embedded template is used for loading process;
template.template = $elem.html();
$http.get(template.url)
.success((data: string) => {
template.template = data;
//we must check if the return value can affect the view of the content control.
//if (scope.selector) if (scope.view != scope.selector(scope.data, scope.templates)) {
// //if view is affected, view must be updated.
// scope.view = scope.selector(scope.data, scope.templates);
//}
});
}
else {
template.template = $elem.html();
}
scope.templates.push(template);
});
element.children('template').remove();
var root = new TreeRoot(scope, element, $compile);
root.refresh();
}
}
}
export class TreeItemBase {
public root: TreeRoot;
public scope: ITreeItemScope;
public view: ng.IAugmentedJQuery;
public presentor: JQuery;
public children: TreeItemBase[] = [];
public data: any;
public index: number;
public childrenWatchUnregister: any;
public parent: TreeItemBase;
public level: number;
/**
* For observable interface of ItemsSource Watching, do not use this function;
* @param array
* @param item
* @param index
*/
private onInsert = (array: any[], item: any, index: number) => {
this.insert(item, index);
}
/**
* For observable interface of ItemsSource Watching, do not use this function;
* @param array
* @param item
* @param index
*/
private onRemoveAt = (array: any[], item: any, index: number) => {
this.removeAt(index);
}
/**
* For observable interface of ItemsSource Watching, do not use this function;
* @param array
*/
private onClear = (array: any[]) => {
this.clear();
}
/**
* For observable interface of ItemsSource Watching, do not use this function;
* @param array
* @param item
* @param from
* @param to
*/
private onMoveTo = (array: any[], item: any, from: number, to: number) => {
this.moveTo(from, to);
}
// protected functions for TreeView logic
protected updateApp = () => {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].scope.app = this.root.app;
}
}
protected updateController = () => {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].scope.controller = this.root.controller;
}
}
protected updateModelBuilder = () => {
if (this.root.modelBuilder) {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].scope.model = this.root.modelBuilder(this.children[i].data, this.children[i]);
}
}
else {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].scope.model = null;
}
}
}
protected updateChildrenView = () => {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].buildView();
}
this.renderChildren();
}
/**
* For watch children array;
* @param newValue
* @param oldValue
*/
protected childrenChanged = (newChildren: any[], oldChildren: any[]) => {
this.clearChildren();
//detach all listeners;
if (oldChildren) {
oldChildren.onInsert = null;
oldChildren.onRemoveAt = null;
oldChildren.onClear = null;
oldChildren.onMoveTo = null;
}
if (newChildren) {
//attach listeners to new value;
newChildren.onInsert = this.onInsert;
newChildren.onRemoveAt = this.onRemoveAt;
newChildren.onClear = this.onClear;
newChildren.onMoveTo = this.onMoveTo;
for (var i: number = 0; i < newChildren.length; i++) {
var data = newChildren[i];
var child = new TreeItem(data, this, this.level + 1);
child.index = i;
child.buildView();
this.children.push(child);
}
this.renderChildren();
}
}
/**
* clear children (perform remove on each child) but it doesn't update the children view; you need to call renderChildren to update view;
* this function is for internal call;
*/
protected clearChildren = () => {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].destroy();
}
}
/**
* destroy and rebuild the view, then it has to find the presentor;
*/
protected buildView: () => void;
protected render: () => void;
//public functions:
/**
* clear chilren (perform remove on each child), destory scope, remove view;
*/
public destroy = () => {
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].destroy();
}
if (this.scope) this.scope.$destroy();
if (this.view) this.view.remove();
this.scope = null;
this.view = null;
}
public clear = () => {
this.clearChildren();
this.renderChildren();
}
/**
* present the children in the order of children;
*/
public renderChildren = () => {
if (this.presentor) {
//detach all children;
this.presentor.children().detach();
//append all children;
for (var i: number = 0; i < this.children.length; i++) {
this.children[i].index = i;
if (this.children[i].view) this.presentor.append(this.children[i].view);
}
}
}
/**
* insert a data at 'index'
* @param data
* @param index
*/
public insert = (data: any, index: number) => {
var child = new TreeItem(data, this, this.level + 1);
if (index < 0) index = 0;
if (index > this.children.length) index = this.children.length;
child.index = index;
child.buildView();
this.children.splice(index, 0, child);
//we also need to take care of the view;
this.renderChildren();
}
public removeAt = (index: number) => {
var child = this.children[index];
if (child) {
child.destroy();
this.children.splice(index, 1);
this.renderChildren();
}
}
/**
* Move a child from 'from' to 'to';
* @param from
* @param to
*/
public moveTo = (from: number, to: number) => {
var child = this.children.splice(from, 1)[0];
this.children.splice(to, 0, child);
this.renderChildren();
}
/**
* Move a child from 'from' position of this item to the 'to' position of the target item;
* This function allows simple 'drag-drop' operation for the tree view.
* @param from
* @param target
* @param to
*/
public moveToTreeItem = (from: number, target: TreeItemBase, to: number, rebuildView?: boolean) => {
var child = this.children.splice(from, 1)[0];
this.renderChildren();
child.parent = target;
child.root = target.root;
if (rebuildView) child.buildView();
target.children.splice(to, 1, child);
target.renderChildren();
}
/**
* Allow viewmodel to update the view;
*/
public refresh = () => {
this.buildView();
this.render();
}
}
export class TreeItem extends TreeItemBase {
constructor(data: any, parent: TreeItemBase, level: number) {
super();
this.data = data;
this.parent = parent;
this.root = parent.root;
//from root;
}
public buildView = () => {
this.destroy();
var tempalte: TreeTemplate;
if (this.root.templateSelector) tempalte = this.root.templateSelector(this.data, this.root.templates);
if (!tempalte) if (this.root.templates) tempalte = this.root.templates[0]; // by default use the first template;
var templateHTML = '{{data}}<div presenter></div>';
var childrenSource: string;
if (tempalte) {
templateHTML = tempalte.template; //this is the default view for the tree view;
childrenSource = tempalte.children;
}
//echo('templateHTML: ' + templateHTML);
this.scope = <any>this.parent.scope.$new(true, this.parent.scope);
this.scope.data = this.data;
this.scope.controller = this.root.controller;
if (this.root.modelBuilder) this.scope.model = this.root.modelBuilder(this.data, this);
this.scope.app = this.root.app;
//add watch here?
if (this.root.childrenSelector) {
var childrenSourceFromSelector = this.root.childrenSelector(this.data, this.parent.data, this.level);
if (childrenSourceFromSelector) childrenSource = childrenSourceFromSelector;
}
if (childrenSource) this.childrenWatchUnregister = this.scope.$watch('data.' + childrenSource, this.childrenChanged);
this.view = this.root.compile(templateHTML)(this.scope);
this.presentor = this.view.filter('div[presenter]');
if (!this.presentor) this.presentor = this.view.find('div[presenter]');
}
public render = () => {
this.parent.renderChildren();
//this.parent.scope.$apply();
}
}
export class TreeRoot extends TreeItemBase {
public controller: any;
public app: any;
public compile: ng.ICompileService;
public templateSelector: (data: any, templates: TreeTemplate[]) => TreeTemplate;
public modelBuilder: (data: any, treeItem: TreeItemBase) => any;
public childrenSelector: (child: any, parent: any, level: number) => string;
public templates: TreeTemplate[] = [];
constructor(scope: ITreeScope, element: ng.IAugmentedJQuery, complie: ng.ICompileService) {
super();
this.level = 0;
this.scope = scope;
this.compile = complie;
this.view = element;
this.presentor = element.find('div[presenter]');
this.app = scope.app;
this.controller = scope.controller;
this.templates = scope.templates;
this.modelBuilder = scope.modelBuilder;
this.templateSelector = scope.templateSelector;
this.childrenSelector = scope.childrenSelector;
scope.$watch('app', this.appWatcher);
scope.$watch('controller', this.controllerWatcher);
this.root = this;
if (!this.presentor) {
console.log('Fetal Error in Tree Directive: No <div presentor></div> node was found. You must provide one <div presentor></div> in the Tree Directive to present the data.');
}
}
public buildView = () => {
//we must watch the data;
this.childrenWatchUnregister = this.scope.$watch('tree', this.childrenChanged);
}
public render = () => {
//nothing to do;
//this.scope.$apply();
}
private appWatcher = (newApp: any, oldApp: any) => {
this.updateApp();
}
private controllerWatcher = (newController: any, oldController: any) => {
this.updateController();
}
private modelBuilderWatcher = (newModelBuilder: any, oldModelBuilder: any) => {
this.updateModelBuilder();
}
private templatesWatcher = (newTemplates: any, oldTemplates: any) => {
this.updateChildrenView();
}
private templateSelectorWatcher = (newTemplateSelector: any, oldTemplateSelector: any) => {
this.updateChildrenView();
}
private childrenSelectorWatcher = (newChildrenSelector: any, oldChildrenSelector: any) => {
this.updateChildrenView();
}
}
【 在 Inshua 的大作中提到: 】
: 我这个框架本来就是一个组件框架,数据方面自有数据框架
:
: 不要吹牛,你给个angular的实现吧
: ...................
--
修改:facilitator FROM 110.23.10.*
FROM 110.23.10.*