Passing variable to Custom Svelte Web Component - javascript

I have created simple Custom Web Component using Svelte. It has been compiled and seems it should work well, but there is the difficulty. I'm trying to pass into prop some variable, but getting undefined all the time, but if I'm passing some string
Result.svelte component
<svelte:options tag="svelte-result" />
<script>
export let result = {metadata: {}, transfers: []};
export let string = 'no string';
</script>
<div class="result__wrapper">
{string}
<div class="result__metadata">
<div>{result.metadata.offset}</div>
<div>{result.metadata.limit}</div>
<div>{result.metadata.total}</div>
</div>
</div>
When it copiled I'm using it like
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Svelte test</title>
<script defer src="/svelte/wapi-client/svelte-component.js"></script>
</head>
<body>
<div id="test"></div>
</body>
<script>
const data = {
metadata: {
limit: 20,
offset: 0,
total: 311301
},
transfers: [
{
amount: "7.95",
identifier: "9cd9901f-44a5-4436-9aef-880354bbe2e4"
}
]
};
document.getElementById('test').innerHTML = `
<svelte-result string="works" result=${data}></svelte-result>`;
</script>
</html>
data variable not passed to component, but string passed and shown correctly... What Am I doing wrong? How can I pass data variable into component ?

You can't pass objects as attributes to custom elements. You need to stringify your object before passing it.
index.html
...
document.getElementById('test').innerHTML = `
<svelte-result string="works" result=${JSON.stringify(data)}></svelte-result>`;
...
Foo.svelte
<svelte:options tag="svelte-result" />
<script>
export let result = {metadata: {}, transfers: []};
export let string = 'no string';
$: _result = typeof result === 'string' ? JSON.parse(result) : result;
</script>
<div class="result__wrapper">
{string}
<div class="result__metadata">
<div>{_result.metadata.offset}</div>
<div>{_result.metadata.limit}</div>
<div>{_result.metadata.total}</div>
</div>
</div>

As an alternative to using JSON.stringify to pass the data to the component, you can pass it as a property rather than as an attribute — in other words instead of this...
document.getElementById('test').innerHTML = `
<svelte-result string="works" result=${data}></svelte-result>`;
...you do this:
document.getElementById('test').innerHTML = `
<svelte-result string="works"></svelte-result>`;
document.querySelector('svelte-result').result = data;
Not ideal, of course, since it means that you have to accommodate the initial undefined state and the post-initialisation state once result has been passed through, but web components are a bit awkward like that.

Related

Can't change image source of element ID when using variable name instead of string

I am writing a function to change the src of an image based on two strings passed to the function, a, which is the element ID, and b, which is the new source for the image.
In this instance, I have confirmed that both variables are passed to the function correctly.
console.log(a); is layerAddButton
console.log(b); is images/layerAdd-blue.svg
Here is my code :
function swapImg(id,url)
{
var a = id;
var b = url;
document.getElementById(a).src = b;
}
This does not change the url of the image as expected, but gives an error in chrome :
functions.js:3025 Uncaught TypeError: Cannot set properties of null
(setting 'src')
at swapImg (functions.js:3025:32)
at HTMLButtonElement.onmouseleave (app.htm:1132:274)
This works fine when I hard code like so :
document.getElementById('layerAddButton').src = 'images/layerAdd-blue.svg';
I tried it and it works. Make sure to write the Id correctly when passing it to the function. And make sure the script is in the HTML after the element and not in the head.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<img
src="https://www.testingtime.com/app/uploads/2017/07/Grundregeln_fuer_User_Testing-750x500.jpg"
alt=""
id="imgElement"
/>
<body>
<script>
function swapImg(id, url) {
var a = id;
var b = url;
console.log(document.getElementById(a));
document.getElementById(a).src = b;
}
swapImg("imgElement", "https://source.unsplash.com/random");
</script>
</body>
</html>
To fix this, you just needed to pass the variable to the getElementById() function and not just the variable name.
The correct code should be :
function swapImg(id,url)
{
var a = id;
var b = url;
document.getElementById(a).src = b;
}

Getting API Data to show on a webpage with Javascript

I'm trying to get data from https://wttr.in/?format=j1 to show on a webpage. I'm very new to Javascript so I hoped this would be easy but I'm struggling to get it to work, what am I doing wrong?.
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content=
"width=device-width, initial-scale=1.0" />
</head>
<body>
<p id="temp"></p>
</body>
<script>
const api_url = "https://wttr.in/?format=j1";
async function getWeather() {
const response = await fetch(api_url);
const data = await response.json();
const weather = data.results[0].current_condition[0];
let { temp } = weather.temp_C;
document.getElementById("temp").href = "temp:" + temp;
getWeather();
</script>
</html>
As per the comments from CBroe (thanks for the help) the main issues with my code were a few things.
No closing bracket
Not accessing the correct part of the JSON array
Not setting the element correctly
The working code looks like this:
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content=
"width=device-width, initial-scale=1.0" />
</head>
<body>
<p id="temp"></p>
</body>
<script>
const api_url = "https://wttr.in/?format=j1";
async function getWeather() {
const response = await fetch(api_url);
const data = await response.json();
const weather = data.current_condition[0];
let temp = weather.temp_C;
document.getElementById("temp").innerHTML = "temp:" + temp;
}
getWeather();
</script>
</html>
You are not parsing the json response correctly. The data does not have any results array, it is an object.
So to get current_condition you need to do something like this:
const currentCondition = data.current_condition[0];
Also, to get temp_C this is wrong:
let { temp } = weather.temp_C; //WRONG
// reason is that this statement means that `temp_C` is an object, and it contains `temp` property, which is not the case here. Because temp_C is a string.
So to get temp_C you need to simply do this:
let temp_C = currentCondition.temp_C;
Plus, worth mentioning here that if you are using the temp_C only for displaying purposes and not intending to reassign any value to it, then its better to use const instead of let
So it would become:
const temp_C = currentCondition.temp_C;
And if you want to use 'destructuring' you need to write it like this:
const { temp_C } = currentCondition;
Also your function is missing the closing paranthesis }.

How to create a form for comments with the ability of dynamically adding them to the list?

I need to create a form for comments with the ability to dynamically add them to the list. Each comment should have an assigned ID in consecutive order. The newest comment should be at the very bottom. Comments should be stored in the comments array. Each comment should have properties such as id (number) and text (string). Comments array must be empty when loaded initially. Each click on the "Add" button should create a new object inside the array and create element in the DOM tree.
let nextId = 1;
const comments = [];
const commentForm = document.querySelector('[data-id="comment-form"]');
const commentInput = commentForm.querySelector('[data-input="comment"]');
const button = commentForm.querySelector('[data-action="add"]');
const commentList = commentForm.querySelector('[data-id="comment-list"]');
button.addEventListener('click', () => {
const object = {};
if (commentInput.value != '') {
comments.map(() => ({ id: 'nextId++', text: commentInput.value }));
}
createElement();
});
function createElement() {
const newComment = document.createElement('li');
newComment.setAttribute('data-comment-id', comments.id);
newComment.textContent = comments.text;
commentList.appendChild(newComment);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="./css/styles.css" />
</head>
<body>
<div id="root">
<form data-id="comment-form">
<textarea data-input="comment"></textarea>
<button data-action="add">Add</button>
</form>
<ul data-id="comment-list"></ul>
</div>
<script src="./js/app.js"></script>
</body>
</html>
There are some issues in your code:
You are trying to access commentList from commentForm, but that element is outside of the commentForm. Use document object to access the element.
comments is an array from which you are trying to access text property, there is text property on comments.
You should pass the current input value to the function so that you can set the newly created LI's text with the value.
You should use push() instead of map() to push an item into the array. nextId is a variable but you are using that as if it is a string, you should remove the quotes around it.
For the better user experience, I will suggest you to clear the value of the input after creating the item.
Demo:
let nextId = 1;
const comments = [];
const commentForm = document.querySelector('[data-id="comment-form"]');
const commentInput = commentForm.querySelector('[data-input="comment"]');
const button = commentForm.querySelector('[data-action="add"]');
const commentList = document.querySelector('[data-id="comment-list"]');
button.addEventListener('click', () => {
const object = {};
if (commentInput.value != '') {
comments.push({ id: nextId++, text: commentInput.value });
}
createElement(commentInput.value);
commentInput.value = '';
});
function createElement(ci) {
const newComment = document.createElement('li');
newComment.setAttribute('data-comment-id', comments.id);
newComment.textContent = ci;
commentList.appendChild(newComment);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="./css/styles.css" />
</head>
<body>
<div id="root">
<form data-id="comment-form">
<textarea data-input="comment"></textarea>
<button type="button" data-action="add">Add</button>
</form>
<ul data-id="comment-list"></ul>
</div>
<script src="./js/app.js"></script>
</body>
</html>

Routing(?) in Vanilla JS

I need my webite to display info in a certain language, based on a query in my webite's URL (e.g. www.website.com/index.php?country=FR). How can I do that with vanilla JS and not React/Angular?
My approach:
1) JS recognizes a query in the URL (in this case- 'country=FR') and then appends a js file, which has neccessary french words in it defined by variables.
2) JS in my script tag that's in the HTML file, appends the main page markup text with template literals in it.
3)
I don't know, whether the browser fails to either fetch the language file itself or its variables. At the moment it does not render anything.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./js/main.js"></script>
</head>
<body>
<script>
const template= `
<h1>Good Morning: ${goodmorning} </h1>
<h2>Good Evening: ${goodevening} </h2>
<h3>My name is: ${mynameis}</h3>`
function markupAppend() {
$('body').html(template);
console.log('Markup loaded')
}
markupAppend()
</script>
</body>
</html>
=========================
Main.js
var domain = window.location.href;
var FRString = domain.includes("country=FR");
var ESString = domain.includes("country=ES");
if (FRString) {
$('head').append(`<script src="./Language_files/FRENCHwords.js" />`)
}
if (ESString) {
$('head').append(`<script src="./Language_files/SPANISHwords.js" />`)
}
=========================
FRENCHwords.js
const goodmorning = 'Bonjour';
const goodevening = 'Bonsoir';
const mynameis = 'Mon nom est';
=========================
SPANISHwords.js
const goodmorning = 'Buenos dias';
const goodevening = 'Buenas tardes';
const mynameis = 'Mi nombre es';
No errors displayed, the page is just not rendering...
In Your main.js file, you are using domain.includes, it only returns the domain name but not the entire URL. You can use window.location.href.includes for this.
Instead of: domain.includes("country=FR");
Try: window.location.href.includes("country=FR");

How to read object properly in JavaScript

I am new to programming and recently began learning Javascript, I have a problem that appeared in few exercises that I made. I searched the site for more information but have not found a solution for my problem. I apologize in advance for my bad english and if this is not the right place or the right way to ask this question because this is my first post in Stackoverflow.
Currently practicing HTML templates. Assuming that the code is correct, I'm not sure where I'm wrong. Loading code into the browser and Handlebars gives me an error: "Error: You must pass a string or Handlebars AST to Handlebars.compile. You passed undefined". I tried to debug and saw that when I tried to take a value from date object it gives back undefined. In previous exercise had a similar problem in which I tried to read JSON object and did not manage to parse it and returned again undefined. Can you help me, I am stuck on this problem for some time.
var data = {
animals: [{
name: 'Lion',
url: 'https://susanmcmovies.files.wordpress.com/2014/12/the-lion-king-wallpaper-the-lion-king-2-simbas-pride-4685023-1024-768.jpg'
}, {
name: 'Turtle',
url: 'http://www.enkivillage.com/s/upload/images/a231e4349b9e3f28c740d802d4565eaf.jpg'
}, {
name: 'Dog'
}, {
name: 'Cat',
url: 'http://i.imgur.com/Ruuef.jpg'
}, {
name: 'Dog Again'
}]
}
window.onload = function() {
var htmlTemplate = document.getElementsByClassName('container-template').innerHTML;
var template = Handlebars.compile(htmlTemplate);
for (let x of data.animals) {
if (x.hasOwnProperty('url')) { //x.url
x.hasUrl = true;
} else {
x.hasUrl = false;
}
}
document.getElementsByClassName('container').innerHTML = template(data);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Animals & Batman</title>
</head>
<body>
<div class="container">
</div>
<script class="container-template" type="text/x-handlebars-template">
<h1>Animals</h1>
{{#each animals}}
{{#if hasUrl}}
<li>
See a {{name}}
</li>
{{else}}
<li>
No link for {{name}}, here is Batman!
</li>
{{/if}}
{{/each}}
</script>
<script src="../handlebars-v4.0.5.js" charset="utf-8"></script>
<script src="../jquery-3.1.0.js" charset="utf-8"></script>
<script src="./main.js" charset="utf-8"></script>
</body>
</html>
document.getElementsByClassName returns an array of elements, not a single one - since multiple elements on a page can have the same class.
What you probably want is to use the id instead of class:
<script id="container-template" type="text/x-handlebars-template">
var htmlTemplate = document.getElementById('container-template').innerHTML;

Categories