My range input is not following the currentTime of the audio - javascript

My range input is not following the currentTime of the audio, using v-model and template ref from nuxt3.
If i put timeline ref outside of onMounted, it gives "Cannot read properties of null (reading 'currentTime')" console error. If i put it in onMounted, it does nothing because template can't reach it in onMounted hook probably.
<script setup>
const player = ref(null);
onMounted(() => {
const timeline = ref(player.value.currentTime);
});
</script>
<template>
<audio
ref="player"
src="example.mp3"
type="audio/mpeg"
/>
<input v-model="timeline" type="range" min="0" />
</template>
Probably an easy one but i couldn't see the solution today.

Related

How can you select code nodes that were added through templates?

So I am using the template method to dynamically add HTML content in a page, and I want to change the value of an input through an event listener.
Here's a completely random snippet of code as an example (it's nonsensical on purpose):
favoriteElement += `<div class="favorite__page JS-favoritePage">
<p id="JS-amountOfFavorites">Quantity of saved pages : ${amount}</p>
<input type="number" class="favoritesQuantity" name="amountOfFavorites" min="1" max="100" value="${value}">
</div>`
So let's say that I want to have access to the value of the input, I'll declare a variable and get it through their query selector :
let inputFavoritesQuantity = document.querySelector('input [class="favoritesQuantity"]');
Now I'll add an event listener:
inputFavoritesQuantity.addEventListener("input", function(e){
let valueOfInput = e.target.value;
//Other code
}
Though the problem is that I do not have access to the input because it's added with a template, so it gives an error Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
I could add everything by hand using the properties createElement,setAttribute,appendChild...
But it would make the code VERY long and difficult to maintain! (without even considering the fact on my code project I'd have to add 5 nested elements which have 5 attributes each!)
Is there another efficient method to have access to an element with templates?
The DOMParser compiles strings into a document. You need to access the
documentElement in order to add to the existing dom. Here's an example of use
let amount = 100
let value = 50
favoriteElement = `<div class="favorite__page JS-favoritePage">
<p id="JS-amountOfFavorites">Quantity of saved pages : ${amount}</p>
<input type="number" name="amountOfFavorites" min="1" max="100" value="${value}" />
</div>`
// This converts the string and gets the documentElement.
var node = new DOMParser().parseFromString(favoriteElement, "text/html").documentElement
//Now we are working with an actual element and not a string of text.
let inputFavoritesQuantity = node.querySelector('input [class="favoritesQuantity"]');
node.addEventListener("input", function(e){
let valueOfInput = e.target.value;
console.log('value changed', valueOfInput);
})
var outputDiv = document.getElementById('content')
outputDiv.appendChild(node);
<div id="content">
</div>

svelte prop value not working with if statement

this is my svelte component code
<script>
export let canCascade = true;
let show = true;
function cascade() {
if (canCascade) {
show = !show;
}
}
</script>
{#if show}
<div class="shade" on:click|self={cascade}>
shade
</div>
{/if}
When I use the component as <Component canCascade=false /> the 'if block' doesn't work.
But hard-coding the value inside just works fine.
Am I missing something here - some conceptual error?
Like #Corrl pointed out in the comments, you need to use {brackets}. If you don't, the variable will follow the rules of an html attribute.
Working repl https://svelte.dev/repl/d13df678eab243e9a13fb705da197219?version=3
In Svelte when we want to pass JavaScript value / expression to an attribute of a component we need to wrap the value / expression with curly brackets {}.
Otherwise, it will be used as a string.
As an example, take a look at the following code:
Component.svelte:
<script>
export let test = true;
$test: console.log(`typeof test = ${typeof test}`);
</script>
App.svelte:
<script>
import Component from "./Component.svelte";
</script>
<Component test=true />
<div />
When you will open the console inside the browser developer tools,
the output will be:
typeof test = string

Template Literals - Changing the Inner HTML of an element (onInput event)

So I´ve never used Template Literals before, but I need to work with a template now that seemingly includes a form with Template Literals
This is one of my inputs:
<input
type="number"
className="mf-input "
min="1900"
max="2099"
step="1"
id="curyear"
name="currentyear"
placeholder="${ parent.decodeEntities(`Current Year`) } "
onInput=${parent.handleChange}
aria-invalid=${validation.errors['currentyear'] ? 'true' : 'false'}
ref=${el => parent.activateValidation({"message":"This field is required.","minLength":1900,"maxLength":2099,"type":"by_character_length","required":false,"expression":"null"}, el)}
/>
<${validation.ErrorMessage}
errors=${validation.errors}
name="currentyear"
as=${html`<span className="mf-error-message"></span>`}
/>
What I am trying to do is, in the onInput method, instead of handling the validation, I also want to change the innerHTML of an element:
<h2 class="elementor-heading-title elementor-size-default" id="curyeartext">Current Year</h2>
I´ve been trying to do it for hours, but I can't get it to work.
EDIT: turns out, they are template literals and not reactJS
Avoid setting innerHTML inside React, use state instead. This is because React will overwrite your modified DOM when it re-renders, if the html is in a node that React is controlling.
export default function MyReactComponent() {
var [ currentInput, setCurrentInput ] = React.useState();
return <>
<input
type="number"
className="mf-input"
min="1900"
max="2099"
step="1"
name="currentyear"
onInput={(e) => setCurrentInput(e.target.value)}
value={currentInput}
/>
<h2 class="elementor-heading-title elementor-size-default" id="curyeartext">
{currentInput}
</h2>
</>;
}
However, if you have a situation where it is unavoidable, you can tell React to ignore a node by only specifying a ref on it - i.e. no other props or child JSX:
export default function MyReactComponent() {
return <div ref={divRef => {
divRef.innerHTML = "Hello <b>world!</b>";
}} />
}
This technique is typically used when integrating non-React specific JS libraries into React, as you can do whatever you want with the contents of that DOM node.
I managed to do this by adding a function above the return:
function myfunction() {
document.querySelector('#curyeartext').innerHTML = document.querySelector('#curyear').value;
document.querySelector('#lastyear').innerHTML = document.querySelector('#curyear').value-1;
document.querySelector('#yearbefore').innerHTML = document.querySelector('#curyear').value-2;
}
And call it in the onInput event handler
onInput=${myfunction}

Is there a way to change the url of audioplayer once the state is changed on the component it exists in?

I'm working on an application that uses text to speech API services. On the front end, I have a textarea element that takes in the input value and a button that consequently makes a post call so that I get back the speech of the script I have written in form of an URL.
These are the state variables of the component
state = {
textVal: "",
audioUrl: "",
}
This is the handleChange method, textarea, and the button HTML.
handleChange() {
this.setState({ textVal: event.target.value });
}
<textarea
placeholder="Insert script here"
rows="4"
cols="50"
value={this.state.textVal}
onChange={this.handleChange}
/>
<button onClick={this.audioRequest}>
Listen
</button>
This is how I make the post call, although I have removed unnecessary stuff
audioRequest() {
const data = {
textScript: this.state.textVal,
};
axios.post(
"...", data)
.then(({ data }) => {
this.setState({
audioUrl: data.audioUrl,
});
});
}
After this, I pass on the URL to the audio player element as shown with just a conditional to check if I URL isn't empty
<audio controls>
{(this.state.audioUrl === "") ?
"Nothing to play yet" :
<source src={this.state.audioUrl} type="audio/wav" />}
</audio>
The problem starts when I type in a new script and hit the listen button, I do get the new URL back in the console but the audio player does not play new the script, instead plays to the first one.
I would really appreciate some help here.
You have to make the audio replay as well, like this:
onClick={() => {
setSource("/file_example_OOG_1MG.ogg");
ref.current.pause();
ref.current.load();
ref.current.play();
}}
Working sample: https://codesandbox.io/s/react-audio-player-forked-wx225?file=/src/Player/Player.js

Vuejs: watching state variable from component

I have a global variable that is populated with an API call when a component is mounted.
I also have a chart component that I would like to show if that variable is not null (i.e. has the request has finished and it has been populated).
At the moment to render the chart I am using this:
<template>
<div class="container">
<b-chart
v-if="$store.state.lists[api_key] != null"
:chartdata="$store.state.lists[api_key]"
:options="options"
/>
</div>
</template>
I have tried moving this check $store.state.lists[api_key] != null to computed or watch, to minimise the inline scripting, but I can't seem to get it to work. Would someone please show me how.
Try this:
computed: {
canShowChart() {
return this.$store.state.lists[this.api_key] != null;
}
}
<b-chart
v-if="canShowChart"
:chartdata="$store.state.lists[api_key]"
:options="options"
/>
Since null values are interpreted as "falsy", and assuming you have an "api_key" data variable, you can use it this way:
computed: {
chartData() {
return this.$store.state.lists[this.api_key]
}
}
<template>
<div class="container">
<b-chart
v-if="chartData"
:chartdata="chartData"
:options="options"
/>
</div>
</template>

Categories