У меня есть приложение AngularJS, и на одной из страниц отображается ряд виджетов. Один из виджетов отображает таблицу, содержащую информацию о системе, которая отслеживается приложением. Эта таблица в настоящее время содержит два столбца: столбец «Тип», отображающий тип информации, отображаемой в этой строке; и столбец «Данные», отображающий значение переменной, содержащей эту информацию.

Можно настроить информацию, отображаемую в виджете, с помощью диалогового окна, которое открывается при нажатии кнопки «Настройки» на панели инструментов виджета. В этом диалоговом окне есть поля ввода текста «Столбцы» и «Строки», которые используются для установки заголовков столбцов и строк, отображаемых в таблице. В настоящее время, когда пользователь начинает вводить текст в поле ввода текста «Строки», отображается раскрывающийся список, показывающий пользователю переменные, которые доступны для отображения в таблице и соответствуют тому, что пользователь ввел (т. Е. По мере того, как они продолжают для ввода, список доступных опций станет более конкретным).

Функция, используемая для отображения этого раскрывающегося списка пользователю при вводе текста в поле ввода текста:

$scope.autocompleteVarsFilter = function(query) {
    if(!query && lastVarQryKw) {
        query = lastVarQryKw;
    }
    var result = Object.keys(fxVar.getVars()).filter(function(name) {
        return name.indexOf(query.toLowerCase()) !== -1;
    });

    if(result.length > 0) {
        lastVarQryKw = query;
    }
    return result;
};

В настоящее время я пытаюсь добавить в эту таблицу третий столбец, чтобы отобразить ссылку на заданную страницу, которую выберет пользователь. Я решил, что "ссылка" будет обозначаться строкой, начинающейся с символа : (т.е. если пользователь вводит :index, это будет ссылка на домашнюю страницу сайта).

Как только пользователь вводит :, полный список доступных страниц должен отображаться в «раскрывающемся списке», и по мере того, как пользователь продолжает вводить текст, этот список следует фильтровать на основе того, что он набирает (как это с именами переменных)

Я обновил свою функцию следующим образом:

$scope.autocompleteVarsFilter = function(query) {
    if(query.startWith(":") {
        var buttonsQuery = query.substring(1);
        if(!buttonsQuery && lastVarQryKw) {
            buttonsQuery = lastVarQryKw;
        }

        var userPages = pagesPresets; //This is getting the full list of available pages from a service called 'pagesPresets'
        console.log("Value of userPages: ", userPages);

        var page;
        for(page in userPages) {
            console.log("for loop started");
            if(page.key.startWith(buttonsQuery)) {
                console.log("Matching page found: ", page);
            }
        }

        if(result.length > 0) {
            lastVarQryKw = query;
        }
        return result;

    } else {
        if(!query && lastVarQryKw) {
            query = lastVarQryKw;
        }
        var result = Object.keys(fxVar.getVars()).filter(function(name) {
            return name.indexOf(query.toLowerCase()) !== -1;
        });

        if(result.length > 0) {
            lastVarQryKw = query;
        }

        return result;
    }
};

Предложение else в операторе if выполняет то, что функция изначально делала (т. Е. Отображает доступные переменные), и в настоящее время все еще работает по назначению. Однако при тестировании этой функции, если я сейчас наберу :, а затем любую строку в поле ввода текста, будет выполнено предложение if, но доступные страницы не будут отображаться в раскрывающемся списке, и я вижу следующий вывод, отображаемый в консоли моими операторами console.log():

Значение userPages:

0: {_id: "...", _rev: 1, _url: "...", ключ: "pages / auth", {...}, ...}

1: {_id: "...", _rev: 13, _url: "...", ключ: "pages / userpage2", значение: {...}, ...}

2: {_id: "...", _rev: 13, _url: "...", ключ: "pages /", значение: "/ pages / userpage1", ...}

цикл начался

TypeError: невозможно прочитать свойство startWith из undefined

Проблема, похоже, в том, что я не могу ссылаться на атрибут / свойство key объектов пользовательской страницы, чтобы сопоставить их значение со значением, которое пользователь вводит в текстовое поле.

Учитывая, что я вижу, что используемые мной объекты page имеют атрибут key, содержащий значение, которое я хочу фильтровать, как мне на самом деле получить этот атрибут key для используй это?

Я попытался изменить этот цикл for на:

var page;
var key;
for(page in userPages) {
    console.log("for loop started");
    key = page.getAttribute("key");
    if(key.startsWith(buttonsQuery)) {
        console.log("Matching page found: ", page);
    }
}

Но это просто дает результат:

цикл начался

TypeError: page.getAttribute не является функцией

У кого-нибудь есть предложения?

Изменить

Я попытался сделать то, что предложил @Alex K в их ответе, и обновил цикл for в своей функции, чтобы:

var page;
var pageKey;
var key;
var result;

console.log("Iterating using foreach");
for(page in userPages) {
    page = userPages[page];
    if(page.key.startsWith(buttonsQuery)) {
        console.log("Matching page found: ", page.key);
        pageKey = page.key;
        result = pageKey;
    }
};

Я также пробовал обновить его до:

userPages.forEach(function(page) {
    page = userPages[page];
    if(page.key.startsWith(buttonsQuery)) {
        console.log("Matching page found: ", page.key);
        pageKey = page.key;
        result = pageKey;
    }
});

А также:

userPages.forEach(function(page) {
    page = userPages[page];
    if(page.key.startsWith(buttonsQuery)) {
        console.log("Matching page found: ", page.key);
        pageKey = page.key;
        result = pageKey;
    }
});

Но все они имеют одинаковый вывод в консоли:

Итерация с использованием foreach

ctrl.js: 483 Соответствующая страница найдена: pages / userpage2

ctrl.js: 483 Соответствующая страница найдена: pages / userpage1

ctrl.js: 483 Соответствующих страниц найдено: страниц /

ctrl.js: 483 Соответствующая страница найдена: pages / pagetitle

ctrl.js: 483 Соответствующая страница найдена: pages / auth

angular.js:10160 TypeError: a.forEach is not a function
at Object.b.makeObjectArray (ng-tags-input.min.js:1)
at ng-tags-input.min.js:1
at wrappedCallback (angular.js:11735)
at wrappedCallback (angular.js:11735)
at angular.js:11821
at Scope.$eval (angular.js:12864)
at Scope.$digest (angular.js:12676)
at Scope.$apply (angular.js:12968)
at angular.js:14494
at completeOutstandingRequest (angular.js:4413)

Стало ясно, что строка внутри оператора if выполнялась, потому что я получал все эти операторы печати в консоли, и все же по какой-то причине он думает, что forEach не является функцией .. .

У кого-нибудь есть предложения, как это исправить?

0
Noble-Surfer 16 Ноя 2017 в 17:39

1 ответ

Лучший ответ

Цикл for...in < / a> выполняет итерацию по перечислимому свойству именам объекта . Затем вам нужно получить доступ к объекту, используя это имя свойства, чтобы получить значение. Вот пример:

console.log('Using for...in on an object:');

var myObj = {
  'prop1': 'Hello world!',
  'prop2': 12345,
  'some crazy property': null
};

for (var propName in myObj) {

  console.log('propName is ' + propName);

  //Now we can lookup the value using that property name.
  var propValue = myObj[propName];

  console.log('myObj[' + propName + '] is ' + propValue);

}

Напротив, цикл forEach выполняет итерацию непосредственно по значениям массива . Ваша функция обратного вызова получает значение напрямую, поэтому вам не нужно обращаться к массиву для его получения. Пример:

console.log('Using forEach on an array:');

var myArray = [
  {key: 'this is object 1!'},
  {key: 'this is object 2!'}
];

myArray.forEach(function (obj) {
  
  console.log('in ForEach loop...');
  
  console.log('obj is ' + JSON.stringify(obj));
  
  //We can directly access the object's properties as needed
  console.log('obj.key is ' + obj.key);
  
});

Итак, вот как вы можете обновить свой код, используя for...in или используя forEach:

var buttonsQuery = 'pages/userpage2';

var userPages = [{
    _id: "...",
    _rev: 1,
    _url: "...",
    key: "pages/auth"
  },
  {
    _id: "...",
    _rev: 13,
    _url: "...",
    key: "pages/userpage2"
  },
  {
    _id: "...",
    _rev: 13,
    _url: "...",
    key: "pages/",
    value: "/pages/userpage1"
  }
]

console.log('Iterating using for...in');
for (var prop in userPages) {

  console.log('prop is ' + prop);

  //Access the page object using the property name
  var page = userPages[prop];

  console.log('page is ' + JSON.stringify(page));

  if (page.key.startsWith(buttonsQuery)) {
    console.log("Matching page found: ", page);
  }
}


//Better way to iterate if userPages is an array:
console.log('iterating using forEach');
userPages.forEach(function(page) {

  //page is already the object we're interested in.
  //No need to access the array again.
  console.log('page is ' + JSON.stringify(page));

  //Access the object's properties as needed
  if (page.key.startsWith(buttonsQuery)) {
    console.log("Matching page found: ", page);
  }
});

Вы также можете использовать { {X0}} для итерации, но это не поддерживается в IE.

1
Alex K 17 Ноя 2017 в 14:15