My problem is that I want to insert values that are not repeated when doing a push
This is my code :
addAddress: function() {
this.insertAddresses.Adress = this.address_address
this.insertAddresses.State = this.selectedStateAddress
this.insertAddresses.City = this.selectedCityAddress
if(this.insertAddresses.Adress !== "" && this.insertAddresses.State !== null && this.insertAddresses.City !== null) {
let copia = Object.assign({}, this.insertAddresses);
this.addresses.push(copia)
}
else
{
this.$message.error('Not enough data to add');
return
}
},
When adding a new element to my object, it returns the following.
When I press the add button again, it adds the same values again, I want to perform a validation so that the data is not the same. How could I perform this validation in the correct way?
Verify that the item doesn't already exist in the array before inserting.
You can search the array using Array.prototype.find:
export default {
methods: {
addAddress() {
const newItem = {
Address: this.address_address,
State: this.selectedStateAddress,
City: this.selectedCityAddress
}
this.insertItem(newItem)
},
insertItem(item) {
const existingItem = this.addresses.find(a => {
return
a.State === item.State
&& a.City === item.City
&& a.Address === item.Address
})
if (!existingItem) {
this.addresses.push(item)
}
}
}
}
On the other hand, if your app requires better performance (e.g., there are many addresses), you could save a separate dictonary to track whether the address already exists:
export default {
data() {
return {
seenAddresses: {}
}
},
methods: {
insertItem(item) {
const { Address, State, City } = item
const key = JSON.stringify({ Address, State, City })
const seen = this.seenAddresses[key]
if (!seen) {
this.seenAddresses[key] = item
this.addresses.push(item)
}
}
}
}
demo
check it:
let filter= this.addresses.find(x=> this.insertAddresses.State==x.State)
if (filter==null) {
this.$message.error('your message');
}
OR FILTER ALL
let filter= this.addresses.find(x=> this.insertAddresses.Adress==x.Adress && this.insertAddresses.State==x.State && this.insertAddresses.City==x.City)
if (filter==null) {
this.$message.error('your message');
}
``
Related
I have LIVR in a project i'm working now and is quite unclear to me how this work. I can't understand how to create new rules for custom validation.
Here's the code:
LIVR.Validator.defaultAutoTrim(true);
let validator = new LIVR.Validator({});
LIVR.Validator.registerDefaultRules({
nested_object_value() {
return function (value) {
if (!value || (value && !value.value || value === [])) {
return 'REQUIRED';
}
return '';
};
},
max_number_advancement() {
return function (value) {
if (value > 100) {
return 'MAX_NUMBER';
}
return '';
};
},
required_if_activity_present() {
return function (value, allValue) {
if (allValue.activitycycletype && !value || allValue.requestpeople === []) {
console.log(first)
return 'REQUIRED_IF_CYCLETYPE';
}
return '';
};
},
});
And this is how its used:
validationForm = () => {
const { formValue, updateErrors } = this.props;
const validData = validator.validate(formValue);
console.log(formValue)
if (!validData) {
const errorsValidator = validator.getErrors();
if (errorsValidator && Object.keys(errorsValidator).length > 0) {
const newErrors = {};
Object.keys(errorsValidator).forEach((error) => {
newErrors[error] = errorsValidator[error];
});
updateErrors(newErrors);
}
blame(t('validation-error'));
return false;
}
updateErrors({});
return true;
}
Opening the form with this validation in the app, seems to call only the last method required_if_activity_present().
What i expect here is that i can create a new method inside registerDefaultRules(), that is a LIVR method, like this:
LIVR.Validator.registerDefaultRules({
re quired_not_empty() {
return function (value) {
if (!value) {
return 'REQUIRED';
}
return '';
};
},
... //other methods
}
but seems not working, the newer method is not being called at all by validator.validate()
Anyone know how to create a new rules where i can check if an element inside the object that has to be validate is an empty array?
Because seems that LIVR doesn't return a validation error in this case, but only on empty string and null values.
Thanks in advance
I have a little problem with my function, one of the params that I want to set I'm getting from the http request. The problem is that the final data from method below is recalculated in one of the components, and when it happens the result of request is still null. When the response come it's not triggering onChanges so I can't recalculate this data again, and doCheck triggering "too often".
updateData(componentRecords: ComponentRecord[], importSourceGroup?: ImportSource[], isImportSource = false, component: DiaryNode = null) {
const recordData = [];
const records = isImportSource ? importSourceGroup : componentRecords;
for (const record of records) {
const recordRow: any = record.ID === 'addingRow' ? record : {
ID: record.ID,
InputTypeID: record.InputTypeID,
SubRecords: record.SubRecords,
attachmentField: record.Fields ? record.Fields.find(({Type}) => Type === DiaryFieldType.ATTACHMENT) : null,
documentsFolder: null,
DateUpdated: null,
ComponentInstanceID: null,
linkedUnits: {},
recordRef: record
};
if (record.ID !== 'addingRow') {
if (isImportSource) {
recordRow.DateUpdated = (record as ImportSource).DateUpdated;
recordRow.ComponentInstanceID = (record as ImportSource).ComponentInstanceID;
}
if (recordRow.attachmentField && recordRow.attachmentField.Value) {
this.subManager.add(this.documentsApiService
.getFileDetailsByFolderID(recordRow.attachmentField.Value)
.subscribe((documents: DocumentsFolder) =>
recordRow.documentsFolder = documents));
}
if (record.Fields) {
for (const field of record.Fields) {
const label = field.Label;
recordRow[label] = field.Type === DiaryFieldType.INTEGER ? parseInt(field.Value, 10) : field.Value;
const schema = component && component.Schema && component.Schema.find(diaryFormField => diaryFormField.Label === label);
if (schema && schema.LinkedUnit) {
recordRow.linkedUnits[label] = schema.LinkedUnit.Attributes.List.PickListItems[0].Label;
}
}
}
}
recordData.push(recordRow);
}
return recordData;
}
The part that is async is
if (recordRow.attachmentField && recordRow.attachmentField.Value) {
this.subManager.add(this.documentsApiService
.getFileDetailsByFolderID(recordRow.attachmentField.Value)
.subscribe((documents: DocumentsFolder) =>
recordRow.documentsFolder = documents));
}
So I don't know what is the best solution for this but I was wondering if it's possible to wait here for the response, and go furthure when it comes.
What do you think?
In short the property cannot be assigned synchronously using the asynchronous HTTP request. Instead you need to make entire paradigm asynchronous. Then you could subscribe to the function updateData() to fetch the array.
Additionally you could use RxJS forkJoin function to combine multiple parallel observables. Try the following
updateData(
componentRecords: ComponentRecord[],
importSourceGroup?: ImportSource[],
isImportSource = false,
component: DiaryNode = null
): Observable<any> { // <-- return `Observable` here
const records = isImportSource ? importSourceGroup : componentRecords;
return forkJoin( // <-- use `forkJoin` to combine multiple parallel observables
records.map(record => {
const recordRow: any = record.ID === 'addingRow' ? record : {
ID: record.ID,
InputTypeID: record.InputTypeID,
SubRecords: record.SubRecords,
attachmentField: record.Fields ? record.Fields.find(({Type}) => Type === DiaryFieldType.ATTACHMENT) : null,
documentsFolder: null,
DateUpdated: null,
ComponentInstanceID: null,
linkedUnits: {},
recordRef: record
};
if (record.ID !== 'addingRow') {
if (isImportSource) {
recordRow.DateUpdated = (record as ImportSource).DateUpdated;
recordRow.ComponentInstanceID = (record as ImportSource).ComponentInstanceID;
}
if (record.Fields) {
for (const field of record.Fields) {
const label = field.Label;
recordRow[label] = field.Type === DiaryFieldType.INTEGER ? parseInt(field.Value, 10) : field.Value;
const schema = component && component.Schema && component.Schema.find(diaryFormField => diaryFormField.Label === label);
if (schema && schema.LinkedUnit) {
recordRow.linkedUnits[label] = schema.LinkedUnit.Attributes.List.PickListItems[0].Label;
}
}
}
if (recordRow.attachmentField && recordRow.attachmentField.Value) {
return this.documentsApiService.getFileDetailsByFolderID(recordRow.attachmentField.Value).pipe( // <-- return the HTTP request
map((documents: DocumentsFolder) => ({ ...recordRow, recordRow.documentsFolder: documents })) // <-- spread operator to append new value to object
);
}
return of(recordRow); // <-- use `of()` to return as observable
}
return of(recordRow); // <-- use `of()` to return as observable
})
);
}
See here to learn more about fetching info from async request.
I am using vue 3 where is i am receiving an array of associate schedule from server. Now i am saving this schedule to 2 arrays. I am doing this because i need the original fetched data later after doings changes in associate list array which is my first array.
associateList
orignalList
The problem is when I am replacing the associate array after doing changes with original array .No nothing works infact original list contains same changes which i did on associate list array even i have not touched the original list anywhere in my code just saving the data from response on it. I just want the original res on original list array so i can replace associate list with original array when watch function detect changes in attendance list array.
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import ApprovalService from "../../service/ApprovalService";
import Toaster from "../../helpers/Toaster";
import moment from "moment";
import { camelCase } from "lodash";
import {
ScheduleList,
AttendanceList,
ApprovedList,
} from "../hoursApproval/IHoursAppoval";
import VueCal from "vue-cal";
import "vue-cal/dist/vuecal.css";
import AssociatePinVerification from "../../components/AssociatePinVerification.vue";
#Options({
components: { VueCal, AssociatePinVerification },
watch: {
attendanceList() {
const oL = this.orignalList;
alert('orgi'+oL.length);
this.associateList = this.orignalList;
this.checkScheduleContainsLogedHrs();
},
},
})
export default class HoursApproval extends Vue {
private ApprovalTxn;
private scheduleID = "";
private toast;
private orignalList: ScheduleList[] = [];
private associateList: ScheduleList[] = [];
private approvedList: ScheduleList[] = [];
private attendanceList: AttendanceList[] = [];
private approveManually = {
hours: 0,
freezed: false,
shiftDate: "",
counterId: 0,
};
//DEFAULT METHOD OF TYPE SCRIPT
//CALLING WHENEVER COMPONENT LOADS
created() {
this.ApprovalTxn = new ApprovalService();
this.toast = new Toaster();
}
mounted() {
this.getSchedule();
}
getSchedule() {
this.ApprovalTxn.getAssociateShifts(this.searchDate).then((res) => {
const d = this.camelizeKeys(res);
const s = d.employeeList.scheduleList;
if (s != null)
{
this.orignalList = this.camelizeKeys(d.employeeList.scheduleList);
this.associateList = this.camelizeKeys(d.employeeList.scheduleList);
}
else
{
this.associateList = [];
this.orignalList = [];
}
this.scheduleID = d.employeeList.id;
this.weekStartingDate = d.postStartingDate;
this.weekEndingDate = d.postEndingDate;
this.weekNo = d.weekNo;
});
}
camelizeKeys = (obj) => {
if (Array.isArray(obj)) {
return obj.map((v) => this.camelizeKeys(v));
} else if (obj !== null && obj.constructor === Object) {
return Object.keys(obj).reduce(
(result, key) => ({
...result,
[camelCase(key)]: this.camelizeKeys(obj[key]),
}),
{}
);
}
return obj;
};
formatDate(value) {
if (value) {
return moment(String(value)).format("DD-MM-YYYY");
}
}
updateAssociateLogin() {
if (
this.loginDetails.loginTime == "" ||
this.loginDetails.logoutTime == "" ||
this.loginDetails.loginDate == ""
) {
this.toast.showWarning(
"Please set date login and logout timings for associate to proceed"
);
} else {
this.associateList = [];
this.ApprovalTxn.updateAssociateLogin(
this.loginDetails.loginTime,
this.loginDetails.attendenceID,
this.managerApproved,
this.loginDetails.logoutTime,
this.loginDetails.loginDate,
this.weekStartingDate,
this.weekEndingDate
).then((res) => {
this.toast.handleResponse(res);
alert(this.orignalList.length);
// this.associateList = this.orignalList;
const d = this.camelizeKeys(res);
//DOING THIS TO CHNAGE THE RE ACTIVITY OF VUE
//this.modifyTimings();
this.attendanceList = d.data;
//alert(this.orignalList.length);
//console.log(this.associateList);
});
this.loginHoursDialog = false;
}
}
}
</script>
I'm trying to figure out why my useEffect function ends up in an infinite loop.
I have two variables that are hooked into my Redux store:
const vehicles: AllVehiclesCollection = useSelector((state: ReduxState) => state.claims?.vehicles ?? {});
const properties: AllPropertiesCollection = useSelector((state: ReduxState) => state.claims?.properties ?? {});
and I have an action that is dispatched to the store that updates these only after a user clicks a button.
I have a useEffect that will trigger based on either of these variables changing.
useEffect(() => {
let fullVehicleList: DropdownData[] = getFormattedVehicleListForDisplay();
let fullPropertyList: DropdownData[] = getFormattedPropertyListForDisplay();
let fullList = fullVehicleList.concat(fullPropertyList);
if (fullList.length > 0) {
setVehiclesAndPropertiesList(fullList);
} else {
setVehiclesAndPropertiesList(null);
}
}, [vehicles, properties]);
Nowhere in this code are the vehicles or properties variables changed or any actions dispatched that would change the Redux state.
getFormattedVehicleListForDisplay function:
const getFormattedVehicleListForDisplay = () => {
let list: DropdownData[] = [];
if (Object.keys(vehicles).length > 0) {
let thisPolicysVehicles = [];
if (vehicles !== null) {
const key = `${selectedPolicy.symbol}${selectedPolicy.number}`;
thisPolicysVehicles = vehicles[key];
}
if (thisPolicysVehicles && thisPolicysVehicles.length > 0) {
thisPolicysVehicles.forEach((vehicle: VehicleInformation) => {
if (vehicle.vehicleMake !== OTHER_VEHICLE) {
list.push({
label: formatVehicleForDisplay(vehicle),
value: { ...vehicle, type: 'V' },
});
} else {
list.push({ label: vehicle.vehicleMake, value: {} });
}
});
}
}
return list;
};
getFormattedPropertyListForDisplay function:
const getFormattedPropertyListForDisplay = () => {
let list: DropdownDataOMIG[] = [];
if (Object.keys(properties).length > 0) {
let thisPolicysProperties = [];
if (properties !== null) {
const key = `${selectedPolicy.symbol}${selectedPolicy.number}`;
thisPolicysProperties = properties[key];
}
if (thisPolicysProperties && thisPolicysProperties.length > 0) {
thisPolicysProperties.forEach((property: LocationInformation) => {
if (property.locStreet1 !== OTHER_PROP) {
list.push({
label: formatPropertyForDisplay(property),
value: { ...property, type: 'P' },
});
} else {
list.push({ label: property.locStreet1, value: {} });
}
});
}
}
return list;
};
For reference, the data in vehicles and properties is a set of key-value pairs where the key is a unique identifier of a given account number and the value is an array of vehicle/property objects for that account.
Any idea why this goes into an infinite loop when using Redux state in the dependency array? Is there a different way to use Redux state in a dependency array? Thanks!
When using
const vehicles = useSelector((state: ReduxState) => state.claims?.vehicles ?? {});
Each time this is triggered, and you don't have vehicles in your store, you return a new object {}. and {} === {} // false
So ain your useEffect dependency array, it's each time a new Object, so useEffect is triggered.
So either remove your || {} in your selector (because null === null & undefined === undefined) or consider moving to useShallowSelector as explained in react-redux documentation
I've built a Vue JS search component to search and filter a list of properties for a property website. The search component is listed on every page, so it makes sense for me to use a URL search query which takes me to the main properties page and then use something like this.$route.query.search to get the value from my query and store in a variable.
The property data is coming from a JSON file which essentially looks like this:
{
"data": [
{
"id": 12,
"sold": false,
"isFeatured": true,
"slug": "green-park-talbot-green-cf72-8rb",
"address": "Green Park, Talbot Green CF72 8RB",
"county": "Rhondda Cynon Taf",
"price": "695000",
"features": ["Modern fitted kitchen", "Integrated appliances", "Front and rear gardens"],
"type": "Semi-Detached",
"bedrooms": "3"
}
}
My search query would be something like this:
/properties/?search=Address%20Here&type=Apartment&bedrooms=2&county=Maesteg
Which then would filter each thing.
How this works is quite simple, inside my data object I have my variables which get each query and store them as follows:
data () {
return {
searchAddress: this.$route.query.search,
searchType: this.$route.query.type,
searchBedrooms: this.$route.query.bedrooms,
searchCounty: this.$route.query.county
}
}
And then I have a filter inside the computed area called filteredProperties which filters down the properties inside the v-for which isn't necessary to show here:
computed: {
filteredProperties: function(){
return this.properties.filter((property) => {
return property.address.match(this.searchAddress) && property.type.match(this.searchType) && property.bedrooms.match(this.searchBedrooms) && property.county.match(this.searchCounty)
});
}
}
Now this works absolutely fine and works correctly... however I now need to modify this to instead of having <select> dropdowns which is how you would currently pick the number of bedrooms, or the property type etc, I now need to replace the property type <select> dropdown with checkboxes so that the user can select multiple property types and essentially add that as an array into the URL.
I'm not quite sure how to modify this part of my filter to be able to look for multiple property types:
property.type.match(this.searchType)
Many thanks
UPDATE
I've recently tried updating my computed filter with the following:
computed: {
filteredProperties: function(){
return this.properties.filter((property) => {
return property.address.match(this.searchAddress) &&
this.searchAddress.some(function(val){
return property.search.match(val)
}) &&
property.type.match(this.searchType) &&
this.searchType.some(function(val){
return property.type.match(val)
}) &&
property.bedrooms.match(this.searchBedrooms) &&
this.searchBedrooms.some(function(val){
return property.bedrooms.match(val)
}) &&
property.county.match(this.searchCounty) &&
this.searchCounty.some(function(val){
return property.county.match(val)
})
});
}
}
I need the search to work with and without a URL query.
Also tried an if/else statement:
computed: {
filteredProperties: function(){
return this.properties.filter((property) => {
return property.address.match(this.searchAddress) &&
if (this.searchType.length > 1) {
this.searchType.some(function(val){
return property.type.match(val)
})
} else {
property.type.match(this.searchType)
} &&
property.bedrooms.match(this.searchBedrooms) &&
property.county.match(this.searchCounty)
});
}
}
UPDATE
I got it working by doing the following:
computed: {
filteredProperties: function(){
return this.properties.filter((property) => {
let searchTypeMatch;
if (typeof this.searchType === "object") {
searchTypeMatch = this.searchType.some(function(val){
return property.type.match(val)
})
} else {
searchTypeMatch = property.type.match(this.searchType)
}
return property.address.match(this.searchAddress) &&
searchTypeMatch &&
property.bedrooms.match(this.searchBedrooms) &&
property.county.match(this.searchCounty)
});
}
}
You will have to use JSON for the query parameters in order to serialize/deserialize the arrays.
data () {
return {
searchAddress: this.$route.query.search ? JSON.parse(this.$route.query.search) : [],
searchType: this.$route.query.type ? JSON.parse(this.$route.query.type) : [],
searchBedrooms: this.$route.query.bedrooms ? JSON.parse(this.$route.query.bedrooms) : [],
searchCounty: this.$route.query.county ? JSON.parse(this.$route.query.county) : []
}
}
computed: {
filteredProperties: function()
{
return this.properties.filter((property) =>
{
return (this.searchAddress.length ? this.searchAddress.some((address) =>
{
return property.address.match(address);
}) : true) && (this.searchType.length ? this.searchType.some((type) =>
{
return property.type.match(type);
}) : true) && (this.searchBedrooms.length ? this.searchBedrooms.some((bedrooms) =>
{
return property.bedrooms.match(bedrooms);
}) : true) && (this.searchCounty.length ? this.searchCounty.some((county) =>
{
return property.county.match(county);
}) : true)
});
}
}
Then you send query params like this
this.$router.push("/search?search=" + JSON.stringify(searchArray)
+ "&type=" + JSON.stringify(typeArray)
+ "&bedrooms=" + JSON.stringify(bedroomsArray)
+ "&county=" + JSON.stringify(countyArray)
);
I have not worked with routing in Vue apps but for the following to work you will have to ensure that this.$route.query.search (and the other route properties) is an (are) [](s).
return this.searchAddress.some(function(val) {
return property.address.match(val)
}) &&
this.searchType.some(function(val){
return property.type.match(val)
}) && ...
Let me know if this works for you.
RE-EDITED:
Hi, please change the computed property to the following
computed: {
filteredProperties: function () {
let self = this
let routeConstraints = ['search', 'type', 'bedrooms', 'county'].filter(function (val) {
return self.$route.query[val] !== undefined
})
if (routeConstraints.length === 0) {
return self.properties
} else {
return routeConstraints.reduce(function (acc, val) {
return acc.filter(function (property) {
//basically I am checking if there is some value in the query parameter that matches properties.
if (self.$route.query[val] !== undefined) {
//check if the route parameter is an array (object)
if (typeof this.searchType === "object") {
self.$route.query[val] = [self.$route.query[val]]
}
}
return self.$route.query[val].some(function (item) {
//changed the matching condition to indexOf
return property[val].match(item).length > 0
})
})
}, self.properties)
}
}
}
Basically,
I am trying to check what routes values are set from your checkbox selections
Using it to filter the properties array.
If none are set then all properties are returned.
Hope this works.