so I'm trying to create a web app that when you insert a search it gets the data from the youtube API using JSON and renders a list with the videos matching your search. When it retrieves it's getting some Letter and numerical answer but not a list of the videos. Any help in the right direction will be appreciated. This is the HTML for it:
<div class="search-box-container">
<form action="#" class="js-search-form search-box">
<label for="query"></label>
<input type="text" class="js-query search-form" placeholder="Search me">
<button type="submit" class="button">Search</button>
</form>
</div>
<h2>Results</h2>
<div class="js-search-results">
</div>
And this is the JS/Jquery for it:
const YOUTUBE_SEARCH_URL =
'https://www.googleapis.com/youtube/v3/search';
`const key = 'key'//(hidden for privacy concerns);`
function getDataFromApi(searchTerm, callback) {
const query = {
part: 'snippet',
key: key,
q: `${searchTerm} in:name`,
}
$.getJSON(YOUTUBE_SEARCH_URL, query, callback);
}
function renderResult(result) {
return `
<div>
<h2>
<a class="js-result-name" href="http//www.youtube.com/watch?v=${
result.id.videoId}" target="_blank">${result.id.videoId}</a></h2>
</div>
`;
}
function displayYoutubeSearchData(data) {
console.log(data);
const results = data.items.map((item, index) => renderResult(item));
$('.js-search-results').html(results);
}
function watchSubmit() {
$('.js-search-form').submit(event => {
event.preventDefault();
const queryTarget = $(event.currentTarget).find('.js-query');
const query = queryTarget.val();
queryTarget.val("");
getDataFromApi(query,displayYoutubeSearchData );
});
}
$(watchSubmit);
This is the answer that gets rendered
You were almost there: it is just a typo.
Look at the href attribute inside the template literal returned by the renderResult() method.
href="http//www.youtube.com/watch?v=${result.id.videoId}"
Mind the wrongly formed scheme (http// vs https://).
A little bit of a context:
The YouTube API returns a collection of search results (i.e. an array of objects, data.items in your code) that match the query parameters specified in the API request.
Each item contains an id object with a videoId property. That is the "alphanumeric answer" you refer to in your question. After mapping data.items into an array of result HTML templates, you are reading that video id with ${result.id.videoId}. Then you concatenate the YouTube watch URL with the video id.
You should check the JSON structure of the search result in the YouTube Data API docs. Besides id.videoId, it contains more useful information. For example, you could prefer to show to the users the title of the video using ${result.snippet.title} instead of the alphanumeric videoId.
Related
I am new to Javascript and I've been learning how to import a country's attributes into an HTML element. Some of you might recognize this code, it's from a tutorial, which is now outdated. I've been searching around for an updated solution, but couldn't find any.
First I have the function to fetch the data:
const getCountryData = function (country) {
fetch(`https://restcountries.com/v3.1/name/${country}`)
.then(response => response.json())
.then(data => renderCountry(data[0]));
};
Then I call that function, supplying a country getCountryData('czechia') to infuse it into an element like this:
const renderCountry = function(data, className = '') {
const html = `
<article class="country ${className}">
<img class="country__img" src="${data.flags.svg}" />
<div class="country__data">
<h3 class="country__name">${data.name.common}</h3>
<h4 class="country__region">${data.region}</h4>
<p class="country__row">${(+data.population / 1000000).toFixed(1)} people</p>
<p class="country__row">${data.fifa}</p>
</div>
</article>
`
countriesContainer.insertAdjacentHTML
('beforeend', html);
countriesContainer.style.opacity = 1;
}
This works fine, but the issue is that at the end of the HTML, where I input {data.fifa} I want to have the name of the country's main currency instead. Unfortunately, the data is structured in a way, that in order to have the currency's name displayed, I first have to call it's short name, as shown below:
"currencies": {
"CZK": {
"name": "Czech koruna",
"symbol": "Kč"
}
},
If I call the {data.currencies} into the string, I'm just gonna get an empty object back. If I call it as {currencies.CZK.name}, it works, but the issue is that if I call Sweden, for example, it won't display anything, because then it'd need to be {currencies.SEK.name}. How do I get around this? How can I can call a currency's name without having to incorporate CZK, SEK, USD, EUR etc. into the variable?
Any help is appreciated.
You can transform that object into an array:
const currencyArray = Object.values(data.currencies)
console.log(currencyArray[0].name)
If the country has many currencies, just change the index from 0 to 1, 2, ...
I am new to working with APIs in javascript. I am looking to get input that a user puts into a box on a site (a city name) and fetch the API, to retrieve the temperature in that city. So far I have the following to fetch the API. But I am a bit lost on how to actually get that data and display it. How would I get the 'data'? I'm just not used to using APIs with Javascript and looking to learn more.
js file:
function hi() {
function temperature(input) {
const myKey = "Hidden_for_privacy";
const api = `https://api.openweathermap.org/data/2.5/weather?
q=${input}&lang=en&&appid=${myKey}&units=metric`;
fetch(api)
.then(function(response){
let data = response.json();
console.log(data);
return data;
})
Then I have this. searchUser is just representing the location the user types in:
const search = document.getElementById("searchUser");
const button = document.getElementById("submit");
button.addEventListener("click", () => {
const currentVal = search.value;
Relevant HTML:
<div class="container searchContainer">
<div class="search card card-body">
<h3>Get The Weather For a Location</h3>
<p class="lead">Enter Your Location</p>
<input type="text" id="searchUser" class="form-control"
placeholder="Location">
</div>
<br>
<div id="profile"></div>
</div>
<div class="container text-center mt-2">
<button class="btn btn-primary" id="submit">Submit</button>
</div>
<div id="content">
</div>
I think you have a few syntax errors going on before you can get into displaying data on the screen. I'd suggest concentrating on the JS implementation first to ensure you are successfully fetching data and loading it to the console. For instance, the closures in your JS might be causing problems. The hi function is creating a closure and then you are passing an argument of input into a function inside it but there is no local variables for it to grab.
Maybe try something like this to start and see what it logs:
function getTemperature() {
const myKey = "Hidden_for_privacy";
const api = `https://api.openweathermap.org/data/2.5/weather?
q=${input}&lang=en&&appid=${myKey}&units=metric`;
// .json returns a promise so you need to use .then to get the data from it synchronously
fetch(api)
.then((response) => response.json())
.then(data => console.log(data))
}
So what im trying to do is query a Minecraft server with javascript, and with the response i get back with the api, i want to grab the .playerlist and put the response in this url (https://cravatar.eu/avatar/ {name} /100.png) for each person connected
If someone knows a better way to achieve this, i would very much appreciate your input!
Im also pretty new to javascript, so not fully know what im doing :/
Heres the HTML that i have (i know it may be messy, its also not fully my code)
<div class="card"> <div class="icon"><img src="https://cdn.worldvectorlogo.com/logos/minecraft-1.svg"></div><div class="header">
<div class="image"> <img src="https://res.cloudinary.com/lmn/image/upload/e_sharpen:100/f_auto,fl_lossy,q_auto/v1/gameskinnyc/u/n/t/untitled-a5150.jpg" alt="" /> </div>
<h2>Server Status</h2>
</div>
<div id="rest">Loading...</div>
<img src="https://cravatar.eu/avatar/" $face "/>
</div>
And here is the javascript
//Query api at this address
var url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, function(r) {
//data is the JSON string
if(r.error){
$('#rest').html('Server Offline.');
return false;
}
var p1 = '';
if(r.Players > 0 ){ p1 = '<br>'+r.Playerlist; }
// Text to display below
$('#rest').html('Total Online: '+r.Players+p1);
// Trying to add playerlist to html url
$('#face').html+p1;
});
Since you've pasted jQuery code, I'll submit my answer in jQuery. However, I do recommend you learn primitive JavaScript and not focus your attention just on jQuery... it's become something of a meme on StackOverflow.
Starting off, you really should be wrapping your code in $(document).ready this'll only run the code when the page has loaded.
$(document).ready(() => {
// The document is ready, let's run some code!
});
Then add your AJAX request as normal inside this bracket.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
});
});
Okay, whilst writing this, I checked the URL provided by OP and saw that it was timing out so I've grabbed a sample response from the Minetools' documentation.
{
"MaxPlayers": 200,
"Motd": "A Minecraft Server",
"Playerlist": [
"Connor",
"Kamil",
"David"
],
"Players": 3,
"Plugins": [],
"Software": "CraftBukkit on Bukkit 1.8.8-R0.2-SNAPSHOT",
"Version": "1.8.8",
"status": "OK"
}
So in your JSON response, you can see that Playerlist is a array which can contain multiple things in one variable. You can also iterate through an array, which is what we'll be doing to build the image URLs.
We iterate through an array using forEach.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
response.Playerlist.forEach(playerName => {
console.log(playerName);
});
});
});
//Console:
//Connor
//Kamil
//David
Now that we're iterating through the player list we can start assembling the URLs for these images and adding them to your document's body.
I've cleaned up your HTML, take note of the new div#user-images I've added. This'll be the place where jQuery will add your images from the forEach loop.
<div class="card">
<div class="icon">
<img src="https://cdn.worldvectorlogo.com/logos/minecraft-1.svg">
</div>
<div class="header">
<div class="image">
<img src="https://res.cloudinary.com/lmn/image/upload/e_sharpen:100/f_auto,fl_lossy,q_auto/v1/gameskinnyc/u/n/t/untitled-a5150.jpg" alt="" />
</div>
<h2>Server Status</h2>
</div>
<!-- This div tag will need to hide when there is no error, or say when there is. -->
<div id="rest">Loading...</div>
<!-- The user images will be added inside this div. -->
<div id="user-images"></div>
</div>
Now we have our HTML ready we can start using the jQuery function appendTo to add elements into our div#user-images.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
response.Playerlist.forEach(playerName => {
$(`<img src="https://cravatar.eu/avatar/${playerName}" />`).appendTo("#user-images");
});
});
});
Your div#user-images should start filling up with the images of players from the Playerlist array.
I noticed you added a simple way of showing whether or not there's an error with the API. We can interact with div#rest to show/hide or change text depending on the success of the response.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
if(response.error){
$("#rest").html("The server is offline!");
}else{
//There is no error, hide the div#rest
$("#rest").hide();
response.Playerlist.forEach(playerName => {
$(`<img src="https://cravatar.eu/avatar/${playerName}" />`).appendTo("#user-images");
});
}
});
});
And that's it really. I hope this gives you some understanding of arrays, and iterating through them, as well as some DOM functions from jQuery.
I have a function which grabs comments from the server, I would like to display total number of comments available in a server.
Here is the function in .ts file:
this.activeRouter.params.subscribe(params => {
// tslint:disable-next-line:prefer-const
let id = params['id'];
this.userService.getComments(id)
.pipe(
map(data => data.sort((a, b) => new Date(b.localTime).getTime() - new Date(a.localTime).getTime()))
)
.subscribe(data => this.comments = data);
});
Here is the get function in service
getComments (id: number) {
return this.http.get<Comment[]>(this.commentsUrl);
}
Here is the html for displaying comments
<div class="comments-description" *ngFor="let comment of comments">
<span class="comments_count">(353)</span>
<div class="comments-photo">
<img src="https://randomuser.me/api/portraits/men/84.jpg" alt="">
</div>
<div class="comments_wrapper">
<div class="comments_details">
<h1>{{comment.author}}</h1>
<span class="days">1d</span>
</div>
<div class="comments_text">
<p>{{comment.description}} </p>
</div>
</div>
</div>
why not simply use the length
<span class="comments_count">{{comments.length}}</span>
If you get all the comments in the response then you can use comments.length
<span class="comments_count">{{comments.length}}</span>
But the best practice is to get it from the API side. Add one more field in your API response for comment count.
Solution:
While getting comments data from server , You have to return total number of comments along with the new data.
Once you call service you can add data in one array.
and set total count in one variable.
Note:
You have to read count from sever and return result in same API or service
Update your API and get comments count using API and show your comment count.
Such counter must be on the server side.
For example, post has 10000 comments.
1 request will not fetch them all, only a portion (page).
And it's not good to get them all only to find out a count.
So the response should contain a 'count' field.
I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?
I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.
please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">