Angular: RxWeb unique() validator not working properly - javascript

I have implemented a form that has a FormArray. In the FormArray, one control needs to have a unique value throughout the array. The unique() validator from RxWeb is working fine normally but the issue is working when I am pushing values in the FormArray manually it is not triggering the unique() validator. An example method mentioned below which is called on a button click:
abc() {
let skillsArray = <FormArray>this.jobFormGroup.controls.skills;
skillsArray.removeAt(0);
const arr = [
{ skillName: 'A' },{ skillName: 'A' },
];
arr.forEach(item => skillsArray.push(this.formBuilder.group(item)))
}
None of the field is highlighed as red.
Highlighted in red when I enter data from UI.
You can use this Stackblitz example for playing around with the issue. Please click on the "Trigger Issue" button to create the issue shown in Image 1.

Because your function abc() does not add the controls with the unique validator
const arr = [
{ skillName:['A',RxwebValidators.unique()] },{ skillName:
['A',RxwebValidators.unique()] },
];

Related

ReactJS Complex Form Input Field Duplicating Text in Multiple Input Fields

I have a form where you can add/remove groups and input items, I was able to get the groups of input field working, but I'm having trouble with the items input within the respected groups:
I created a code sand box here: https://codesandbox.io/s/twilight-cache-4ipv6?file=/src/Form.jsx
If you click on Add Items + button, and type in the item fields, the value duplicates to all the fields.
Also, sometimes I feel like the x button doesn't work, and will only remove the last item or something, I believe this is "controlled component?"
In addition, I want to ask if there's a better method on what I'm doing? It seems like there's a lot of complexities in the code I'm trying to write up. I feel like I'm writing too much of the set state hooks.
I think we don't need that fields state.
And we can update Add Handlers like this
const handleAddGroup = i => {
const newGroup = [...group];
newGroup.push({
id: null,
cat: "",
items: [
{
name: "",
value: ""
}
]
});
setGroups(newGroup);
};
const handleAddField = i => {
setGroups(state => {
const stateCopy = JSON.parse(JSON.stringify(state));
stateCopy[i].items.push({
name: "",
value: ""
});
return stateCopy;
});
};
https://codesandbox.io/s/cool-frog-2yrlt

Vue.js - Remove specific component dynamically

I am trying to dynamically create/remove a Vue component. I have figured out how to dynamically add the component, but I am having some troubles with allowing the users to remove the specific component.
Consider below two Vue files:
TableControls.vue
<a v-on:click="addColumn">Add Column</a>
<script>
export default {
methods: {
addColumn: function () {
Event.$emit('column-was-added')
}
}
};
</script>
DocumentViewer.vue:
<div v-for="count in columns">
<VueDragResize :id="count">
<a #click="removeColumn(count)">Remove Column</a>
</VueDragResize>
</div>
<script>
import VueDragResize from 'vue-drag-resize';
export default {
components: {
VueDragResize
},
data() {
return {
columns: [1],
}
},
created() {
Event.$on("column-was-added", () => this.addColumn())
},
methods: {
addColumn: function () {
this.columns.push(this.columns.length + 1)
},
removeColumn: function (id) {
this.columns.splice(id, 1)
}
}
};
</script>
As you can see, whenever a user clicks on <a v-on:click="addColumn">Add Column</a>, it will submit an event, and the DocumentViewer.vue file will pick up it, firing the addColumn method. This will ultimately create a new <VueDragResize></VueDragResize> component.
This works great.
The problem is when I want to remove the component again. My removeColumn method simply removes an id from the columns array:
removeColumn: function (id) {
this.columns.splice(id, 1)
}
This results in that a column is in fact removed. However, consider below example. When user clicks on the remove icon for the first column, it will remove the 2nd column instead. (And when there is only one column present, it cannot be removed).
I believe this is due to the fact that I splice() the array, but I cannot see how else I can remove the component dynamically?
I see, Array on Vue does not re render when you modify them.
You need to use the
Vue.set(items, indexOfItem, newValue)
if you want to modify
and use
Vue.delete(target, indexOfObjectToDelete);
If you want to delete an item from an array
You may read the additional info here
https://v2.vuejs.org/v2/api/#Vue-delete
If you want to delete an item from array. Using this will cause the component to rerender.
In this case it will be intuitive to do this
removeColumn: function (id) {
Vue.delete(this.columns, id)
}
Note that id should be the index. Vue.delete ensures the re-render of the component.
EDIT, you must use the index, instead of the count here.
<div v-for="(count, index) in columns">
<VueDragResize :id="index">
<a #click="removeColumn(index)">Remove Column</a>
</VueDragResize>
</div>
I would recommend reshaping your data, each element should be an object with an id and whatever other properties you want. Not simply an id then you would need something like this:
removeColumn(id) {
const elToRemove = this.columns.findIndex(el => el.id === id)
let newArr = [elToRemove, ...this.columns]
this.columns = newArr
}
Also make another computed property for columns like this to make sure they change dynamically (when you add/remove):
computed: {
dynColumns(){ return this.columns}
}
I have same problem, and I found the solution of this problem. It is need to set #key with v-for. This is Built-in Special Attributes.
By default, if you do not set "#key", array index is set to#key. So if array length is 3, #key is 0,1,2. Vue identify eash v-for elements by key. If you remove second value of array, then array index is 0 and 1, because array length is 2. Then Vue understand that #key==2 element removed, So Vue remove 3rd component. So if you remove second value of array, if no #key, third component will be removed.
To avoid this, need to set #key to identify component like this:
let arr = [
{ id: 'a', ...},
{ id: 'b', ...},
{ id: 'c', ...}
];
<div v-for="obj in arr" :key="obj.id">
<someYourComponent>
...
</someYourComponent>
</div>

Kendo UI Grid - Add/Remove filters dynamically

I need to Create a Kendo ui grid. Since this has many filters, I need to have 4 regular filters and rest should be able to add dynamically according to users choice. Can someone provide assistance on this?
In order to filter by text box you can hook up a keyUp event in order to retrieve the value. You can then add this as a filter to the existing filter object.
$('#NameOfInput').keyup(function () {
var val = $('#NameOfInput').val();
var grid = $("#yourGrid").data("kendoGrid");
var filter = grid.dataSource.filter();
filter.filters.push({
field: "NameOfFieldYouWishToFilter",
operator: "eq",
value: val,
FilterName: "UniqueIdentifierForFilter"
});
grid.dataSource.filter(filter);
});
Using a drop down box, you can achieve the desired functionality by using the onChange event, get the value using $('#yourDropDown').val();.
The FilterName is optional incase you require additional logic to add/remove filters. i.e. you can use this to determine whether the filter already exists in the array and if so you can use splice to remove it.
EDIT
Using FilterName you can search to see if a filter already exists and remove it:
var filterIndex = filter.filters.map((e: any) => { return e.FilterName }).indexOf("UniqueIdentifierForFilter");
if (filterIndex > -1)
{
filter.filters.splice(filterIndex, 1);
}
For #lakshan, while this is largely correct, you will get an error if there are no filters at first. I found this answer when I encountered the undefined filter error. My full solution for adding a filter, either to an undefined filter set, or along with an existing one:
var grid = $("#ActivityGrid").data("kendoGrid");
var dataSource = grid.dataSource;
var gridFilter = dataSource.filter();
var upcomingFilter = {
field: "ActivityDate",
operator: "gte",
value: new Date(),
FilterName: "UpcomingOnly"
};
if ($("#UpcomingOnlyCheckbox")[0].checked) {
if (gridFilter == undefined) {
dataSource.filter(upcomingFilter);
}
else {
gridFilter.filters.push(upcomingFilter);
dataSource.filter(gridFilter);
}
}

Filling a react form from the Google Chrome console

I've been trying to write a bot to autocomplete some forms on a website, by copy+pasting the script into the Chrome console. (Nothing illegal.) However, the problem is that this website is written in React, which means that the controlled components they use for forms interfere with simple form.value changes. If I try to fill a form using something akin to form.value = answer, I still need to do a manual keypress on the form to have it work, which isn't suited for my needs of automation.
What I have tried so far:
- Filling in the form.value and firing a keypress/keydown/keyup afterwards.
- Filling in the form.value minus one letter and firing a keypress afterwards, corresponding to the letter that was missed out.
For some strange reason afterwards, as well, the enter key doesn't work to submit until I do the manual keypress.
Can anyone help me out? Thanks!
Better dirty way for filling form fields
I use this when doing dirty browser testing of forms
Adapted from Here
(()=>{
const inputTypes = [
window.HTMLInputElement,
window.HTMLSelectElement,
window.HTMLTextAreaElement
];
const triggerInputChange = (selector,value)=>{
const node = document.querySelector(selector);
// only process the change on elements we know have a value setter in their constructor
if (inputTypes.indexOf(node.__proto__.constructor) > -1) {
const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
let event = new Event('input',{
bubbles: true
});
if(node.__proto__.constructor === window.HTMLSelectElement){
event = new Event('change',{
bubbles: true
});
}
setValue.call(node, value);
node.dispatchEvent(event);
}
}
const formFields = [
['company', 'Shorts & Company'],
['first_name', 'McFirsty'],
['last_name', 'McLasty'],
['address1', '123 N. In The Woods'],
['city', 'Trunkborns'],
['state', 'WA'],
['zip', '55555']
];
formFields.forEach(field=>triggerInputChange(field[0], field[1]));
}
)()
Addressing the specific question
document.querySelector('input').focus();
document.execCommand('insertText', false, 'Some Text For the Input');
Or if you want to replace the text every time
document.querySelector('input').select();
document.execCommand('insertText', false, 'Some Text For the Input');
I have a chrome script dev tools -> sources -> scripts that I use when doing dirty tests of forms
(()=>{
const fillText = (selector, value) => {
document.querySelector(selector).select();
document.execCommand('insertText', false, value);
}
const formFields = [
['[data-ref-name="company"]', 'My Company'],
['[data-ref-name="first_name"]', 'Styks']
]
formFields.forEach(field => fillText(field[0], field[1]));
}
)()

How to set a default value for a dependent field based on the answer of the parent with Javascript?

I'm trying to have 4 sub fields, billing code, channel, subject code and name, autopopulate certain values based on the answer of their parent field, Event Type. In other words, if the answer to event type is "A", dropdown fields will appear for each sub field based on "A".
Let's say the first select-element of the document has the values
'Plant', 'Animal' and 'Ghost' and the next select-element should change
when the selected value of first ele changes, then this is how we do it:
var majorField = document.getElementsByTagName('select')[0]
var minorField = document.getElementsByTagName('select')[1]
// Define a dict, mapping each possible value to a function:
majorField.onChangeMap = {
Plant: function() { minorField.innerHTML = '<option>Cocoa</option><option>Cocos</option>' },
Animal: function() { minorField.innerHTML = '<option>Cat</option><option>Cow</option>' },
Ghost: function() { minorField.style.visibility = 'hidden' },
}
// When value changes, execute function of dict-map:
majorField.onchange = function(event) {
event.target.onChangeMap[event.target.value]()
}
// Make sure he minor-field has initially the correct state, by
// executing the mapped function for the major-field's current-value:
majorField.onChangeMap[majorField.value]()
Note that this is a very minimal illustration-example for the mapping only, as
one wouldn't set the options as an html-string, and hiding the minorField on
Ghost should be reversed on all other options with visibility = 'visible', here.

Categories