gpt4 book ai didi

ember.js - 如何使用 Ember.js 动态添加和删除 View

转载 作者:行者123 更新时间:2023-12-03 23:55:00 25 4
gpt4 key购买 nike

我正在尝试创建一个用于遍历关系数据库中的表的接口(interface)。每个选择代表一列。如果该列是外键,则会在右侧添加一个新选择。对于用户访问的每个外键,这种情况都会发生。选择的数量是动态的。

我做了一个有问题的实现,其中包含手动添加和删除选择 View 的代码。我认为它可能可以替换为更好的 Ember 代码(也许是某种数组对象?),我只是不确定如何最好地使用该框架来解决这个问题。

这是我的 JSBin http://jsbin.com/olefUMAr/3/edit

HTML:

<!DOCTYPE html>
<html>
<head>
<meta name="description" content="Ember template" />
<meta charset=utf-8 />
<title>JS Bin</title>
<script src="http://code.jquery.com/jquery-1.9.0.js"></script>
<script src="http://builds.emberjs.com/handlebars-1.0.0.js"></script>
<script src="http://builds.emberjs.com/tags/v1.1.2/ember.js"></script>
</head>
<body>
<script type="text/x-handlebars" data-template-name="my_template">
{{view fieldSelects}}
</script>

<div id="main"></div>
</body>
</html>

JavaScript:
App = Ember.Application.create();

var TemplatedViewController = Ember.Object.extend({
templateFunction: null,
viewArgs: null,
viewBaseClass: Ember.View,
view: function () {
var controller = this;
var viewArgs = this.get('viewArgs') || {};
var args = {
template: controller.get('templateFunction'),
controller: controller
};
args = $.extend(viewArgs, args);
return this.get('viewBaseClass').extend(args);
}.property('templateFunction', 'viewArgs'),
appendView: function (selector) {
this.get('view').create().appendTo(selector);
},
appendViewToBody: function () {
this.get('view').create().append();
}
});

var DATA = {};
DATA.model_data = {
"Book": {
"fields": [
"id",
"title",
"publication_year",
"authors"
],
"meta": {
"id": {},
"title": {},
"publication_year": {},
"authors": {
"model": "Author"
}
}
},
"Author": {
"fields": [
"id",
"first_name",
"last_name",
"books"
],
"meta": {
"id": {},
"first_name": {},
"last_name": {},
"books": {
"model": "Book"
}
}
}
};

var Controller = TemplatedViewController.extend({
view: function () {
var controller = this;
return this.get('viewBaseClass').extend({
controller: controller,
templateName: 'my_template'
});
}.property(),
selectedFields: null,
fieldSelects: function () {
var filter = this;
return Ember.ContainerView.extend({
controller: this,
childViews: function () {
var that = this;
var selectedFields = filter.get('selectedFields');

var ret = [];
var model = 'Book';
selectedFields.forEach(function (item, index, enumerable) {
var selection = item;
if (model) {
var select = that.makeSelect(model, that.getPositionIndex(), selection, true).create();
ret.pushObject(select);
model = DATA.model_data[model].meta[selection].model;
}
});

return ret;
}.property(),
nextPositionIndex: 0,
incrementPositionIndex: function () {
this.set('nextPositionIndex', this.get('nextPositionIndex') + 1);
},
getPositionIndex: function () {
var index = this.get('nextPositionIndex');
this.incrementPositionIndex();
return index;
},
setNextPositionIndex: function (newValue) {
this.set('nextPositionIndex', newValue+1);
},
makeSelect: function (modelName, positionIndex, selection, isInitializing) {
var view = this;
return Ember.Select.extend({
positionIndex: positionIndex,
controller: filter,
content: DATA.model_data[modelName].fields,
prompt: '---------',
selection: selection || null,
selectionChanged: function () {
var field = this.get('selection');

// Remove child views after this one
var lastIndex = view.get('length') - 1;
if (lastIndex > this.get('positionIndex')) {
view.removeAt(this.get('positionIndex')+1, lastIndex-this.get('positionIndex'));
view.setNextPositionIndex(this.get('positionIndex'));
}

if (! isInitializing && DATA.model_data[modelName].meta[field].model) {
var relatedModel = DATA.model_data[modelName].meta[field].model;
view.pushObject(view.makeSelect(relatedModel, view.getPositionIndex()).create());
}

// Reset ``isInitializing`` after the first run
if (isInitializing) {
isInitializing = false;
}

var selectedFields = [];
view.get('childViews').forEach(function (item, index, enumerable) {
var childView = item;
var selection = childView.get('selection');
selectedFields.pushObject(selection);
});
filter.set('selectedFields', selectedFields);
}.observes('selection')
});
}
});
}.property()
});
var controller = Controller.create({
selectedFields: ['authors', 'first_name']
});

$(function () {
controller.appendView('#main');
});

最佳答案

方法:

我会使用 Ember Component 来解决这个问题.
我使用了一个组件,因为它将是:

  • 易于重复使用
  • 该代码是自包含的,对您的任何其他代码没有外部要求。
  • 我们可以使用普通的 javascript 来创建 View 。普通的 javascript 应该使代码流更容易理解(因为您不必知道 Ember 在幕后对扩展对象做了什么),并且它的开销会更少。

  • 演示:

    我创建了这个 JSBin here , 下面的代码。

    用法

    添加到您的 Handlebars 模板:
    {{select-filter-box data=model selected=selected}}

    创建一个 select-filter-box标签,然后将您的模型绑定(bind)到 data属性,以及您的 selected值数组到 selected属性。

    应用:
    App = Ember.Application.create();

    App.ApplicationController = Ember.ObjectController.extend({
    model: DATA.model_data,
    selected: ['Author','']
    });

    App.SelectFilterBoxComponent = Ember.Component.extend({

    template: Ember.Handlebars.compile(''), // Blank template
    data: null,
    lastCount: 0,
    selected: [],
    selectedChanged: function(){

    // Properties required to build view
    var p = this.getProperties("elementId", "data", "lastCount", "selected");

    // Used to gain context of controller in on selected changed event
    var controller = this;

    // Check there is at least one property. I.e. the base model.
    var length = p.selected.length;
    if(length > 1){

    var currentModelName = p.selected[0];
    var type = {};

    // This function will return an existing select box or create new
    var getOrCreate = function(idx){

    // Determine the id of the select box
    var id = p.elementId + "_" + idx;

    // Try get the select box if it exists
    var select = $("#" + id);
    if(select.length === 0){

    // Create select box
    select = $("<select id='" + id +"'></select>");

    // Action to take if select is changed. State is made available through evt.data
    select.on("change", { controller: controller, index: idx }, function(evt){

    // Restore the state
    var controller = evt.data.controller;
    var index = evt.data.index;
    var selected = controller.get("selected");

    // The selected field
    var fieldName = $(this).val();

    // Update the selected
    selected = selected.slice(0, index);
    selected.push(fieldName);
    controller.set("selected", selected);
    });

    // Add it to the component container
    $("#" + p.elementId).append(select);
    }
    return select;
    };

    // Add the options to the select box
    var populate = function(select){
    // Only populate the select box if it doesn't have the correct model
    if(select.data("type")==currentModelName)
    return;

    // Clear any existing options
    select.html("");

    // Get the field from the model
    var fields = p.data[currentModelName].fields;

    // Add default empty option
    select.append($("<option value=''>------</option>"));

    // Add the fields to the select box
    for(var f = 0; f < fields.length; f++)
    select.append($("<option>" + fields[f] + "</option>"));

    // Set the model type on the select
    select.data("type", currentModelName);
    };

    var setModelNameFromFieldName = function(fieldName){

    // Get the field type from current model meta
    type = p.data[currentModelName].meta[fieldName];

    // Set the current model
    currentModelName = (type !== undefined && type.model !== undefined) ? type.model : null;
    };

    // Remove any unneeded select boxes. I.e. where the number of selects exceed the selected length
    if(p.lastCount > length)
    for(var i=length; i < p.lastCount; i++)
    $("#" + p.elementId + "_" + i).remove();

    this.set("lastCount", length);

    // Loop through all of the selected, to build view
    for(var s = 1; s < length; s++)
    {
    // Get or Create select box at index s
    var select = getOrCreate(s);

    // Populate the model fields to the selectbox, if required
    populate(select);

    // Current selected
    var field = p.selected[s];

    // Ensure correct value is selected
    select.val(field);

    // Set the model for next iteration
    setModelNameFromFieldName(field);

    if(s === length - 1 && type !== undefined && type.model !== undefined)
    {
    p.selected.push('');
    this.notifyPropertyChange("selected");
    }
    }
    }
    }.observes("selected"),

    didInsertElement: function(){
    this.selectedChanged();
    }
    });

    这个怎么运作

    该组件采用两个参数 modelselected然后将观察者绑定(bind)到 selected属性(property)。任何时候通过用户与选择框的交互或绑定(bind)到 selected 的属性更改选择。 View 将重新确定。

    该代码使用以下方法:
  • 确定选择数组 ( selected ) 是否大于 1。(因为第一个值需要是基础模型)。
  • 循环所有选定的字段 i ,从索引 1 开始。
  • 确定是否选择框 i存在。如果没有创建一个选择框。
  • 确定是否选择框 i具有基于当前填充模型的正确模型字段。如果是,则不执行任何操作,如果不填充字段。
  • 设置选择框的当前值。
  • 如果我们是最后一个选择框并且选择的字段链接到模型,则将空白值推送到选择上,以触发下一个下拉列表。
  • 创建选择框时,会出现 onchange连接处理程序以更新 selected通过切片 selected 来计算值当前索引右侧的数组并添加其自己的值。这将导致 View 根据需要更改。
  • 属性(property)count跟踪上一个 selected的长度,因此如果对选择进行更改会减少当前 selected值长度,然后可以删除不需要的选择框。

  • 源代码有注释,我希望它很清楚,如果您对它的工作原理有任何疑问,请随时提问,我会尽量解释得更好。

    您的型号:

    查看您的模型后,您是否考虑将其简化为以下内容?我很欣赏你可能无法,因为超出问题范围的其他原因。只是一个想法。
    DATA.model_data = {
    "Book": {
    "id": {},
    "title": {},
    "publication_year": {},
    "authors": { "model": "Author" }
    },
    "Author": {
    "id": {},
    "first_name": {},
    "last_name": {},
    "books": { "model": "Book" }
    }
    };

    因此字段名称将从对象键中读取,值将是元数据。

    我希望你觉得这很有用。如果您有任何疑问或问题,请告诉我。

    Controller :

    您可以在此组件中使用您想要的任何 Controller 。在我的组件演示中,我使用了 Ember 的内置 ApplicationController为简单起见。
    notifyPropertyChange()的说明:

    之所以调用它,是因为当我们将新字符串插入 selected 时数组,使用 push数组的功能。

    我用过 push方法,因为这是将新条目添加到现有数组的最有效方法。

    虽然 Ember 确实有一个 pushObject 应该处理通知的方法,我无法让它兑现这一点。所以 this.notifyPropertyChange("selected");告诉 Ember 我们更新了数组。但是,我希望这不是破坏交易的因素。

    Ember 组件的替代方案 - 实现为 View

    如果您不想以组件格式使用它,您可以将它实现为 View 。它最终实现了相同的目标,但这可能是您更熟悉的设计模式。

    See this JSBin for implementation as a View .完整代码这里就不放了,因为有些和上面一样,大家可以在JSBin中看到

    用法:

    创建 App.SelectFilterBoxView 的实例,带有一个具有 data 的 Controller 和 selected属性(property):
    var myView = App.SelectFilterBoxView.create({
    controller: Ember.Object.create({
    data: DATA.model_data,
    selected: ['Author','']
    })
    });

    然后根据需要追加 View ,例如添加到 #main .
    myView.appendTo("#main");

    关于ember.js - 如何使用 Ember.js 动态添加和删除 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20360726/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com