How do I get the value from sibling of selected option and pass it as value in inputfield autofill (VueJS) - vue.js

Flow of my app:
user types a city
user select a country code in select
then submit
But the problem is that when someone types 'ams' instead of 'amsterdam' it will also output a list of responding country codes. If someone select one and then click on the submit button. It will give an error that the this.city is undefined. The reason for this is because the url that is fetched consists variables of this.city and this.country_code. Something looks like www.weatherbit.com/${this.city}=country${this.country_code} and that is the reason why it gives an error in this case. So in order to avoid that I want to get the autofill option if the this.city is not a valid city name. When someone clicks on country code 'NL' and the city name is undefined (not matching the entire city name) it will autofill the input field with that. In order to do so it needs to get the 'country.city_name' and pass it to v-model="city". How can I do that.
<select v-model="countryCode" #change="handleChange">
<option v-for="(country, index) in checkCountryName" :key="index" :value="country.country_code" :selected="index === 0">
{{ country.city_name}} // only this value should be output in input /// and should not be in option element
{{ country.country_code }} // only this should be shown in option if this is undefined
</option>
</select>
<input v-model="city" placeholder="Please enter your location...">
data () {
return {
city: '',
cityList: [],
countryCode: null,
errors: []
}
},
mounted () {
axios.get('https://gist.githubusercontent.com/chiholiu/9243c14cd7f310c0866947414496ad99/raw/8f97404080b3812477004793e2318552fc876aa2/cities.json')
.then(response => {
this.cityList = response.data
})
},
computed: {
checkCountryName () {
if(this.city.length < 1) return;
return this.cityList.filter((cityName) => {
return cityName.city_name.toLowerCase().match(this.city.toLowerCase())
})
}
},

There are few options to achieve this scenario.
Option 1: Enter full city name
Instead of match, find the city the user typed is fully matching the city_name. This will give you the full city name when you call the api.
Option 2: Change the input value
Once the user typed the half word like ams and select a country, at handleChange event in select, you can set the city_name to city like
methods: {
handleChange(e) {
this.city = e.target.value.city_name
}
}

Related

Correct way to implement drill-down tags in vue with vuetify

I am using a v-chip-group with v-chips to represent a tag cloud for records in my database. I have an object array with records that look something like { key:'device', count:100}. A record could have multiple tags, so as you click on a tag, a new query is made that filters on that tag, the result will then have a new tag cloud with a subset of the previous.
It looks something like this:
tag1 (1000), tag2 (100), tag3 (100)
When you click on tag1 you end up with:
tag1 (1000), tag3 (15) (no tag2 because there is no overlap between tag1 and tag2).
Here is the relevant template code:
<v-chip-group v-model="selectedTag" multiple #change="refresh">
<v-chip v-for="tag in tags" :key="tag.key" active-class="primary">
<v-avatar left class="grey">{{ tag.count }}</v-avatar>
{{ tag.key }}
</v-chip>
</v-chip-group>
The problem I have is that in the typescript I do something like this:
refresh() {
// get simple array of tag strings
const selectedTags = this.selectedTag.map((value: any) => {
if (this.tags && this.tags[value]) {
return this.tags[value].key
} else {
return null
}
}).filter((value: any) => {
return value != null
})
Promise.all([
...
ApiCall('GET', 'tags', {limit: 1000, tags: selectedTags}),
...
).then((values) => {
// decode response from server into new tags
this.tags = values[2].series['0'].values.map((item: any) => {
return {key: item.bucket, count: item.doc_count}
})
const newTags: number[] = []
this.tags.forEach((tag, index) => {
// find the new index of the previously selected tags and save them
if (selectedTags.find(st => {
return st === tag.key
})) {
newTags.push(index)
}
})
// update selectedTag with the new value
this.$set(this, 'selectedTag', newTags)
// did not work this.selectedTag = newTags
})
}
What I'm seeing is that when I click a chip, it correctly fires the #change event and calls refresh, but then when the refresh finishes, I see an additional refresh get called with an empty selectedTag, which clears my filters and recalls the above functionality.
Is there a way to get #change to fire when a chip is changed, but not fire (or filter it out) when the event is generated by changing the data referenced by v-model?

q-input has value then only Rules will apply

If q-input has value != '' then only i want to apply the Rules like required 8 number maximum. In the below code it gives me the required input error even it's null.
<q-input
filled
name="landline"
label="Landline Phone Number"
v-model="user.landline"
placeholder="Landline Phone Number"
ref="landlinePhoneNumber"
type="number"
:maxlength="8"
:rules="[val => val!='' && val.length > 7 || 'Landline Required 8 digit']"
/>
Try to add prop lazy-rules.
By default, it's set to 'ondemand', which means that validation will be triggered only when the component’s validate() method is manually called or when the wrapper QForm submits itself. More info
You have to return true when the field is null first, then validate only if it's not null. Also, add the prop lazy-rules so that it only validates when the form field loses focus.
Here is how I did it in Vue 3, using composable and TypeScript. The form field component:
<q-input
class="q-mt-md"
filled
v-model="id_number"
label="ID Number "
type="text"
hint="Optional/Leave blank if not available"
lazy-rules
:rules="[(val) => isNumberBlankOrValid(val) || 'Invalid ID Number']"
/>
The method isNumberBlankOrValid called from the field above:
const isNumberBlankOrValid = (val: string) => {
if (val.length === 0) {
return true
}
return isValidNumber(val)
}
The isValidNumber for other fields that must be filled:
const isValidNumber = (val: string) => val && isNumeric(val)
The isNumeric method is a simple regex for validating numbers:
const isNumeric = (value: string) => {
return /^\d+$/.test(value)
}

How to validate multiple user inputs within just one popup using Vue-SweetAlert2

As a coding training, right now I'm making a web page where you can click a "Create" button, which triggers a popup, where you are supposed to fill in 6 data inputs, whose input style varies like text, select etc. (See the code and the attached image below)
<template>
<v-btn class="create-button" color="yellow" #click="alertDisplay">Create</v-btn>
<br/>
<p>Test result of createCustomer: {{ createdCustomer }}</p>
</div>
</template>
<script>
export default {
data() {
return {
createdCustomer: null
}
},
methods: {
alertDisplay() {
const {value: formValues} = await this.$swal.fire({
title: 'Create private customer',
html: '<input id="swal-input1" class="swal2-input" placeholder="Customer Number">' +
'<select id="swal-input2" class="swal2-input"> <option value="fi_FI">fi_FI</option> <option value="sv_SE">sv_SE</option> </select>'
+
'<input id="swal-input3" class="swal2-input" placeholder="regNo">' +
'<input id="swal-input4" class="swal2-input" placeholder="Address">' +
'<input id="swal-input5" class="swal2-input" placeholder="First Name">' +
'<input id="swal-input6" class="swal2-input" placeholder="Last Name">'
,
focusConfirm: false,
preConfirm: () => {
return [
document.getElementById('swal-input1').value,
document.getElementById('swal-input2').value,
document.getElementById('swal-input3').value,
document.getElementById('swal-input4').value,
document.getElementById('swal-input5').value,
document.getElementById('swal-input6').value
]
}
})
if (formValues) {
this.createdCustomer = this.$swal.fire(JSON.stringify(formValues));
console.log(this.createdCustomer);
}
}
}
}
</script>
Technically, it's working. The popup shows up when the "create" button is clicked, and you can fill in all the 6 blanks and click the "OK" button as well. But I want to add some functionalities that check if the user inputs are valid, I mean things like
address should be within 50 characters
firstName should be within 20 characters
customerNumber should include both alphabets and numbers
and so on.
If it were C or Java, I could probably do something like
if(length <= 50){
// proceed
} else {
// warn the user that the string is too long
}
, but when it comes to validating multiple inputs within a single popup using Vue-SweetAlert2, I'm not sure how to do it, and I haven't been able to find any page that explains detailed enough.
If it were just a single input, maybe you could use inputValidor like this
const {value: ipAddress} = await Swal.fire({
title: 'Enter an IP address',
input: 'text',
inputValue: inputValue,
showCancelButton: true,
inputValidator: (value) => {
if (!value) {
return 'You need to write something!'
}
}
})
if (ipAddress) {
Swal.fire(`Your IP address is ${ipAddress}`)
}
, but this example only involves "one input". Plus, what this checks is just "whether an IP address has been given or not" (, which means whether there is a value or not, and it doesn't really check if the length of the IP address is correct and / or whether the input string consists of numbers / alphabets).
On the other hand, what I'm trying to do is to "restrict multiple input values (such as the length of the string etc)" "within a single popup". Any idea how I am supposed to do this?
Unfortunately the HTML tags to restrict inputs (e.g. required, pattern, etc.) do not work (see this issues),
so I find two work around.
Using preConfirm as in the linked issues
You could use preConfirm and if/else statement with Regex to check your requirement, if they are not satisfied you could use Swal.showValidationMessage(error).
const{value:formValue} = await Swal.fire({
title: "Some multiple input",
html:
<input id="swal-input1" class="swal-input1" placeholder="name"> +
<input id="swal-input2" class="swal-input2" placeholder="phone">,
preConfirm: () => {
if($("#swal-input1").val() !== "Joe"){
Swal.showValidationMessage("your name must be Joe");
} else if (!('[0-9]+'.test($("#swal-input2").val())){
Swal.showValidationMessage("your phone must contain some numbers");
}
}
});
Using Bootstrap
In this way Bootstrap does the check at the validation, you need to include class="form-control" in your input class and change a little your html code.
If some conditions fails, the browser shows a validation message for each fields, in the order they are in the html code.
const {value: formValue} = await Swal.fire({
title: 'Some multiple inputs',
html:
'<form id="multiple-inputs">' +
'<div class="form-group">' +
'<input type="text" class="form-control swal-input1" id="swal-input1" min=2 max=4 required>' +
'</div>' +
'<div class="form-group">' +
'<input type="text" class="form-control swal-input2" id="swal-input2" placeholder="Name" pattern="[A-Za-z]" required>' +
'</div>' +
'</form>',
});
I have tried both the solution, actually only with Bootstrap3 but it should work also with the latest release.

Angular Translate default translate value while using filter

Is there any way to provide the translate-default value while using the filter instead of directive?
e.g:
How to achieve the same results as this
<h3 translate="TEST" translate-default="Not present"></h3>
with filter format
{{ 'TEST' | translate }}
How do i put the "translate-default" attribute when using the translate filter?
What i need to do is show the original text if the key is not present.
I have created a wrapping filter for that purpose:
.filter('txf', ['$translate', ($translate: angular.translate.ITranslateService) => {
return (input: string, stringIfNotAvailable: string = '') => {
const translation = $translate.instant(input);
return translation === input ? stringIfNotAvailable : translation;
};
}]);

select a record from a list-box using text in Protractor

I want to select the record from the list box using text. how can i use the filter function to select the particular record. I will be having many options but i want to select the value which i want by checking the text (e.g Spanish). I dont want to select value by index becoz if i do that i wont be able to verify test, moreover list gets updated. kindly help. below r my html code.
<ul class="addList">
<li ng-repeat="skill in availableSkills" ng-click="addSkillFunc(skill, $index)" class="ng-binding ng-scope">Mandarin</li>
<li ng-repeat="skill in availableSkills" ng-click="addSkillFunc(skill, $index)" class="ng-binding ng-scope">English</li>
<li ng-repeat="skill in availableSkills" ng-click="addSkillFunc(skill, $index)" class="ng-binding ng-scope">Spanish</li>
</ul>
Yea i can select the record by index. but i want something like selectbyvisibleText which is available in Selenium.
Finally got the solution. created a function SelectRowByCellValue and used to call it where ever i want by
SelectRowByCellValue(AGP.SkillList, Data.SkillSelect);
SkillList = element.all(by.css('Ul.addList li'));
SkillSelect = Value that u want to select. (Spanish)
this.SelectRowByCellValue = function (Elem, Texts) {
Elem.filter(function (element) {
return element.getText().then(function (text) {
if (text == Texts) {
element.click();
return false;
}
});
}).then(function (filteredElements) {
});
};