is(':checked')/this.checked not working - input

Tried several solutions before posting, but none seems to be working and I must be doing something wrong.
My code is as follows (simplified)
<label>
<input type="checkbox" id="menu-image-settings-container" value="1" name="..." checked="checked">
Add image?
</label>
<div class="image-settings">show content if checked</div>
js code
$( ".image-settings" ).hide();
var wrapper = $(this).closest('li.menu-item');
if ( $('input#menu-image-settings-container').is(':checked') ) {
wrapper.find('.image-settings').show();
console.log('is-checked');
} else {
wrapper.find('.image-settings').hide();
console.log('is-not-checked');
}
$('input#menu-image-settings-container').change(function(){
if(this.checked) {
wrapper.find('.image-settings').show();
} else {
wrapper.find('.image-settings').hide();
}
});
console.log says is-not-checked, while it clearly is in the source code.
I've tried using if ($('input#menu-image-settings-container').is(':checked')) {...} but that didn't worked either, while normally that should work as well.

First, is(':checked') is a correct solution. Here's why: https://stackoverflow.com/a/7672031/9145243
And you can see that in this part of code your console.log works.
The actual problem is in this construction: wrapper.find('.image-settings').hide(); This code just can't find your .image-settings element.
I've replaced it with $('.image-settings') just to show you that the problem was here.
$('input#menu-image-settings-container').change(function() {
if($(this).is(':checked')) {
$('.image-settings').show();
} else {
$('.image-settings').hide();
}
});
When you debug your code it's a good practise to always use console.log, line by line. And then you will clearly see where exactly you have a problem

Related

Vue class components dynamically add component depending on answer from backend

So from the backend I get a array of objects that look kind of like this
ItemsToAdd
{
Page: MemberPage
Feature: Search
Text: "Something to explain said feature"
}
So i match these values to enums in the frontend and then on for example the memberpage i do this check
private get itemsForPageFeatures(): ItemsToAdd[] {
return this.items.filter(
(f) =>
f.page== Pages.MemberPage &&
f.feature != null
);
}
What we get from the backend will change a lot over time and is only the same for weeks at most. So I would like to avoid to have to add the components in the template as it will become dead code fast and will become a huge thing to have to just go around and delete dead code. So preferably i would like to add it using a function and then for example for the search feature i would have a ref on the parent like
<SearchBox :ref="Features.Search" />
and in code just add elements where the ItemsToAdd objects Feature property match the ref
is this possible in Vue? things like appendChild and so on doesn't work in Vue but that is the closest thing i can think of to kind of what I want. This function would basically just loop through the itemsForPageFeatures and add the features belonging to the page it is run on.
For another example how the template looks
<template>
<div class="container-fluid mt-3">
<div
class="d-flex flex-row justify-content-between flex-wrap align-items-center"
>
<div class="d-align-self-end">
<SearchBox :ref="Features.Search" />
</div>
</div>
<MessagesFilter
:ref="Features.MessagesFilter"
/>
<DataChart
:ref="Features.DataChart"
/>
So say we got an answer from backend where it contains an object that has a feature property DataChart and another one with Search so now i would want components to be added under the DataChart component and the SearchBox component but not the messagesFilter one as we didnt get that from the backend. But then next week we change in backend so we no longer want to display the Search feature component under searchbox. so we only get the object with DataChart so then it should only render the DataChart one. So the solution would have to work without having to make changes to the frontend everytime we change what we want to display as the backend will only be database configs that dont require releases.
Closest i can come up with is this function that does not work for Vue as appendChild doesnt work there but to help with kind of what i imagine. So the component to be generated is known and will always be the same type of component. It is where it is to be placed that is the dynamic part.
private showTextBoxes() {
this.itemsForPageFeatures.forEach((element) => {
let el = this.$createElement(NewMinorFeatureTextBox, {
props: {
item: element,
},
});
var ref = `${element.feature}`
this.$refs.ref.appendChild(el);
});
}
You can use dynamic components for it. use it like this:
<component v-for="item in itemsForPageFeatures" :is="getComponent(item.Feature)" :key="item.Feature"/>
also inside your script:
export default {
data() {
return {
items: [
{
Page: "MemberPage",
Feature: "Search",
Text: "Something to explain said feature"
}
]
};
},
computed: {
itemsForPageFeatures() {
return this.items.filter(
f =>
f.Page === "MemberPage" &&
f.Feature != null
);
}
},
methods: {
getComponent(feature) {
switch (feature) {
case "Search":
return "search-box";
default:
return "";
}
}
}
};

vuelidate $touch() for same form creating/editing

I might have somewhat a tricky question and just want to be sure, I went the correct way.
To be precise, it's vuelidate 0x on vue 2 with good old boostrap-vue
Let's pressure we have a simple form, with the following validation
<b-form>
.....
<b-form-group
id="title"
label="Title"
label-for="Title"
>
<b-form-input
id="title-input"
v-model="$v.form.title.$model"
:state="validateState('title')"
type="text"
></b-form-input>
<b-form-invalid-feedback id="title-input-live-feedback">
You must enter a title.
</b-form-invalid-feedback>
</b-form-group>
....
validations: {
form: {
title: { required }
}
},
So when we create this item. I do the following:
async createCompany() {
if (!this.$v.$dirty) {
this.$v.$touch()
}
if (this.$v.$invalid) return
try {
await this.storeCompany()
...
} catch (error) {
//
}
},
Now, I come back to the form, for example, to modify it. This means, the item has once passed the validation successfully, especially for required fields.
I noticed that in that sense $touch() is not necessary, unless you add new validations, which would always result in $invalid. Still, I wanted to have a solution that will also work for this case, just so I do not stumble over it in the future.
So, to come up with a "flexible" solution, I did the following:
async updateCompany() {
if (this.$v.$invalid && !this.$v.$dirty) {
this.$v.$touch()
return
}
if (this.$v.$invalid) return
try {
let response = await CompaniesApi.update(this.id, this.form)
...
} catch (error) {
//
}
},
Basically, I first check if $invalid is true and if the form was not submitted (by checking for !this.$v.$dirty).
It works exactly as I want, no double, triple touches and no flickering in terms of validation changes due to touches.
What gives me a bit of a headache is the fact that I first check for this.$v.$invalid before I actually $touch().
Somehow, I thought it must be the other way around.
But I guess I misunderstood $touch() in a way that it's main job is really to show the $errors if needed (vuelidate only shows errors when $invalid and $dirty is set to true).
I know I could go the other way, disabling the submit button until the form is valid, but then I would need to add further elements to the form, e.g. make clear what is missing or maybe even $touch() the validation in the created() or mounted() hook.
Which however is also not what I want, because it would show errors to the user without any interaction.
So, the only solution I could find is the one described.
Did anybody else come across this? I mean, it does what I want, still would appreciate any feedback/ideas.

Cypress - How to get element using value in the text field

I have couple of text_area_field in my application which can be distinguished only by using the value inside it. How can I get a specific element using value?
Ex:
<div data-component="options">
<input data-component="text_area">Value 1</input>
<input data-component="text_area">Value 2</input>
<input data-component="text_area">Value 3</input>
<input data-component="text_area">Value 4</input>
I have tried
cy.get('[data-component="options"] [data-component="text_area"]').contains('Value 3').clear().type('Edited 3').
but it is giving the error :
Timed out retrying: Expected to find content: 'Value 3' within the element: but never did
Any help would be highly appreciated.
Check if the div allows typing. If yes, try the below code and see if that is working:
cy.get('[data-component="options"] [data-component="text_area"]').each(($div, i) => {
const valueText = Cypress.$($div).text();
console.log(valueText);
if(valueText === "Value 3") {
cy.wrap($div).clear().type("Edited 3");
}
})
You could also try using eq(), but if there are changes in selector with more options it may fail.
cy.get('[data-component="options"] > div').eq(2).then(($div)=>{
cy.wrap($div).contains("Value 3").clear().type("Edited 3");
})
You could possibly try this one too:
cy.get('[data-component="options"] > div').contains("Value 3").clear().type("Edited 3");
I think this will work .Try this code:
cy.get('[data-component="options"] > div').invoke('show').eq(2).then(($div)=>{
cy.wrap($div).contains("Value 3").clear().type("Edited 3");
})

Aurelia - bound control's backing property setter called twice

I implemented a custom control which is basically a styled file uploader specifically for images, which allows the preview of images. The problem is that I experience a weird behavior: the setter of the property to which the regular HTML <input type="file"> is bound gets called twice in FF 53, IE 11, Edge 38, but not in Chrome 57. I have no idea why this happens, perhaps some of you have experienced similar behavior and know a solution.
The markup is the following:
<template>
<!-- markup for preview functionality, irrelevant here -->
<input type="file" class="sr-only" id="${id}" aria-describedby="${id}-help" multiple accept="image/*" files.bind="imageGroup" />
<label class="btn btn-default btn-sm" aria-hidden="true" for="${id}" role="button">
<i class="fa fa-plus" aria-hidden="true"></i> Add files
</label>
</template>
The backing TypeScript code:
import { bindable, bindingMode, autoinject } from 'aurelia-framework';
import { DialogService } from 'aurelia-dialog';
import { Confirm } from '../confirm';
#autoinject
export class ImageUploader {
#bindable({ defaultBindingMode: bindingMode.twoWay }) imageArray: Array<File> = null;
#bindable id: string = null;
#bindable maxImageCount: number = 10;
#bindable maxFileSizeKiloBytes: number = 2048;
constructor(private dialogService: DialogService) { }
idChanged(newValue: string) {
if (!newValue) {
throw Error('The id parameter needs a value in order to make the file uploader work.');
}
}
set imageGroup(fileList: FileList) {
console.log('imageGroup');
if (!fileList || !fileList.length) return;
if (!this.imageArray) return;
for (let i = 0; i < fileList.length; ++i) {
let file = fileList.item(i);
if (this.imageArray.length >= this.maxImageCount) {
// TODO: Alert: maximum number of images reached
} else {
if (file && file.type.startsWith('image/')) {
// Size is in bytes. Div by 1024 to get kilobytes.
if (file.size / (1024) > this.maxFileSizeKiloBytes) {
// TODO: Alert: Image file too large.
} else {
this.imageArray.push(file);
}
} else {
// TODO: Alert: unsupported media type.
}
}
}
}
}
As you can see, I use the "hide the default file uploader and style a bound label instead" trick to style the uploader itself - I only say this to point out that I've checked if maybe this is the cause, but it's not, the same behavior can be experienced if I show the default file uploader and use that.
You can also see that I've bound the <input type="file"> to a setter-only property. I've done that because this property is basically just a proxy which populates another array (which is not in the control, this is what is bound to the control) which is necessary because I need to allow the user to upload files in multiple turns so that (s)he can select files from different folders.
The rest of the code is irrelevant, because the console.log line runs twice whenever I select some files, so the error is located somewhere here - I am just unable to figure out exactly what is causing this to happen.
Any help is appreciated.
I confirmed that using setter on that case causes to call your setter two times in FF, aurelia created another setter for every property that needs to observe. However you can achieve same behavior using observable with minimal changes and also provided encapsulation in this case. See those gist.run link. More info here.
UPDATE
For Firefox this is still open as a bug. But a workaround using change.delegate as elefantino and abemeister said works on FF, see gist here.

Disqus Plugin Explanation of Dynamic Tags

So I am using the Disqus Plugin v2.65. I am trying to edit the dsq-global-toolbar at the top of the Disqus comments.
The following tags are in disqus-comment-system/comments.php
<div id="disqus_thread">
<?php if (!get_option('disqus_disable_ssr')): ?>
<?php
// if (is_file(TEMPLATEPATH . '/comments.php')) {
// include(TEMPLATEPATH . '/comments.php');
// }
?>
<div id="dsq-content">
<ul id="dsq-comments">
however on my site there are mulitple tags (the disqus-global-toolbar div) that seem to be dynamically appended between the dsq-content div and the dsq-comments ul. Where is this coming from and where can I edit this? Any help would be greatly appreciated.
I think it is coming somewhere around line 3140 in disqus.js
You can use this code to wait for the document to finish loading completely then do your changes (client side):
$(document).ready(function() {
window.disqus_no_style = true;
$.getScript('http://sitename.disqus.com/embed.js', function() {
var loader = setInterval(function() {
if($('#disqus_thread').html().length) {
clearInterval(loader);
disqusReady();
}
}, 1000);
});
function disqusReady() {
//whatever you can imagine
}
});
window.diqus_no_style can be deleted as well as the $.getsript wrapper.
Is that what you are looking for?
Something like this (use livequery instead of live):
function disqusReady() {
$('#dsq-global-toolbar').livequery(function() {
//$(this) will refer to object
});
}
Not sure what plug-in you're talking about, but if it's WordPress, I've done the same thing. Modify wp-content/plug-ins/disqus-comment-system/comments.php by adding an event handler for 'afterRender' (fires when the content ready in the DOM, but still hidden), eg. around line 70:
config.callbacks.afterRender.push(myFunctionToModifyDisqusOutput);