Научитесь использовать API, Vue.js, SCSS и Promises и писать семантический HTML5.
Шаги, которые мы предпримем вместе
- Разбейте виджет, чтобы понять, какие HTML-элементы нам следует использовать.
- Изучите, протестируйте и зарегистрируйтесь в службе API, которая предоставит нам необходимые данные.
- Добавьте фиктивные данные из WordsAPI в наш HTML-шаблон, чтобы проверить его структуру.
- Используйте Vue.js, чтобы быстро создать прототип нашего виджета с использованием данных из API.
- По частям заменяйте жестко запрограммированный текст данными из нашей модели Vue.
- Протестируйте наше приложение и обновите части, чтобы учесть данные, полученные от API, для любого слова.
- Стиль нашего приложения с использованием SCSS имитирует виджет Google.
Разбейте виджет, чтобы понять, какие HTML-элементы нам следует использовать
Вот разбивка
- У виджета есть заголовок «Словарь».
- Затем краткая форма: кнопки
input
иsubmit
. - Результатом поиска является список определений с одним
dt
иdd
для каждого набора существительных и глаголов. - Внутри каждого
dd
находитсяol
с элементами списка для каждого существительного или глагола. - Каждый
li
будет содержать определение и может включать термин, используемый в предложении, и один или несколько синонимов. Определение -p
. Предложение представляет собойq
, заключенное вp,
, а список синонимов - это еще одинdl
.
Вот HTML-код с заполнителями, отмеченными {{ }}
<div id="app"> <h1>Dictionary</h1> <input type="text" /> <button>Search</button> <dl> <dt> <p>{{syllables}}</p> <p>{{pronunciation}}</p> </dt> <dd> <p>noun</p> <ol> <li> <p>{{definition}}</p> <p> <q>{{sentence}}</q> </p> <dl> <dt>synonyms</dt> <dd>{{synonym}}</dd> </dl> </li> </ol> </dd> <dd> <p>verb</p> <ol> <li> <p>{{definition}}</p> <p> <q>{{sentence}}</q> </p> <dl> <dt>synonyms</dt> <dd>{{synonym}}</dd> </dl> </li> </ol> </dd> </dl> </div>
Исследование, тестирование и подписка на службу API, которая предоставит нам необходимые данные
В левом нижнем углу виджета Словаря Google есть надпись From Oxford. Это привело меня к Google oxford dictionary api
и, таким образом, к официальному Oxford Dictionaries API.
К сожалению, я обнаружил два узких места:
- У API есть связанная стоимость всякий раз, когда он вызывается. Я бы предпочел не платить только за несколько вызовов API.
- Не похоже, что API возвращает синонимы, которые являются ключевой частью виджета, который мы пытаемся воссоздать.
Скорее всего, я недостаточно прочитал об этом веб-сайте, а то, что я считаю узкими местами, на самом деле не так.
Но когда в другом поиске Google по запросу dictionary api
было обнаружено простое английское название ресурса WordsAPI без каких-либо узких мест, описанных выше, мое решение было легко принять.
Для использования WordsAPI и почти всех API-интерфейсов требуется ключ.
Ключ используется, помимо прочего, для отслеживания количества запросов, сделанных за день.
Чтобы получить свой ключ WordsAPI, вы должны:
- Зарегистрируйтесь в RapidAPI, зонтичном сервисе, через который вы можете подписаться на WordsAPI.
- Введите платежную информацию на случай, если вы превысите лимит уровня бесплатного пользования, и они должны будут взимать с вас плату.
- Подпишитесь на WordsAPI, чтобы любые запросы, которые вы делаете к API, могли быть проверены, и вы получали реальные данные вместо надоедливого сообщения об ошибке.
Добавьте фиктивные данные из WordsAPI в наш HTML-шаблон, чтобы проверить его структуру
Интересующие части ответа, возвращенного на этот запрос API, представлены ниже.
{ "word": "program", "results": [ { "definition": "an integrated course of academic studies", "partOfSpeech": "noun", "synonyms": [ "course of study", "curriculum", "programme", "syllabus" ], "examples": [ "he was admitted to a new program at the university" ] }, // more result objects ], "syllables": { "count": 2, "list": [ "pro", "gram" ] }, "pronunciation": { "all": "'proʊgræm" } }
На этом этапе мы должны ответить на вопрос: «Настроили ли мы наш HTML-шаблон для эффективного отображения каждого блока данных, показанных выше?»
И снова наш шаблон
<div id="app"> <h1>Dictionary</h1> <input type="text" /> <button>Search</button> <dl> <dt> <p>{{syllables}}</p> <p>{{pronunciation}}</p> </dt> <dd> <!-- will repeat for each result that is a noun --> <p>noun</p> <ol> <li> <p>{{definition}}</p> <p> <q>{{sentence}}</q> <!-- repeat for each example --> </p> <dl> <dt>synonyms</dt> <dd>{{synonym}}</dd> <!-- repeat for each synonym --> </dl> </li> </ol> </dd> <dd> <!-- will repeat for each result that is a verb --> <p>verb</p> <ol> <li> <p>{{definition}}</p> <p> <q>{{sentence}}</q> </p> <dl> <dt>synonyms</dt> <dd>{{synonym}}</dd> <!-- repeat for each synonym --> </dl> </li> </ol> </dd> </dl> </div>
{{ syllables }}
можно получить из массиваsyllables.list
.{{ pronunciation }}
передан нам из одноименной собственности.{{ sentence }}
можно получить из массиваexamples
каждого результата.- Каждый
{{ synonym }}
может быть получен из массиваsynonyms
. {{ definition }}
передан нам из одноименной собственности.- И мы можем правильно группировать существительные и глаголы на основе свойства
partOfSpeech
для каждого результата.
Наш HTML-шаблон готов. Теперь давайте создадим экземпляр Vue и поработаем над отображением данных из объекта ответа в этом шаблоне.
Используйте Vue.js для быстрого создания прототипа нашего виджета с использованием данных из API
Давайте помнить на этом этапе, что:
- Создавая прототип, мы хотим работать быстро.
- Наша цель - заставить что-то работать, только для демонстрации и обучения.
- На этом этапе нас не обязательно заботит использование передового опыта, написание кода, готового к производству, или учет всех крайних случаев.
Учитывая это понимание, я считаю, что Vue.js, прогрессивный JavaScript-фреймворк дает мне возможность работать быстрее всего при создании прототипов таких виджетов.
Как добавить Vue.js в свой проект
Используете редактор кода с загруженным HTML-файлом? Добавьте тег script
непосредственно перед закрывающим тегом </body>
с атрибутом src
, установленным для одного из множества CDN, доступных для Vue.js, например:
... <script src="https://unpkg.com/[email protected]/dist/vue.js" /> </body> </html>
Пользуетесь сервисом вроде Codepen? Нажмите на ручке шестеренку рядом с JS и найдите vue. Он должен автоматически заполнить список, первым из которых является Vue.js. Выберите это, чтобы добавить к ручке. Сохрани и закрой.
Как начать прототипирование с Vue.js
Vue
- это функция-конструктор: это функция, которая создает объект. Это означает, что для использования Vue мы должны включить ключевое слово new
, набор ()
круглых скобок и сохранить то, что возвращается в переменной, как это.
var app = new Vue();
Как и большинство функций, Vue ожидает один или несколько аргументов, когда вы его вызываете. Для наших целей мы передадим единственный аргумент - объект.
var app = new Vue( {} );
Этот объект будет иметь несколько свойств, каждое из которых соответствует ожиданиям Vue. Начнем со свойства, которое подключает Vue к нашему виджету. Напомним, что в нашем HTML-шаблоне я заключил все в <div>
с id
из app
.
<div id="app"> ... </div>
Я сделал это для Vue. Vue подключается к одному элементу HTML, и проще всего использовать id
при нацеливании на элемент.
var app = new Vue({ el: "#app" });
Мы добавили одну пару key: value
к объекту, который передаем в функцию конструктора Vue. key
- это el
, сокращение от element
, а значение - "#app"
, строка, содержащая селектор CSS, нацеленный на наш div
.
И вот так наш div
теперь является экземпляром Vue! К сожалению, пока ничего не происходит. Давай исправим это.
Как добавить состояние по умолчанию в наш экземпляр Vue
Давайте добавим к объекту еще одну key: value
пару. Эта пара играет важную роль в экземпляре Vue: она хранит состояние нашего приложения. key
называется data
, а его значением является объект, как показано ниже.
var app = new Vue({ el: "#app", data: {} });
Внутри этого объекта мы можем добавлять key: value
пары с любыми именами, которые захотим, поскольку они уникальны для нашего приложения и не диктуются или не ожидаются Vue.
Нам нужно сохранить только два фрагмента данных: ответ на наш запрос API и слово, которое мы хотим найти.
Нам нужно установить начальные значения по умолчанию для каждого элемента, чтобы Vue мог правильно регистрировать и отслеживать любые будущие изменения для каждого элемента.
var app = new Vue({ el: "#app", data: { response: null, word: "" } });
Я выбрал интуитивно понятные метки для каждого ключа объекта данных, response
для ответа API и word
для слова, которое мы хотим найти.
Как настроить запрос WordsAPI как часть нашего экземпляра Vue
Этот шаг состоит из трех частей:
- Нам нужно отправить запрос GET в WordsAPI, который включает слово, которое мы хотим найти.
- Когда мы получаем ответ от API, мы хотим преобразовать его в JSON, чтобы мы могли выполнять с ним операции.
- После преобразования в JSON мы хотим сохранить объект ответа в свойстве
response
объектаdata
, который мы инициализировали сnull
в качестве значения.
Все это мы сделаем внутри function
. Эта функция будет сохранена в нашем экземпляре Vue, потому что Vue будет вызывать ее в соответствующее время.
Приступим к написанию функции:
function handleAPIRequest() { ... }
Это длинное имя функции, но совершенно ясно, что она делает: она обрабатывает наш запрос API.
Удобный встроенный способ отправки GET-запросов - Fetch API.
В статье Использование Fetch в сети разработчиков Mozilla предлагается полезный базовый пример:
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
Что тут происходит? Ну много.
fetch
- этоfunction
, который вызывается с одним аргументом: строка, содержащая URL-адрес, в данном случае указывающий на файл JSON.- Следующая строка фактически является продолжением первой: вызов функции
fetch
возвращает объект. Мы немедленно вызываем методthen
(он же функция), передавая ему единственный аргумент:function
. - Эта функция - та, которая передана в качестве аргумента для вызова функции
then
- при вызове ожидает единственный параметр - в данном случае помеченный какresponse
. Функция выполняет единственную команду: вернуть результат вызоваjson
на себя. Эта команда эффективно преобразует строку текста, отправленную запросом API, в объект JavaScript, с которым мы предпочитаем работать. - Следующая строка - это еще одно продолжение вызова функции
fetch
в первой строке. Он работает аналогично предыдущему вызову функцииthen
. Переданная ему функция будет ожидать один параметр. Он получит объект JSON, возвращенный из предыдущего вызова функции. - Мы заменим команду
console.log()
на команду, которая сохраняет объект JSON в свойствеresponse
в объектеdata
нашего экземпляра Vue.
Выражаясь более простыми, но продвинутыми терминами, Fetch возвращает обещание. Каждый из вызовов функции then
регистрирует последующие действия, которые должны быть выполнены, если и когда обещание успешно разрешится.
Как мы можем изменить образец кода в соответствии с нашими потребностями?
Четырьмя важными способами:
- Нам нужно отправить запрос на другой URL.
- Нам нужно добавить часть данных к этому URL-адресу.
- Нам нужно настроить этот запрос перед отправкой, чтобы WordsAPI мог правильно проверить наш запрос.
- Нам нужно сохранить возможный JSON обратно в наш экземпляр Vue.
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, { headers: { "X-Mashape-Key": "someCrazyLongAPIKeyThatContainsNumbersAndLetters" } }) .then(function(response) { return response.json() }) .then(function(myJson) { this.response = myJson })
Какой URL использовать?
Мы получаем это из WordsAPI. В их документации указано, что при поиске слова мы используем этот URL: https://wordsapiv1.p.mashape.com/words/
Как мы добавляем наши данные к этому URL-адресу?
Этот URL-адрес ожидает один последний бит после части words/
: слово, которое мы хотим найти. В нашем экземпляре Vue это значение будет сохранено в свойстве с меткой word
внутри объекта data
. Vue дает нам удобный способ ссылаться на любое значение, хранящееся в объекте data
: мы пишем this.word
вместо this.data.word
или получаем доступ к слову другими способами.
Мы можем использовать template literal
, чтобы четко указать полный URL-адрес, включая наши данные. Литералы шаблонов окружены обратными галочками: нижний символ на клавише слева от 1
на большинстве клавиатур.
Литералы шаблона позволяют нам вставлять оцененные данные в строку, используя следующий синтаксис: ${ expression }
.
Следовательно, строка, которую мы передаем fetch
, будет выглядеть так:
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`)
Как мы отправляем требуемый ключ API с нашим запросом, чтобы WordsAPI не возвращал ошибку о недопустимом доступе?
Вызов функции fetch
принимает второй параметр: объект, используемый для настройки запроса API.
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, {})
Этот объект допускает множество возможных key: value
пар. Нам нужно установить только один, headers
.
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, { headers: {} })
WordsAPI требует, чтобы мы добавили одну пару key: value
к объекту headers
. Ключ - X-Mashape-Key
, а значение - длинная строка символов, которая присваивается вашей учетной записи при подписке на WordsAPI.
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, { headers: { "X-Mashape-Key": "yourAPIKeyThatContainsNumbersAndLetters" } })
Теперь размещение этого вызова API в контексте нашей функции выглядит так:
function handleAPIRequest() { fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, { headers: { "X-Mashape-Key": "yourAPIKeyThatContainsNumbersAndLetters" } }) }
Напомним, что пример кода MDN содержал два связанных вызова then
:
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
Мы можем скопировать первый then
вызов точно так, как написано.
function handleAPIRequest() {
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, {
headers: {
"X-Mashape-Key": "yourAPIKeyThatContainsNumbersAndLetters"
}
})
.then(function(response) {
return response.json();
})
}
Нам нужно изменить второй вызов then
, чтобы сохранить JSON обратно в свойство response
нашего экземпляра Vue.
function handleAPIRequest() {
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, {
headers: {
"X-Mashape-Key": "yourAPIKeyThatContainsNumbersAndLetters"
}
})
.then(function(response) {
return response.json();
})
.then(function(body) {
this.response = body;
})
}
Как и в случае с word
, мы можем удобно ссылаться на любые свойства, хранящиеся внутри объекта data
Vue, через this.
, а затем имя свойства, вместо того, чтобы вставлять this.data.
или каким-либо другим способом.
Если вы предпочитаете использовать новые =>
функции стрелок JavaScript, ваша handleAPIRequest
функция будет выглядеть так:
function handleAPIRequest() {
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, {
headers: {
"X-Mashape-Key": "yourAPIKeyThatContainsNumbersAndLetters"
}
})
.then(response => response.json())
.then(body => this.response = body)
}
Где мы поместим эту функцию во Vue?
В настоящее время у вас есть две key: value
пары в объекте конфигурации Vue: el
и data
. Давайте добавим еще один, который распознает Vue: methods
. Значение - это объект, в котором хранится одна или несколько функций.
var app = new Vue({ el: "#app", data: { response: null, word: "" }, methods: {} });
Наша функция handleAPIRequest
может быть одним из значений свойства этого объекта, например:
var app = new Vue({
el: "#app",
data: {
response: null,
word: ""
},
methods: {
handleAPIRequest: function () {
fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, {
headers: {
"X-Mashape-Key": "yourAPIKey"
}
})
.then(response => response.json())
.then(body => this.response = body)
}
}
});
Как мы запускаем запрос API с любым словом, которое нам нужно?
Вернемся к части нашего HTML-шаблона:
<div id="app> <h1>Dictionary</h1> <input type="text" /> <button>Search</button> ... </div>
Нам нужно сделать две вещи:
- Свяжите
input
со свойствомword
в нашем объектеdata
. - Добавьте к кнопке прослушиватель событий, чтобы при нажатии вызывалась функция
handleAPIRequest
.
Свяжите input
со свойством word
в нашем объекте data
Когда вы вводите текст в поле input
, этот текст должен быть сохранен в объекте data
вашего Vue как значение, повторно присвоенное свойству word
. Кроме того, все, что хранится в word
, всегда должно отображаться как значение поля input
.
Это называется двусторонней привязкой данных, и Vue предлагает очень удобный синтаксис для ее создания: директива v-model
.
Директивы Vue удобно записывать как поддельные атрибуты HTML с префиксом v-
.
Директива model
Vue, как и многие другие, написана так:
<input v-model="word" type="text" />
Внутри кавычек находится любое допустимое выражение JavaScript (также известное как все, что вы обычно пишете в правой части оператора присваивания, =
)
Нам нужно только поместить четырехсимвольную строку word
между кавычками, как показано выше. Теперь начало нашего HTML-шаблона выглядит так:
<div id="app> <h1>Dictionary</h1> <input v-model="word" type="text" /> <button>Search</button> ... </div>
Добавьте к кнопке прослушиватель событий, чтобы при нажатии вызывалась функция handleAPIRequest
Во Vue есть директива, предназначенная для прослушивания событий DOM: on
.
Его синтаксис сначала немного странный.
<button v-on:click="handleAPIRequest">Search</button>
- Он имеет префикс
v-
. - Затем имя директивы
on
. - Затем двоеточие, обозначающее, что то, что следует далее, является аргументом (как при вызове функции).
- Затем название аргумента. В этом случае это должно быть действительное событие DOM, например
click
. - Затем обычный знак равенства, котировка открытия и котировка закрытия.
- Между кавычками идет выражение JavaScript. В этом случае мы вводим имя функции, которая должна быть вызвана. Присмотритесь: мы не вызываем функцию (скобок нет).
Теперь наш шаблон выглядит так:
<div id="app> <h1>Dictionary</h1> <input v-model="word" type="text" /> <button v-on:click="handleAPIRequest">Search</button> ... </div>
Как отобразить различные части объекта ответа в нашем шаблоне
Возвращаясь к нашему шаблону, есть несколько элементов, которые нам нужно отобразить, и каждый из них будет поступать из JSON, возвращенного из нашего запроса API:
{{ syllables }}
{{ pronunciation }}
{{ definition }}
{{ sentences }}
и{{ synonyms }}
для каждого существительного или глагола
Vue предлагает два способа хранения данных, один зависит от другого:
- В объекте
data
хранятся все необработанные данные - Объект
computed
может хранить данные, которые каким-то образом получены из значений, хранящихся вdata
Например, data
может хранить массив [1, 2, 3, 4, 5]
. Если бы где-то в моем шаблоне я хотел отобразить нечетные числа, которые являются подмножеством этого массива [1, 3, 5]
, я бы использовал свойство computed
, которое является отфильтрованной копией этого массива.
В нашем случае каждая из вышеперечисленных меток, которые мы хотим отобразить в нашем шаблоне, может быть получена из объекта JSON, который мы будем хранить в response
внутри объекта data
. Поэтому мы создадим computed
свойств для каждого из них. Каждое свойство computed
по существу будет хранить значения, взятые из response
.
Давайте начнем со слогов, чтобы разобраться в вещах
Помните нашу цель:
- Переводить данные из одной структуры в другую - от того, как они структурированы ответом API, до того, как мы хотим представить их в нашем шаблоне.
Для слогов нам дан массив:
"syllables": { "count": 2, "list": [ "pro", "gram" ] },
… И мы хотим, чтобы в нашем шаблоне отображалось pro•gram
.
- Мы должны перейти от массива
list
к строке. - Элементы массива будут объединены (или объединены) с помощью маркера • в качестве клея.
list
- это свойство родительского объекта syllables
, которое является свойством родительского объекта, которое было возвращено нашим запросом API и сохранено в свойстве data
объекта нашего экземпляра Vue response
.
Все это, чтобы сказать ... вот как мы будем обращаться к list
внутри нашего экземпляра Vue:
this.response.syllables.list
Все массивы в JavaScript имеют доступ к функции join
. При вызове он объединяет каждый элемент в массиве, используя символ, переданный в качестве аргумента функции. Это как раз та функция, которая нам нужна для достижения нашей цели:
this.response.syllables.list.join("•") // "pro•gram"
Мы знаем, как выполнить наш перевод. Теперь давайте разместим эту команду внутри Vue как свойство computed
.
Вспомните форму нашего экземпляра Vue:
var app = new Vue({ el: "#app", data: {...}, methods: { handleAPIRequest: function() {...} } });
Свойство computed
Vue работает очень похоже на methods
: оно хранит объект, все свойства которого указывают на функции.
Давайте добавим computed
и его первую key: value
пару для хранения syllables
:
var app = new Vue({ el: "#app", data: {...}, methods: { handleAPIRequest: function() {...} }, computed: { syllables: function() { return this.response.syllables.list.join("•"); } } });
Есть одно предостережение, которое мы должны учитывать
Прежде чем мы найдем слово, this.response
будет его значением по умолчанию, null
.
Следовательно, JavaScript выдаст ошибку при попытке доступа к this.response.syllables
, потому что null.syllables
не существует.
Нам нужно управлять потоком JavaScript:
- Если
this.response
равноnull
, верните, скажем, пустой массив (на самом деле подойдет любой пустой объект). - В противном случае (например, когда
this.response
является возвращенным объектом JSON), вернитеthis.response.syllables.list.join("•")
.
Чтобы сделать это кратко, мы будем использовать JavaScript ternary operator
, который принимает эту псевдоформу:
(is this true) ? [yes: do this] : [no: do this instead]
Другими словами, короче:
condition ? true : false
Наша обновленная функция syllables
выглядит так:
var app = new Vue({ el: "#app", data: {...}, methods: { handleAPIRequest: function() {...} }, computed: { syllables: function() { return this.response === null ? [] : this.response.syllables.list.join("•"); } } });
Если this.response
равно null
, вернуть пустой массив. В противном случае верните вызов нашей join
функции.
Ниже приведены все четыре свойства computed
и соответствующие им значения функций, как своего рода перемотка вперед:
var app = new Vue({ el: "#app", data: {...}, methods: { handleAPIRequest: function() {...} }, computed: { syllables: function() { return this.response === null ? [] : this.response.syllables.list.join("•"); }, pronunciation: function() { return this.response === null ? [] : `/${this.response.pronunciation.all}/` }, nouns: function() { return this.response === null ? [] : this.response.results.filter(result => result.partOfSpeech === "noun" }, verbs: function() { return this.response === null ? [] : this.response.results.filter(result => result.partOfSpeech === "verb" } } });
- Произношение записывается в виде строки, прикрепленной к
pronunciation.all
. Мы добавляем/
символов в начало и конец в целях презентации. nouns
иverbs
получаются путем фильтрации массиваresults
внутриresponse
, чтобы соответственно включать только результаты, свойствоpartOfSpeech
которых является строкойnoun
илиverb
. Оба вернут массивы
Отображение всех этих данных в нашем шаблоне… наконец-то!
Чтобы отобразить эти вычисленные свойства в нашем шаблоне, нам сначала нужно просмотреть еще две директивы Vue: v-if
и v-for
.
v-if
оценит выражение. Если true, элемент DOM и все его дочерние элементы будут добавлены к компоненту. Если false, элемент DOM и все его дочерние элементы будут удалены из компонента.v-for
будет перебирать объект или массив, создавая элементы DOM для каждой пары или элемента "ключ-значение".
Начнем с самого родительского <dl>
. Он должен появляться только тогда, когда пользователь ввел слово, нажал кнопку, и API вернет ответ с данными определения. Другими словами, если пользователь не нашел слово или слово недействительно для API, мы не должны видеть <dl>
, отображаемого на странице или в DOM.
Таким образом, открывающий тег <dl>
теперь выглядит так:
<dl v-if="response">
Если response
равно null
, это выражение будет оцениваться как false
. Если response
является объектом JSON, он будет оцениваться как true
.
Затем два <dd>
, которые потенциально будут содержать список определений для слова, разделенных на nouns
и verbs
.
Согласно нашим свойствам computed
, оба значения будут массивами независимо от того, что такое response
. Единственная разница в том, пуст каждый или нет.
Следовательно, наши v-if
директивы могут проверять length
массива, чтобы определить, отображает ли <dd>
(длина ›0) или нет (длина == 0).
<dd v-if="nouns.length"> ... </dd> <dd v-if="verbs.length"> ... </dd>
В данном результате есть три элемента, которые могут содержать 0, 1 или более элементов:
- Количество определений.
- Количество примеров предложений.
- Количество синонимов.
Для каждого из этих потенциальных наборов элементов мы будем использовать директиву Vue v-for
, чтобы исключить элемент DOM для каждого элемента.
Вот синтаксис директивы Vue v-for
в контексте элемента списка:
<li v-for="item in collection">...</li>
Внутри <li>
мы можем использовать item
для ссылки на текущий элемент в итерации.
Вспоминая часть нашего шаблона из ранее, теперь мы можем добавить соответствующие директивы v-for
в разметку:
<dd v-if="nouns.length"> <p>noun</p> <ol> <li v-for="noun in nouns"> <p>{{noun.definition}}</p> <p v-for="sentence in noun.examples"> <q>{{noun.sentence}}</q> </p> <dl> <dt>synonyms</dt> <dd v-for="synonym in synonyms">{{synonym}}</dd> </dl> </li> </ol> </dd>
<li>
для каждого результата.<p>
и<q>
для каждого примера предложения.<dd>
для каждого синонима.
Протестируйте наше приложение и обновите его части, чтобы учесть данные, полученные от API для Any Word
Увы! Теперь у вас должны быть разметка, скрипт и ключи API, необходимые для тестирования вашего виджета.
HTML
<div id="app"> <h1>Dictionary</h1> <input type="text" v-model="word" /> <button @click="lookup">Look-up</button> <dl v-if="response"> <dt> <p class="syllables">{{syllables}}</p> <p class="pronunciation">{{pronunciation}}</p> </dt> <dd v-if="nouns.length"> <p class="part-of-speech">noun</p> <ol> <li class="definition-group" v-for="noun in nouns"> <p>{{noun.definition}}</p> <p v-for="example in noun.examples"> <q class="example">{{example}}</q> </p> <dl> <dt class="synonym-heading">synonyms</dt> <dd class="synonym" v-for="synonym in noun.synonyms"> {{synonym}} </dd> </dl> </li> </ol> </dd> <dd v-if="verbs.length"> <p class="part-of-speech">verb</p> <ol> <li class="definition-group" v-for="verb in verbs"> <p>{{verb.definition}}</p> <p v-for="example in verb.examples"> <q class="example">{{example}}</q> </p> <dl> <dt class="synonym-heading">synonyms</dt> <dd class="synonym" v-for="synonym in verb.synonyms"> {{synonym}} </dd> </dl> </li> </ol> </dd> </dl> </div>
JavaScript
var vm = new Vue({ el: "#app", data: { response: null, word: "program" }, computed: { syllables() { return this.response !== null ? this.response.syllables.list.join("•") : []; }, pronunciation() { return this.response !== null ? `/${this.response.pronunciation.all}/` : []; }, verbs() { return this.response !== null ? this.response.results.filter(result => result.partOfSpeech === "verb") : []; }, nouns() { return this.response !== null ? this.response.results.filter(result => result.partOfSpeech === "noun") : []; }, }, methods: { lookup() { fetch(`https://wordsapiv1.p.mashape.com/words/${this.word}`, { headers: { "X-Mashape-Key": "yourAPIKeyHere" } }) .then(response => response.json()) .then(body => this.response = body) }, } })
В нашем коде отсутствуют две функции, которые я оставлю вам в качестве домашнего задания.
- Если слово не имеет синонимов, наш виджет по-прежнему отображает слово
synonyms
, хотя его не должно быть. - В версии Google многие синонимы служат гиперссылками для поиска определения этого слова. Однако не все. Как мы можем добиться такого же эффекта и поведения?
А если вы настроены амбициозно, изучите компонентную архитектуру Vue и произведите рефакторинг этого однокомпонентного приложения в одно, состоящее из нескольких компонентов.
Наконец, давайте создадим наше приложение, используя SCSS, чтобы имитировать виджет Google.
Во фрагменте кода в конце этого раздела вы несколько раз увидите символ &
. Этот специальный символ позволяет автору эффективно вкладывать стили элементов, которые являются дочерними по отношению к другому, в блок объявления стиля этого элемента, при этом ссылаясь на этот родительский элемент в любом месте дочернего селектора.
Например:
.synonym-heading { font-style: italic; &::after { content: ":"; } }
в обычном CSS будет выглядеть так:
.synonym-heading { font-style: italic; } .synonym-heading::after { content: ":"; }
Таким образом, &
в этом контексте компилируется в .synonym-heading
при интерпретации этого блока селектора.
SCSS
#app { min-width: 50vw; max-width: 600px; margin: 2em auto; font-family: sans-serif; border: 1px solid lightgray; border-radius: 10px; padding: 1em; h1 { font-weight: normal; font-size: 2em; line-height: 1; margin-top: 1em; } input, button { font-size: 1.5em; color: gray; } .syllables { font-size: 2em; margin: 0; line-height: 1; } .pronunciation { color: gray; } .synonym-heading { font-style: italic; &::after { content: ":" } } .synonym { &:not(:last-child)::after { content: ", "; } } .synonym-heading, .synonym { color: gray; margin: 0; display: inline; } .part-of-speech { font-style: italic; } .definition-group { margin-bottom: 1em; } .definition-item { margin: 0; } .example { color: gray; } }
Вы достигли конца - либо с помощью твердости, либо путем прокрутки страницы.
В любом случае, спасибо за чтение. Я надеюсь, что вы последовали за мной, потому что это сделает вас гораздо более сильным разработчиком интерфейса.