I have an input name address (created by a plugin in WordPress context).
When I type some text in this input, I would like to modify an alpine component (new dropdown component used for autocompletion).
Before I used that :
address.addEventListener('input', _.debounce(event => callApi(event.target.value), 250));
Instead of calling callApi function, i need to act on the toggle.
In the dropdown component, I can add :
#set-title.window="title = $event.detail"
but how from my debounce, call the toggle ?
I should be able to do $dispatch('set-title', 'Hello World!') somewhere ?
I specify that I cannot modify my basic input (perhaps in javascript)
Thanks for help
You can create an event listener inside the Alpine.js component's init() method and mutate the title variable from there:
<div>
<input type="text" id="address" />
</div>
<div x-data="myComponent">
<div x-text="title"></div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('myComponent', () => ({
title: '',
init() {
const address = document.getElementById('address')
address.addEventListener('input', _.debounce(event => {this.title = event.target.value}, 250)
});
}
}))
})
</script>
Related
in html
<button mat-button class="p-0" (click)="addImage.click()" data-testid="addimagebutton">
<input type="file" #addImage (change)="addImageEvt($event)" accept="image/*" multiple hidden />
spec.ts
it('should call addImageEvt method on click', () => {
spyOn(component, 'addImageEvt').and.callThrough();
fixture.detectChanges();
const addImageButton = debugElement.query(
By.css('[data-testid="addimagebutton"]')
).nativeElement;
addImageButton.click();
addImageButton.dispatchEvent(new Event('change'));
let addImageChange = debugElement.query(By.css('#addImage'));
let addImageElement = addImageChange.nativeElement;
addImageElement.triggerEventHandler('change');
fixture.whenStable().then(() => {
expect(component.addImageEvt).toHaveBeenCalled();
});
it should return true,by calling the method. i did not get where i did mistake.. please help in write test case
I'm facing one issue when clicking on the button this.reloadDatatable() is not called. how can I call reloadDatatable() function from toggle function? below is the demo code for that. Thank you.
<button type="button" #click="addSettingContainer.toggle();"></button>
Alpine.data('initOrderGrid', () => ({
addSettingContainer: {
toggle() {
this.reloadDatatable();
}
},
reloadDatatable: function() {
console.log('a');
},
}));
You cannot access the parent object's scope from a child object without some ugly hacks. However it seem that you want to use an event system: on a button click you want to reload the data table. Alpine.js has builtin event system, we can use the $dispatch function to emit a custom event and the x-on method to capture our custom event:
<div x-data="initOrderGrid"
#reload-data-table.window="reloadDatatable">
</div>
<div x-data>
<button #click="$dispatch('reload-data-table')">Reload data table</button>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('initOrderGrid', () => ({
reloadDatatable() {
console.log('Data table reloaded');
}
}))
})
</script>
Currently I have a useLazyQuery hook which is fired on a button press (part of a search form).
The hook behaves normally, and is only fired when the button is pressed. However, once I've fired it once, it's then fired every time the component re-renders (usually due to state changes).
So if I search once, then edit the search fields, the results appear immediately, and I don't have to click on the search button again.
Not the UI I want, and it causes an error if you delete the search text entirely (as it's trying to search with null as the variable), is there any way to prevent the useLazyQuery from being refetched on re-render?
This can be worked around using useQuery dependent on a 'searching' state which gets toggled on when I click on the button. However I'd rather see if I can avoid adding complexity to the component.
const AddCardSidebar = props => {
const [searching, toggleSearching] = useState(false);
const [searchParams, setSearchParams] = useState({
name: ''
});
const [searchResults, setSearchResults] = useState([]);
const [selectedCard, setSelectedCard] = useState();
const [searchCardsQuery, searchCardsQueryResponse] = useLazyQuery(SEARCH_CARDS, {
variables: { searchParams },
onCompleted() {
setSearchResults(searchCardsQueryResponse.data.searchCards.cards);
}
});
...
return (
<div>
<h1>AddCardSidebar</h1>
<div>
{searchResults.length !== 0 &&
searchResults.map(result => {
return (
<img
key={result.scryfall_id}
src={result.image_uris.small}
alt={result.name}
onClick={() => setSelectedCard(result.scryfall_id)}
/>
);
})}
</div>
<form>
...
<button type='button' onClick={() => searchCardsQuery()}>
Search
</button>
</form>
...
</div>
);
};
You don't have to use async with the apollo client (you can, it works). But if you want to use useLazyQuery you just have to pass variables on the onClick and not directly on the useLazyQuery call.
With the above example, the solution would be:
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDogPhoto] = useLazyQuery(GET_DOG_PHOTO, {
onCompleted: data => setDog(data.dog)
})
return (
<div>
{dog && <img src={dog.displayImage} />}
<button
onClick={() => getDogPhoto({ variables: { breed: 'bulldog' }})}
>
Click me!
</button>
</div>
);
}
The react-apollo documentation doesn't mention whether useLazyQuery should continue to fire the query when variables change, however they do suggest using the useApolloClient hook when you want to manually fire a query. They have an example which matches this use case (clicking a button fires the query).
function DelayedQuery() {
const [dog, setDog] = useState(null);
const client = useApolloClient();
return (
<div>
{dog && <img src={dog.displayImage} />}
<button
onClick={async () => {
const { data } = await client.query({
query: GET_DOG_PHOTO,
variables: { breed: 'bulldog' },
});
setDog(data.dog);
}}
>
Click me!
</button>
</div>
);
}
The Apollo Client documentation isn't explicit about this, but useLazyQuery, like useQuery, fetches from the cache first. If there is no change between queries, it will not refetch using a network call. In order to make a network call each time, you can change the fetchPolicy to network-only or cache-and-network depending on your use case (documentation link to the fetchPolicy options). So with a fetchPolicy change of network-only in your example, it'd look like this:
const AddCardSidebar = props => {
const [searching, toggleSearching] = useState(false);
const [searchParams, setSearchParams] = useState({
name: ''
});
const [searchResults, setSearchResults] = useState([]);
const [selectedCard, setSelectedCard] = useState();
const [searchCardsQuery, searchCardsQueryResponse] =
useLazyQuery(SEARCH_CARDS, {
variables: { searchParams },
fetchPolicy: 'network-only', //<-- only makes network requests
onCompleted() {
setSearchResults(searchCardsQueryResponse.data.searchCards.cards);
}
});
...
return (
<div>
<h1>AddCardSidebar</h1>
<div>
{searchResults.length !== 0 &&
searchResults.map(result => {
return (
<img
key={result.scryfall_id}
src={result.image_uris.small}
alt={result.name}
onClick={() => setSelectedCard(result.scryfall_id)}
/>
);
})}
</div>
<form>
...
<button type='button' onClick={() => searchCardsQuery()}>
Search
</button>
</form>
...
</div>
);
};
When using useLazyQuery, you can set nextFetchPolicy to "standby". This will prevent the query from firing again after the first fetch. For example, I'm using the hook inside of a Cart Context to fetch the cart from an E-Commerce Backend on initial load. After that, all the updates come from mutations of the cart.
I'm trying to get the value of the attribute data-time-start when I click on the span.
My FIDDLE : http://jsfiddle.net/zagloo/7hvrxw2c/20/
HTML :
<textarea id="editor1"> <span class="sub" id="sub1" data-time-start="0">Hello </span>
<span class="sub" id="sub2" data-time-start="2">My </span>
<span class="sub" id="sub3" data-time-start="6">Name </span>
<span class="sub" id="sub4" data-time-start="8">Is </span>
<span class="sub" id="sub5" data-time-start="12">Zoob</span>
</textarea>
My JS:
var textarea;
$(document).ready(function () {
textarea = $('#ckeditor_block').find('textarea').attr('id');
ckeditor_init();
});
function ckeditor_init() {
CKEDITOR.replace(textarea, {
language: 'fr',
allowedContent: true
});
}
I tried with this:
CKEDITOR.on('click', function (e) {
var element = $(e.target);
console.log(element);
var cursor = element.data("timeStart");
console.log(cursor);
});
But nothing appened ...
How to do that please ? thank you !!
You can't (or better you shouldn't) use the default jQuery event/element handling in this case, because the CKEditor comes with its very own event/ element system.
Update: Based on the comments below, to avoid CKEditor's quirky behaviour, it is better to use attachListener instead of jQuery's 'on' to bind an event listener
Step one: Bind the click event:
var editorInstance = CKEDITOR.instances['editor1'];
editorInstance.on('contentDom', function() {
editorInstance.editable().attachListener(
this.document,
'click',
function( event ) {
// execute the code here
}
);
});
Step two: Find and access the data attribute:
var editorInstance = CKEDITOR.instances['editor1'];
editorInstance.on('contentDom', function() {
editorInstance.editable().attachListener(
this.document,
'click',
function( event ) {
/* event is an object containing a property data
of type CKEDITOR.dom.event, this object has a
method to receive the DOM target, which finally has
a data method like the jQuery data method */
event.data.getTarget().data('time-start');
}
);
});
For more info check the CKEditor docs.
Updated fiddle is here
I have the following code on a view:
#using (Html.BeginForm()) {
<div class="select-box form">
<p>
<strong>Select transcript name to view:</strong>
#Html.DropDownList("license", (SelectList)ViewBag.Licenses, new { onchange = "this.form.submit();" })
</p>
</div>
<div class="select-box form">
<p>
<strong>You may have multiple periods for each transcript name:</strong>
#Html.DropDownList("period", (SelectList)ViewBag.Periods, new { onchange = "this.form.submit();" })
</p>
</div>
}
I need to implement some logic depending on which dropdown cause the postback. I'm thinking to add a hidden input and set value of the control name by jQuery before submit the form, but I'm wondering if there is a 'native' way to do this.
This is my controller signature:
public ActionResult Checklist(int license, int period)
Thanks in advance!
I would apply a class to the dropdown so that my jQuery can use that as the selector criteria
#Html.DropDownList("license", (SelectList)ViewBag.Licenses, new { #class="mySelect"})
#Html.DropDownList("period", (SelectList)ViewBag.Periods, new { #class="mySelect"})
<input type="hidden" id="source" name="source" value="" />
And the script is
$(function(){
$(".mySelect").change(function(){
var itemName=$(this).attr("name");
$("#source").val(itemName);
$("form").submit()
});
});
Use something like this. (Here you are calling the action method instead of submitting the form)
Which ever dropdown caused the change will pass non zero value to the action method and the other will pass 0.
#Html.DropDownList("license", (SelectList)ViewBag.Licenses, new { onchange = "document.location.href = '/ControllerName/Checklist?license=' + this.options[this.selectedIndex].value + '&period=0'" })
#Html.DropDownList("period", (SelectList)ViewBag.Periods, new { onchange = "document.location.href = '/ControllerName/Checklist?license=0&period=' + this.options[this.selectedIndex].value;" })
Hope this helps!