将新的 select2 选项标签写入 Express 本地数据库

6
我在 Express 应用中使用 select2,创建了一个输入框,让用户可以从列表中选择主题,并且可以更新此列表以包括任何新添加的选项。但是我遇到的问题是,select2 运行在客户端,而我要使用来自服务器端的数据作为我的 <option> 标签的种子数据(我想将新的选项附加到这些标签上)。我希望用户能够添加原始列表中不存在的主题,以便将来的用户将展示新添加的选项(以及原始的选项)。我考虑了以下几个选项(按优先级递增): - 对于每个添加的标签,添加一个新的 <option>Subject</option> HTML 标签。 - 将新标签添加到数组中,并从该数组生成 <option> 标签。 - 从 JSON 对象中生成 <option> 标签,并在标签创建时更新此对象。 - 从外部数据库(例如 mongoose)生成 <option> 标签,并在标签创建时更新此数据库。 就我所看到的,所有这些选项都需要我的客户端代码(select2-js)与服务器端代码交互(其中我的数组、.json 文件或 mongoose schema 存在)。但是我不知道如何去做。目前的方法是在我的 select2 调用中指定一个“本地”json 文件作为数据来源(参见这里),然后检查每个新标签是否存在于数组中(dataBase),如果不存在,则将其添加到数据库中。但是这并没有像我预期的那样将选项添加到数据库中。
// Data to seed initial tags:
var dataBase = [
    { id: 0, text: 'Maths'},
    { id: 1, text: 'English'},
    { id: 2, text: 'Biology'},
    { id: 3, text: 'Chemistry'},
    { id: 4, text: 'Geography'}
];


$(document).ready(function() {
    $('.select2-container').select2({
        ajax: {
            url: '../../subjects.json',
            dataType: 'json',
        },
        width: 'style',
        multiple: true,
        tags: true,
        createTag: function (tag) {
            var isNew = false;
            tag.term = tag.term.toLowerCase();
            console.log(tag.term);
            if(!search(tag.term, dataBase)){
                if(confirm("Are you sure you want to add this tag:" + tag.term)){
                    dataBase.push({id:dataBase.length+1, text: tag.term});
                    isNew = true;
                }
            }
            return {
                        id: tag.term,
                        text: tag.term,
                        isNew : isNew
                    };
        },
        tokenSeparators: [',', '.']
    })
});

// Is tag in database?
function search(nameKey, myArray){
    for (var i=0; i < myArray.length; i++) {
        if (myArray[i].text.toLowerCase() === nameKey.toLowerCase()) {
            return true
        }
    }
    return false
};

然而,这种方法会将新的标签添加到一个数组中,该数组在我刷新页面后被销毁,并且新的标签并没有被存储。

我应该如何修改它以加载服务器端数据(jsonmongoose 文档或其他被认为是最佳实践的内容),并使用新添加的选项(通过我的测试)来更新这些数据?


1
使用ajax在页面上填充json。然后像您目前正在做的那样使用json来填充select2。将json附加到document.ready和数据库更新以获取结果。 - Lelio Faieta
@LelioFaieta - 感谢您的指引。有没有可能您能够将其扩展成一个答案? - fugu
3个回答

5
您可以使用select2:selectselect2:unselect事件来实现此功能。

var dataBase = [{
    id: 0,
    text: 'Maths'
  },
  {
    id: 1,
    text: 'English'
  },
  {
    id: 2,
    text: 'Biology'
  },
  {
    id: 3,
    text: 'Chemistry'
  },
  {
    id: 4,
    text: 'Geography'
  }
];

$(document).ready(function() {
  $('.select2-container').select2({
    data: dataBase,
    placeholder: 'Start typing to add subjects...',
    width: 'style',
    multiple: true,
    tags: true,
    createTag: function(tag) {
      return {
        id: tag.term,
        text: tag.term,
        isNew: true
      };
    },
    tokenSeparators: [',', '.']
  })
  $(document).on("select2:select select2:unselect", '.select2-container', function(e) {
    var allSelected = $('.select2-container').val();
    console.log('All selected ' + allSelected);

    var lastModified = e.params.data.id;
    console.log('Last Modified ' + lastModified);

    var dbIdArray = dataBase.map((i) => i.id.toString());
    var allTagged = $('.select2-container').val().filter((i) => !(dbIdArray.indexOf(i) > -1))
    console.log('All Tagged ' + allTagged);
  });
});
.select2-container {
  width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />

<select class="select2-container"></select>


5
在您的服务器端,您可以拥有一个API来维护和返回标记数组。如果您希望在服务器关闭后仍然保留该数组,您可以将标记数组存储在数据库中。 服务器端:
let dataBase = [
{ id: 0, text: 'Maths'},
{ id: 1, text: 'English'},
{ id: 2, text: 'Biology'},
{ id: 3, text: 'Chemistry'},
{ id: 4, text: 'Geography'}
];
//Assuming you have a nodejs-express backend
app.get('/tags', (req,res) => {
res.status(200).send({tags: dataBase});
} );

客户端:

$(document).ready(function() {
dataBase=[];
$.get("YOUR_SERVER_ADDRESS/tags", function(data, status){
console.log("Data: " + data + "\nStatus: " + status);
dataBase = data;
});

$('.select2-container').select2({
    data: dataBase,
    placeholder: 'Start typing to add subjects...',
    width: 'style',
    multiple: true,
    tags: true,
    createTag: function (tag) {
        var isNew = false;
        tag.term = tag.term.toLowerCase();
        console.log(tag.term);
        if(!search(tag.term, dataBase)){
            if(confirm("Are you sure you want to add this tag:" + tag.term)){
                dataBase.push({id:dataBase.length+1, text: tag.term});
                isNew = true;
                //Update the tags array server side through a post request
            }
        }
        return {
                    id: tag.term,
                    text: tag.term,
                    isNew : isNew
                };
    },
    tokenSeparators: [',', '.']
})
});

// Is tag in database?
function search(nameKey, myArray){
for (var i=0; i < myArray.length; i++) {
    if (myArray[i].text.toLowerCase() === nameKey.toLowerCase()) {
        return true
    }
}
return false
};

谢谢。我今晚会看一下。YOUR_SERVER_ADDRESS 是相对路径还是绝对路径?我需要安装/要求 ajax 才能使它工作吗? - fugu
1
YOUR_SERVER_ADDRESS是您提供API服务的Web服务器的绝对地址。如果您在本地计算机上运行本地服务器,则应为http://localhost:PORT_NUMBER。再次说明,PORT_NUMBER取决于您正在运行的服务器。默认情况下,Node服务器在端口3000上运行。您需要使用jQuery才能使其正常工作。 - DEVCNN

0
这是我最终得出的结果(感谢两个答案): 1. 设置一个Mongoose数据库来保存主题:

models/subjects.js

var mongoose = require("mongoose");

var SubjectSchema = new mongoose.Schema({
    subject: { type: String },
});

module.exports = mongoose.model("Subjects", SubjectSchema);

2. 在 Node.js Express 后端设置 API 路由:
routes/api.js

var express    = require("express");
var router = express.Router();
var Subjects = require("../models/subjects");

// GET route for all subjects in db
router.get("/api/subjects/all", function(req, res){
    Subjects.find().lean().exec(function (err, subjects) {
        return res.send(JSON.stringify(subjects));
    })
});

// POST route for each added subject tag
router.post("/api/subjects/save", function(req, res){
    var newSubject = {};
    newSubject.subject = req.body.subject;

    console.log("Updating db with:" + newSubject);

    var query = {subject: req.body.subject};

    var options = { upsert: true, new: true, setDefaultsOnInsert: true };

    // Find the document
    Subjects.findOneAndUpdate(query, options, function(error, subject) {
        if (error) return;
        console.log("Updated db enry: " + subject);
    });

    return res.send(newSubject);
});

3. 设置select2输入字段:
public/js/select2.js

var dataBase=[];
$(document).ready(function() {
    // Get all subjects from api (populated in step 2) and push to dataBase array
    $.getJSON('/api/subjects/all')
    .done(function(response) {
        $.each(response, function(i, subject){
            dataBase.push({id: subject._id, text: subject.subject});
        })
        console.log("dataBase: " + dataBase);
    })
    .fail(function(err){
        console.log("$.getJSON('/api/subjects/all') failed")
    })

    // Get data from api, and on 'selecting' a subject (.on("select2:select"), check if it's in the dataBase. If it is, or the user confirms they want to add it to the database, send it to POST route, and save it to our Subjects db.

    $('.select2-container')
    .select2({
        ajax: {
        url : "/api/subjects/all",
        dataType: 'json',
        processResults: function (data) {
            return {
                results: $.map(data, function(obj) {
                    return { id: obj._id, text: obj.subject };
                    })
                };
            }
        },
        placeholder: 'Start typing to add subjects...',
        width: 'style',
        maximumSelectionLength: 5,
        multiple: true,

        createTag: function(tag) {
            return {
                id: tag.term,
                text: tag.term.toLowerCase(),
                isNew : true
            };
        },

        tags: true,
        tokenSeparators: [',', '.']
    })
    .on("select2:select", function(e) {
        if(addSubject(dataBase, e.params.data.text)){
            console.log(e.params.data.text + " has been approved for POST");
            ajaxPost(e.params.data.text)
        } else {
            console.log(e.params.data.text + " has been rejected");
            var tags = $('#selectSubject select').val();
            var i = tags.indexOf(e.params.data.text);
            console.log("Tags: " + tags);
            if (i >= 0) {
                tags.splice(i, 1);
                console.log("post splice: " + tags);
                $('select').val(tags).trigger('change.select2');
            }
        }
    })

    function ajaxPost(subject){
        console.log("In ajaxPost");
        var formData = {subject : subject}
        $.ajax({
            type : "POST",
            contentType : "application/json",
            url : "/api/subjects/save",
            data : JSON.stringify(formData),
            dataType : 'json'})
            .done(console.log("Done posting " + JSON.stringify(formData)))
            .fail(function(e) {
                alert("Error!")
                console.log("ERROR: ", e);
            });
    }

    function addSubject(subjects, input) {
        if (!input || input.length < 3) return false

        var allSubjects = [];

        $.each(subjects, function(i, subject){
            if(subject.text) allSubjects.push(subject.text.toLowerCase())
        });

        console.log("Here is the entered subject: " + input);

        if(allSubjects.includes(input)){
            console.log(input + " already exists")
            return true
        }

        if(confirm("Are you sure you want to add this new subject " + input + "?")){
            console.log(input + " is going to be added to the database");
            return true
        } 
        console.log(input + " will NOT to added to the database");
        return false
    }

});

这个方法可行,但我很想听听对这种方法的反馈意见!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,