Livewire property is null when value updated through php - javascript

I have a very simple livewire component
class Test extends Component
{
public $test = "test";
public function submit()
{
dd($this->test);
}
public function render()
{
return view('livewire.test');
}
}
and view
<div>
<form wire:submit.prevent="submit" method="post">
<input type="text" wire:model="test" id="test">
<button type="button" id="ok">ok</button>
<button type="submit">submit</button>
</form>
<script>
const button = document.getElementById('ok');
button.addEventListener('click', function() {
const input = document.getElementById('test');
input.value = "Test";
});
</script>
</div>
I simplified the code for illustrative purposes to show that JavaScript changes a value.
When I click ok what changes the value from test to Test and then submit, I get test shown instead of Test.
I except to see Test. What am I doing wrong?
It seems that the Livewire Component is not recognising the change from the Javascript code, as the following function doesn't fire either:
public function updatedTest()
{
dd("Fired");
}

What #Peppermingtology said isn't entirely true. He is right that it is not the preferred way of handling Livewire, but sometimes you can't escape it.
You can indeed use AlpineJS and that will solve your issue, but you can also cast a normal change event. Your Livewire variable isn't updating because Livewire has not detected any change. That's because programattically updated inputs don't trigger any event by default to prevent infinite event loops.
If you simply add an event, it should also work:
const input = document.getElementById('test');
input.value = "Test";
let event = new Event('change', {bubbles: true});
input.dispatchEvent(event);

You can't manipulate properties in that manner with Livewire. The preferred way is to use the AlpineJS entangle method which specifically caters for sharing state between Livewire and your client.
That being said, you can achieve what you're after without using AlpineJS. Replacing your existing JavaScript with the below should get you the result you're after.
<script>
document.addEventListener('livewire:load', (evt) => {
const button = document.getElementById('ok');
button.addEventListener('click', function() {
document.getElementById('test').value = #this.test = 'Test';
});
})
</script>

Related

Is it possible to have acces to innerHTML 'id' from otger part of the code?

I have following code, where, based on event, I add some html code. I would like to refer to 'id' from this dynamically injected html in other event (or just from other part of the code):
<div id="choice"></div>
var decisionList = document.getElementById("decisionList");
decisionList.addEventListener("change", function () {
var finalChoice = document.getElementById("choice");
finalChoice.innerHTML='<input id="finalDate" type="date">'
}
and other event referring to 'id' from innerHTML:
var payment = document.getElementById("finalDate");
payment.addEventListener("change", function () {
var textDate = payment.textContent;
alert(textDate);
})
The above is not working. Is it possible or not?
It is possible, but make that payment getter lazy. What that means is, instead of setting up that second change listener right away (in your other code), make that other code a function. Then in your first trigger, where you created the extra div or input or something, call that setup function.
decisionList.addEventListener("change", function () {
const finalChoice = document.getElementById("choice");
finalChoice.innerHTML='<input id="finalDate" type="date">'
createFinalDateListener();
}
function createFinalDateListener() {
const payment = document.getElementById("finalDate");
payment.addEventListener("change", function () {
const textDate = payment.textContent;
alert(textDate);
});
}
Here's a similar example. I do not have the input immediately. Or listener. And I only create a listener after I create the input.
// Here's the main trigger
function addExtraElements() {
// let's create a datepicker dynamically.
document.querySelector('#placeholder').innerHTML = '<input type="date" placeholder="pick date">';
listenDateChanges();
// TODO: don't forget to add cleanup code! Each time you fill that innerHTML, the old listener will remain
}
// Here's your datepicker listener
function listenDateChanges() {
const datePickerEl = document.querySelector('input[type="date"]');
if (!datePickerEl) {
console.log('no picker');
return;
}
datePickerEl.addEventListener('change', () => alert(datePickerEl.value));
}
<div id="placeholder">
Placeholder
</div>
<button onclick="addExtraElements()">Add extra elements</button>

basic javascript question about disabling buttons

I have a simple piece of javascript embedded into my html form, not a separate file, that is supposed to disable the submit form button until a certain checkbox has been checked but it doesn't seem to be working.
<script>
var disclaimer = document.getElementById("disclaimer");
var submitButton = document.getElementById("submit");
submitButton.disabled = true;
if (disclaimer.checked) {
submitButton.disabled = false;
}
</script>
which I wrote and seems simple and effective but I'm not getting the results I'm looking for. After researching I see results such as
$('#check').click(function(){
if($(this).attr('checked') == false){
$('#btncheck').attr("disabled","disabled");
}
else
$('#btncheck').removeAttr('disabled');
});
Now obviously the variable names and such are named differently but this doesn't even look remotely similar to the javascript code I've provided above and I'm having a hard time getting useful tips from the apparently working code below that does the same thing. Could someone break down the code segment below such that I might be able to fix my code above?
This is the snippet of code with the two HTML id's in question,
<label style='font-size: smaller;'>
<input type='checkbox' name='disclaimer' id='disclaimer' required='required' />
I understand that by submitting this form,
I am transferring any copyright and intellectual property rights to the form's owner,
that I have the right to do so,
and that my submission is not infringing on other people's rights.
</label><br/>
<script>
var disclaimer = document.getElementById("disclaimer");
var submitButton = document.getElementById("submit");
submitButton.disabled = true;
if (disclaimer.checked) {
submitButton.disabled = false;
}
</script>
<div class='vspace'/>
<input type='submit' id='submit' name='came-from-form'/>
Edit: Tons of great answers below that were very informative for letting me know what I'm working with. The issue I'm now facing is implementing these things. In the snippets below this seems very easy to implement but as I try to implement each answer below I'm not seeing any results which clearly means I'm doing something wrong somewhere else in my form. I've attached a larger snippet of the code in question if it helps. Otherwise it might be best to ask a new question.
I believe you trying to find the solution in vanilla JavaScript.
You have to attach the event to the check element like the following way:
var disclaimer = document.getElementById("disclaimer");
document.getElementById("submit").disabled = true;
disclaimer.addEventListener('click', function(){
var submitButton = document.getElementById("submit");
submitButton.disabled = true;
if (this.checked) {
submitButton.disabled = false;
}
});
<form>
<input type="checkbox" id="disclaimer"/>
<button id="submit">Submit</button>
</form>
Update:
In your code, the script is executing before the DOM is fully loaded. Hence you get a error:
Uncaught TypeError: Cannot set property 'disabled' of null
You can either place the script at the end or wrap your code with
DOMContentLoaded
The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading. A very different event load should be used only to detect a fully-loaded page. It is an incredibly common mistake to use load where DOMContentLoaded would be much more appropriate, so be cautious.
<label style='font-size: smaller;'>
<input type='checkbox' name='disclaimer' id='disclaimer' required='required' />
I understand that by submitting this form,
I am transferring any copyright and intellectual property rights to the form's owner,
that I have the right to do so,
and that my submission is not infringing on other people's rights.
</label><br/>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var disclaimer = document.getElementById("disclaimer");
document.getElementById("submit").disabled = true;
disclaimer.addEventListener('click', function(){
var submitButton = document.getElementById("submit");
submitButton.disabled = true;
if (this.checked) {
submitButton.disabled = false;
}
});
});
</script>
<div class='vspace'/>
<input type='submit' id='submit' name='came-from-form'/>
Quick Explantion
Here's a quick explanation of the code, which is heavily reliant on the JavaScript library, jQuery:
// click() is called every time the element `id="check"` is clicked
$('#check').click(function(){
// if element with `id="check"` has an attribute called *checked* set to false or it is null, then perform the if-block, otherwise perform the else-block
if($(this).attr('checked') == false){
// set disabled attribute of element with `id="btncheck"` to value of `disabled`
$('#btncheck').attr("disabled","disabled");
}
else
// remove disabled attribute of element with `id="btncheck"`
$('#btncheck').removeAttr('disabled');
});
anything in $() is selecting the element in the DOM, primarily using CSS-like selectors
.attr() is a method that gets/sets the element HTML attribute
.removeAttr() is a method that removes the HTML attribute
Vanilla JS
What you want to accomplish can be done with vanilla JS.
const disclaimer = document.querySelector("#disclaimer");
const submit = document.querySelector("#submit");
submit.disabled = true; // default setting
const clickHandler = (event) => submit.disabled = !event.target.checked;
disclaimer.addEventListener('click', clickHandler ); // attach event
<form>
<input type="checkbox" id="disclaimer"/>
<button id="submit">Submit</button>
</form>
Hope this will work for you
$('#disclaimer').click(function(){
if($(this).attr('checked') == false){
$('#submit').attr("disabled","disabled");
}
else
$('#submit').removeAttr('disabled');
});
Here if condition indicates the action which should be done if the disclaimer is not chcked. Button will be enable if the disclaimer is checked.
If you want jQuery, use prop (is not wrong to use attr too but I prefer prop instead).
For checkbox, use change event instead of click. I'd do like:
$('#check').on("change", function(){
var isChecked = $(this).is(':checked');
$('#btncheck').prop("disabled", !isChecked);
});
The only significant difference between the 2 code samples you posted is that the second one wraps the button disabling in the click event.
First one says : Straight when page is loaded, if the checkbox is checked, enable the button. (hint: happens only once)
Second one says : For each click on the checkbox, if the checkbox is checked, enable the button.
Something like this should work (haven't tested) :
<script>
var disclaimer = document.getElementById("disclaimer");
var submitButton = document.getElementById("submit");
function disableSubmitIfDisclaimerNotAccepted(){
submitButton.disabled = true;
if (disclaimer.checked) {
submitButton.disabled = false;
}
}
disclaimer.onclick = disableSubmitIfDisclaimerNotAccepted; // everytime the checkbox is clicked
disableSubmitIfDisclaimerNotAccepted(); // on page load
</script>
You must listen to input events in order to make changes when something change, like checking an checkbox.
var disclaimer = document.getElementById("disclaimer");
var submitButton = document.getElementById("submit");
submitButton.disabled = true;
disclaimer.addEventListener('change', function() {
if (this.checked) {
submitButton.disabled = false;
} else {
submitButton.disabled = true;
}
})
More about events: https://developer.mozilla.org/en-US/docs/Web/Events
Submit button disabled by default in HTML :
<input type="checkbox" id="disclaimer">
<label for="disclaimer">Disclaimer</label>
<input type="submit" id="submit" disabled>
Simplest solution using ES6 syntax without JQuery :
let disclaimerCheckbox = document.getElementById('disclaimer'),
submitButton = document.getElementById('submit');
disclaimerCheckbox.onchange = () => submitButton.disabled = !disclaimerCheckbox.checked;
JSFiddle
NOTE : no need to use the DOMContentLoaded event if the script has the defer attribute.

Input addEventListener on change not firing

I have a single input to upload a image.
html
<main>
<input id="hs-p" type="file" accept="image/*" capture="camera">
</main>
<script type="text/javascript">
let hsp = new HPS();
</script>
I want to listen for when this input changes ( when someones adds an image to it).
js
let imageInput = null;
class HSP {
constructor() {
this.imageInput = document.getElementById('hs-p');
if (this.imageInput) {
this.imageInput.addEventListener("change", this.uploadImage());
}
}
uploadImage() {
console.log("upload image", this.imageInput);
}
}
module.exports = HSP;
When someone adds an image it should call the uploadImage callaback. However this function is only firing once when the page loads and never fires when i add a image to the input or change the input image.
I am using node & webpack to output the above custom library/sdk which i then import into my html.
Change your event to this:
this.imageInput.addEventListener("change", () => this.uploadImage());
Or to this:
this.imageInput.addEventListener("change", this.uploadImage.bind(this));
What you are doing is calling uploadImage and passing the result of that to the listener. You want to pass the reference to the listener.
change this.imageInput.addEventListener("change", this.uploadImage()); to this.imageInput.addEventListener("change", this.uploadImage); ( removed the () after this.uploadImage ).

Simulate click event on react element

The bounty expires in 7 days. Answers to this question are eligible for a +50 reputation bounty.
ajaykools wants to reward an existing answer:
Worth bounty, only way simulate clicks on dynamic elements like svg, g, circle, etc which are generated on page load.
I'm trying to simulate a .click() event on a React element but I can't figure out why it is not working (It's not reacting when I'm firing the event).
I would like to post a Facebook comment using only JavaScript but I'm stuck at the first step (do a .click() on div[class="UFIInputContainer"] element).
My code is:
document.querySelector('div[class="UFIInputContainer"]').click();
And here's the URL where I'm trying to do it: https://www.facebook.com/plugins/feedback.php...
P.S. I'm not experienced with React and I don't know really if this is technically possible. It's possible?
EDIT: I'm trying to do this from Chrome DevTools Console.
React tracks the mousedown and mouseup events for detecting mouse clicks, instead of the click event like most everything else. So instead of calling the click method directly or dispatching the click event, you have to dispatch the down and up events. For good measure I'm also sending the click event but I think that's unnecessary for React:
const mouseClickEvents = ['mousedown', 'click', 'mouseup'];
function simulateMouseClick(element){
mouseClickEvents.forEach(mouseEventType =>
element.dispatchEvent(
new MouseEvent(mouseEventType, {
view: window,
bubbles: true,
cancelable: true,
buttons: 1
})
)
);
}
var element = document.querySelector('div[class="UFIInputContainer"]');
simulateMouseClick(element);
This answer was inspired by Selenium Webdriver code.
With react 16.8 I would do it like this :
const Example = () => {
const inputRef = React.useRef(null)
return (
<div ref={inputRef} onClick={()=> console.log('clicked')}>
hello
</div>
)
}
And simply call
inputRef.current.click()
Use refs to get the element in the callback function and trigger a click using click() function.
class Example extends React.Component{
simulateClick(e) {
e.click()
}
render(){
return <div className="UFIInputContainer"
ref={this.simulateClick} onClick={()=> console.log('clicked')}>
hello
</div>
}
}
ReactDOM.render(<Example/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
If you don't define a class in your component, and instead you only declare:
function App() { ... }
In this case you only need to set up the useRef hook and use it to point/refer to any html element and then use the reference to trigger regular dom-events.
import React, { useRef } from 'react';
function App() {
const inputNameRef = useRef()
const buttonNameRef = useRef()
function handleKeyDown(event) {
// This function runs when typing within the input text,
// but will advance as desired only when Enter is pressed
if (event.key === 'Enter') {
// Here's exactly how you reference the button and trigger click() event,
// using ref "buttonNameRef", even manipulate innerHTML attribute
// (see the use of "current" property)
buttonNameRef.current.click()
buttonNameRef.current.innerHTML = ">>> I was forced to click!!"
}
}
function handleButtonClick() {
console.log('button click event triggered')
}
return (
<div>
<input ref={inputNameRef} type="text" onKeyDown={handleKeyDown} autoFocus />
<button ref={buttonNameRef} onClick={handleButtonClick}>
Click me</button>
</div>
)
}
export default App;
A slight adjustment to #carlin.scott's great answer which simulates a mousedown, mouseup and click, just as happens during a real mouse click (otherwise React doesn't detect it).
This answer adds a slight pause between the mousedown and mouseup events for extra realism, and puts the events in the correct order (click fires last). The pause makes it asynchronous, which may be undesirable (hence why I didn't just suggest an edit to #carlin.scott's answer).
async function simulateMouseClick(el) {
let opts = {view: window, bubbles: true, cancelable: true, buttons: 1};
el.dispatchEvent(new MouseEvent("mousedown", opts));
await new Promise(r => setTimeout(r, 50));
el.dispatchEvent(new MouseEvent("mouseup", opts));
el.dispatchEvent(new MouseEvent("click", opts));
}
Usage example:
let btn = document.querySelector("div[aria-label=start]");
await simulateMouseClick(btn);
console.log("The button has been clicked.");
Note that it may require page focus to work, so executing in console might not work unless you open the Rendering tab of Chrome DevTools and check the box to "emulate page focus while DevTools is open".
Inspired from previous solution and using some javascript code injection it is also possibile to first inject React into the page, and then to fire a click event on that page elements.
let injc=(src,cbk) => { let script = document.createElement('script');script.src = src;document.getElementsByTagName('head')[0].appendChild(script);script.onload=()=>cbk() }
injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js",() => injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js",() => {
class ReactInjected extends React.Component{
simulateClick(e) {
e.click()
}
render(){
return <div className="UFIInputContainer"
ref={this.simulateClick} onClick={()=> console.log('click injection')}>
hello
</div>
}
}
ReactDOM.render(<ReactInjected/>, document.getElementById('app'))
} ))
<div id="app"></div>
Kind of a dirty hack, but this one works well for me whereas previous suggestions from this post have failed. You'd have to find the element that has the onClick defined on it in the source code (I had to run the website on mobile mode for that). That element would have a __reactEventHandlerXXXXXXX prop allowing you to access the react events.
let elem = document.querySelector('YOUR SELECTOR');
//Grab mouseEvent by firing "click" which wouldn't work, but will give the event
let event;
likeBtn.onclick = e => {
event = Object.assign({}, e);
event.isTrusted = true; //This is key - React will terminate the event if !isTrusted
};
elem.click();
setTimeout(() => {
for (key in elem) {
if (key.startsWith("__reactEventHandlers")) {
elem[key].onClick(event);
}
}
}, 1000);
Using React useRef Hooks you can trigger a click event on any button like this:
export default const () => {
// Defining the ref constant variable
const inputRef = React.useRef(null);
// example use
const keyboardEvent = () => {
inputRef.current.handleClick(); //Trigger click
}
// registering the ref
return (
<div ref={inputRef} onClick={()=> console.log('clicked')}>
hello
</div>
)
}
This answer was inspired by carlin.scott code.
However, it works only with focusin event in my case.
const element = document.querySelector('element')
const events = ['mousedown', 'focusin']
events.forEach(eventType =>
element.dispatchEvent(
new MouseEvent(eventType, { bubbles: true })
)
)

Change onclick action with a Javascript function

I have a button:
<button id="a" onclick="Foo()">Button A</button>
When I click this button the first time, I want it to execute Foo (which it does correctly):
function Foo() {
document.getElementById("a").onclick = Bar();
}
What I want to happen when I click the button the first time is to change the onclick function from Foo() to Bar(). Thus far, I've only been able to achieve an infinite loop or no change at all. Bar() would look something like this:
function Bar() {
document.getElementById("a").onclick = Foo();
}
Thus, clicking this button is just alternating which function gets called. How can I get this to work? Alternatively, what's a better way to show/hide the full text of a post? It originally starts shorted, and I provide a button to "see the full text." But when I click that button I want users to be able to click the button again to have the long version of the text go away.
Here's the full code, if it helps:
function ShowError(id) {
document.getElementById(id).className = document.getElementById(id).className.replace(/\bheight_limited\b/, '');
document.getElementById(id+"Text").className = document.getElementById(id+"Text").className.replace(/\bheight_limited\b/, '');
document.getElementById(id+"Button").innerHTML = "HIDE FULL ERROR";
document.getElementById(id+"Button").onclick = HideError(id);
}
function HideError(id) {
document.getElementById(id).className += " height_limited";
document.getElementById(id+"Text").className += " height_limited";
document.getElementById(id+"Button").innerHTML = "SHOW FULL ERROR";
document.getElementById(id+"Button").onclick = "ShowError(id)";
}
Your code is calling the function and assigning the return value to onClick, also it should be 'onclick'. This is how it should look.
document.getElementById("a").onclick = Bar;
Looking at your other code you probably want to do something like this:
document.getElementById(id+"Button").onclick = function() { HideError(id); }
var Foo = function(){
document.getElementById( "a" ).setAttribute( "onClick", "javascript: Boo();" );
}
var Boo = function(){
alert("test");
}
Do not invoke the method when assigning the new onclick handler.
Simply remove the parenthesis:
document.getElementById("a").onclick = Foo;
UPDATE (due to new information):
document.getElementById("a").onclick = function () { Foo(param); };
Thanks to João Paulo Oliveira, this was my solution which includes a variable (which was my goal).
document.getElementById( "myID" ).setAttribute( "onClick", "myFunction("+VALUE+");" );
I recommend this approach:
Instead of having two click handlers, have only one function with a if-else statement. Let the state of the BUTTON element determine which branch of the if-else statement gets executed:
HTML:
<button id="a" onclick="toggleError(this)">Button A</button>
JavaScript:
function toggleError(button) {
if ( button.className === 'visible' ) {
// HIDE ERROR
button.className = '';
} else {
// SHOW ERROR
button.className = 'visible';
}
}
Live demo: http://jsfiddle.net/simevidas/hPQP9/
You could try changing the button attribute like this:
element.setAttribute( "onClick", "javascript: Boo();" );
What might be easier, is to have two buttons and show/hide them in your functions. (ie. display:none|block;) Each button could then have it's own onclick with whatever code you need.
So, at first button1 would be display:block and button2 would be display:none. Then when you click button1 it would switch button2 to be display:block and button1 to be display:none.
For anyone, like me, trying to set a query string on the action and wondering why it's not working-
You cannot set a query string for a GET form submission, but I have found you can for a POST.
For a GET submission you must set the values in hidden inputs e.g.
an action of: "/handleformsubmission?foo=bar"
would have be added as the hidden field like: <input type="hidden" name="foo" value="bar" />
This can be done add dynamically in JavaScript as (where clickedButton is the submitted button that was clicked:
var form = clickedButton.form;
var hidden = document.createElement("input");
hidden.setAttribute("type", "hidden");
hidden.setAttribute("name", "foo");
hidden.setAttribute("value", "bar");
form.appendChild(hidden);
See this question for more info
submitting a GET form with query string params and hidden params disappear

Categories

Resources