Записать новые теги опции select2 в локальную базу данных в express

#javascript #jquery #ajax #express #jquery-select2

#javascript #jquery #ajax #выразить #jquery-select2

Вопрос:

Я использую select2 в приложении Express для создания поля ввода, в котором пользователи могут выбирать объекты из списка, и могут обновлять этот список любыми недавно добавленными параметрами.

Проблема, с которой я борюсь, заключается в том, что select2 выполняется на стороне клиента, тогда как любые данные, которые я использую для заполнения своих <option> тегов (к которым я хочу добавить новые параметры), находятся на стороне сервера.

Я хочу, чтобы пользователи могли добавлять темы, которых нет в исходном списке, чтобы будущим пользователям были представлены недавно добавленные опции (а также исходные)

Вот варианты, которые я рассмотрел для достижения этого (для повышения желательности):

  • Добавьте новые <option>Subject</option> html-теги для каждого добавленного тега
  • Поместить новые теги в массив и заполнить <option> s из этого массива
  • Заполнять <option> из json объекта и обновлять этот объект при создании тега
  • Заполнять <option> из внешней базы данных (например, mongoose) и обновлять это при создании тега

Насколько я могу видеть, все эти опции требуют, чтобы мой клиентский код ( select2-js ) взаимодействовал с кодом на стороне сервера (где был бы мой массив, .json файл или mongoose схема), и я понятия не имею, как это сделать.

В моем текущем подходе я пытаюсь указать «локальный» json файл в качестве источника данных в моем select2 вызове (смотрите здесь). Однако при этом база данных не заполняется никакими параметрами, так что это работает не так, как я ожидал.

Затем я проверяю, существует ли каждый новый тег в массиве ( 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
};
  

Однако этот подход добавит новые теги в массив, который будет уничтожен, как только я обновлю страницу, и новые теги не будут сохранены.

Как я могу изменить это, чтобы загружать данные на стороне сервера ( json , mongoose документ или что-либо еще, что считается наилучшей практикой), и обновлять эти данные новыми добавленными параметрами (которые проходят мои тесты)?

Комментарии:

1. Используйте ajax для заполнения json на странице. Затем используйте json для заполнения опции select2, как вы делаете в данный момент. Прикрепите json к document.ready и к обновлению базы данных, чтобы получить свой результат

2. @LelioFaieta — спасибо за указания. Есть ли шанс, что вы могли бы добавить это в качестве ответа?

Ответ №1:

На стороне вашего сервера у вас может быть 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
};
  

Комментарии:

1. Спасибо. Я рассмотрю это сегодня вечером. Является YOUR_SERVER_ADDRESS относительным или абсолютным путем? Нужно ли мне вводить / запрашивать ajax , чтобы это сработало?

2. YOUR_SERVER_ADDRESS — это абсолютный адрес вашего веб-сервера, который обслуживает api. Если на вашем компьютере запущен локальный сервер, он должен быть localhost:PORT_NUMBER . Опять же, PORT_NUMBER зависит от сервера, на котором вы работаете. По умолчанию сервер узла работает на порту 3000. Для этого нужен jquery.

Ответ №2:

Для этого вы можете использовать select2:select и select2:unselect event.

 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>  

Ответ №3:

Вот что у меня получилось (благодаря обоим ответам):

1. Настройте базу данных Mongoose для хранения объектов:

models/subjects.js

 var mongoose = require("mongoose");

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

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

2. Настройте маршруты api в серверной части node js express:
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
    }

});
  

Это работает, но я хотел бы услышать отзывы об этом подходе!