Bootstrap Typeahead с источником AJAX: массив не возвращается, как ожидалось

У меня много проблем с тем, чтобы загрузочные шрифты работали правильно с источником AJAX.

Если я предупрежу возвращаемый массив, все будет в порядке, даже если я жестко запрограммирую его для проверки. Кажется, что в большинстве случаев он ничего не возвращает или только несколько элементов из массива, когда вы ввели близкое совпадение.

Вот что у меня есть на данный момент:

$('#companyNameInput').typeahead({
      source: function(query, process){

          $.ajax({
            url: ROOT+'Record/checkCompanyName',
            async: false,
            data: 'q='+query,
            type: 'POST',
            cache: false,
            success: function(data)
            {
              companiesFinal = [];
              map = {};
              companies = $.parseJSON(data);
              $.each(companies, function(i, v){
                map[v.name] = v.id;
                companiesFinal.push(v.name);
              })
            }
          })
          process(companiesFinal);

          // return ['test1', 'test2'] This works fine
          return companiesFinal;
      }

Кто-нибудь знает, почему это работает правильно?


Вот пример массива объектов, возвращенного из моего PHP-скрипта. Объекты с идентификаторами 1 и 1216 отображаются в раскрывающемся списке ввода, но не другие. Я не вижу никаких закономерностей или подсказок, почему будут отображаться только они, а не другие.

[
   {
      "id": "1265",
      "score": "40",
      "name": "LMV AB"
   },
   {
      "id": "10834",
      "score": "33",
      "name": "Letona"
   },
   {
      "id": "19401",
      "score": "33",
      "name": "Lewmar"
   },
   {
      "id": "7158",
      "score": "33",
      "name": "Lazersan"
   },
   {
      "id": "3364",
      "score": "33",
      "name": "Linpac"
   },
   {
      "id": "1216",
      "score": "33",
      "name": "L H Evans Limted"
   },
   {
      "id": "1",
      "score": "33",
      "name": "LH Evans Ltd"
   },
   {
      "id": "7157",
      "score": "33",
      "name": "Lazersan"
   }
]

И, наконец, массив, прошедший в process(companiesFinal):

["LMV AB", "Letona", "Lewmar", "Lazersan", "Linpac", "L H Evans Limted", "LH Evans Ltd", "Lazersan"]

Кто-нибудь знает? Я до сих пор совершенно не понимаю, почему это все еще не работает :(


$('#companyNameInput').typeahead({
      source: function(query, process){

        companyTOut = setTimeout(function(){
          return $.ajax({
            url: ROOT+'Record/checkCompanyName',
            data: 'q='+query,
            type: 'POST',
            dataType: 'json',
            cache: false,
            success: function(data)
            {
              var count = 0;
              var companiesFinal = [];
              map = [];
              $.each(data, function(i, v){
                map[v.name] = [v.id, v.score];
                companiesFinal.push(v.name);
              })
              process(companiesFinal);

            }
          })
        }, 250)
      },
      minLength: 2,
      highlighter: function(item)
      {
        $('#companyNameInput').closest('.control-group').removeClass('success')
        companyLocked = false;
        return '<span class="unselectable" title="'+map[item].score+'">'+item+'</span>';
      },
      updater: function(item)
      {
        selectedEntityId = map[item][0];
        selectedCountryScore = map[item][1];
        lockCompany(selectedEntityId);
        return item;
      }
    });

$output .= '['.$output;
    foreach($results as $result) {
      $output .= '{"id":"'.$result['id'].'",';
      $output .= '"score":"'.$result['score'].'",';
      $output .= '"name":'.json_encode($result['name']).'},';
    }
    header('Content-Type: application/json');
    echo substr($output, 0, strlen($output)-1).']';

Консольный вывод для «parm»:

[Object, Object, Object, Object, Object, Object]
0: Object
id: "25024"
name: "part"
score: "75"
__proto__: Object
1: Object
id: "15693"
name: "pari"
score: "75"
__proto__: Object
2: Object
id: "28079"
name: "Pato"
score: "50"
__proto__: Object
3: Object
id: "18001"
name: "PASS"
score: "50"
__proto__: Object
4: Object
id: "15095"
name: "PSR"
score: "33"
__proto__: Object
5: Object
id: "22662"
name: "PRP"
score: "33"
__proto__: Object
length: 6
__proto__: Array[0]

person imperium2335    schedule 14.03.2013    source источник
comment
Нет мешка, чтобы оправдать голосование против?   -  person imperium2335    schedule 14.03.2013
comment
Я еще немного поэкспериментировал и обнаружил, что выбор элемента из раскрывающегося списка с помощью мыши не всегда работает, что приводит к описанному вами поведению. Не могли бы вы проверить, помогает ли выбор элементов с помощью клавиш up, down и enter?   -  person Marijn    schedule 18.03.2013
comment
@Marijn Проблема в том, что в раскрывающемся списке есть только один или два элемента, когда их должно быть много, поэтому с помощью клавиш со стрелками не так много можно выбрать :(   -  person imperium2335    schedule 19.03.2013
comment
хммм ... не могли бы вы вставить console.log(data) в первую строку вашей функции успеха, чтобы (снова) подтвердить, что возвращаются правильные данные?   -  person Marijn    schedule 19.03.2013
comment
@Marijn Пожалуйста, смотрите редактирование для вывода консоли.   -  person imperium2335    schedule 19.03.2013
comment
Ах, думаю, теперь я понял. Смотрите мой обновленный ответ.   -  person Marijn    schedule 19.03.2013


Ответы (1)


Обновление 2

А, ваша служба возвращает элементы, которые на самом деле не соответствуют запросу parm. Ваш запрос с опережающим вводом — 'parm', которому не соответствует ни один из возвращаемых результатов. Вы можете переопределить функцию matcher, используемую подключаемым модулем typeahead, см. документацию. Просто реализуйте его как return true, чтобы сопоставить все результаты, возвращаемые вашей службой.

Обновление 1

Это обновленная версия, которая сопоставляет имя с идентификатором, который можно использовать позже. Доступен jsfiddle.

var nameIdMap = {};

$('#lookup').typeahead({
    source: function (query, process) {
        return $.ajax({
            dataType: "json",
            url: lookupUrl,
            data: getAjaxRequestData(),
            type: 'POST',
            success: function (json) {
                process(getOptionsFromJson(json));
            }
        });
    },
    minLength: 1,
    updater: function (item) {
        console.log('selected id'+nameIdMap[item]);
        return item;
    }
});

function getOptionsFromJson(json) {
    $.each(json, function (i, v) {
        nameIdMap[v.name] = v.id;
    });

    return $.map(json, function (n, i) {
        return n.name;
    });
}

Оригинальный ответ

Вам нужно сделать вызов асинхронным и вызвать обратный вызов process из обратного вызова успеха следующим образом:

$('#companyNameInput').typeahead({
    source: function (query, process) {
        $.ajax({
            url: ROOT + 'Record/checkCompanyName',
            // async: false, // better go async
            data: 'q=' + query,
            type: 'POST',
            cache: false,
            success: function (data) {
                var companiesFinal = ... // snip
                process(companiesFinal);
            }
        })
    }
});

return ['test1', 'test2']; работает, потому что исходная функция в основном устанавливается на:

// do ajax stuff, but do nothing with the result
// return the typeahead array, which the typeahead will accept as the result:
return ['test1', 'test2'];

Примечания

Для заполнения companiesData есть однострочник:

var companiesFinal = return $.map(data, function (n, i) { n.name; });

И вы, вероятно, захотите объявить свои переменные, используя var; в противном случае они будут иметь глобальную область действия, которая вас укусит.

person Marijn    schedule 15.03.2013
comment
Я переместил процесс в обратный вызов успеха функции ajax, но я все еще получаю точно такие же результаты :( См. редактирование для обновленной версии. - person imperium2335; 16.03.2013
comment
Тип ответа вашего метода PHP установлен на application/Json? - person Marijn; 16.03.2013
comment
Да, я отредактирую ответ, чтобы показать вам часть, которая создает JSON. - person imperium2335; 16.03.2013
comment
У меня есть некоторые трудности с пониманием вашего кода javascript, но я создал для вас эту скрипку, которую я < i>think делает то, что вам нужно, а именно асинхронно извлекает данные typehead и сопоставляет выбранный элемент с его соответствующим идентификатором. Надеюсь, это поможет! - person Marijn; 16.03.2013
comment
Спасибо, что сделал это! Что-то такое маленькое. Вот ваши 50 очков любви :D - person imperium2335; 19.03.2013
comment
Я предполагаю, что этот вариант использования был немного необычным, поскольку он больше похож на ближайший сопоставитель, а не на ввод. Использует levenshtein и т. д., чтобы вернуть совпадения в моем PHP-скрипте вместе с некоторыми другими битами и бобами. - person imperium2335; 19.03.2013
comment
На самом деле, я думаю, что ваш вопрос как раз тот сценарий, для которого предназначена пользовательская функция matcher. Универсальный сопоставитель из нашего решения идеально подходит для интеллектуального автозаполнения на стороне сервера. Кстати, вас также может заинтересовать библиотека select2. Я использовал это с загрузкой Twitter в аналогичном сценарии. - person Marijn; 19.03.2013