How to hide the row if it has no children in kendo-treeview in angular 8? - angular8

How to hide the parent row if it has no children to display.text i can hide it but Empty row still present in the DOM element. Can anyone help to get it resolved? angular 8 + kendo treeview.
<kendo-treeview kendoTreeViewExpandable [nodes]="data" [children]="children" [hasChildren]="hasChildren" textField="text"
>
<ng-template kendoTreeViewNodeTemplate let-dataItem>
<span>{{dataitem.title}}</span>
</ng-template>
</kendo-treeview>

Backend side, I have a parent row looking like this :
{
id: "session_client_1669"
id_linked: "1669"
id_regroup: null
libelle: "UN CLIENT PRESQUE PARFAIT"
menu: "client"
type: "parent"
}
Then, a child row taking for id_regroup its parent's id :
{
id: "new_DOC_RAPPORT_SESSION_1669"
idLinked: "1669"
id_regroup: "session_client_1669"
libelle: "Document à générer - Rapport de fin de formation"
menu: "client"
type: "generated"
}
Finally I use a function to recursively filter out elements of parent type with no children, thus getting rid of it in the DOM:
function removeEmptyParent(listDoc) {
try {
return new Promise(async resolve => {
let listDocTemp = (listDoc || []).filter(
item =>
(listDoc.findIndex(e => e.id_regroup === item.id) !== -1 && item.type === 'parent') || item.type !== 'parent',
);
const index = (listDocTemp || []).findIndex(
item => listDocTemp.findIndex(e => e.id_regroup === item.id) === -1 && item.type === 'parent',
);
if (index > -1) {
listDocTemp = await removeEmptyParent(listDocTemp);
}
resolve(listDocTemp);
});
} catch (error) {
console.log(`error`, error); // eslint-disable-line
}
}

Related

Window object - JSON.stringify replacer

I am attempting to persist a Window object in Local Storage in my Vue project via JSON.stringify with a replacer.
Since Window has a circular structure, I can't use JSON.stringify by itself.
So I've researched the usage of the replacer.
I came across this implementation:
const getCircularReplacer = () => {
console.log("replacer called...");
const seen = new WeakSet();
return (key, value) => {
if (typeof value === window && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
The following replacer implementations didn't work for me....meaning that the getCircularReplacer() method doesn't get called.
const winClean = JSON.stringify(win, getCircularReplacer());
but wrapping the replacer in a function seems to do the trick and it calls the getCircularReplacer method
const winClean = JSON.stringify(win, function () { getCircularReplacer(); });
Another issue is that the winClean returns 'undefined'.
I tested the getCircularReplacer with the following modifications:
if (typeof value === 'object' && value !== null)
and
if (typeof value === window && value !== null) {
and
if (typeof value === Window && value !== null) {
to no avail.
Here's the full code in HellowWorld.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<div>
Google |
Bing |
Yahoo |
BBC
</div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
methods: {
openWindow(url, name) {
console.log(url + " | " + name);
const win = window.open(url, name);
console.log("window name: " + win.name);
console.log(win);
const getCircularReplacer = () => {
console.log("replacer called...");
const seen = new WeakSet();
return (key, value) => {
if (typeof value === window && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
const winClean = JSON.stringify(win, function () {
getCircularReplacer();
});
console.log(winClean);
},
},
};
</script>
Could anybody please clarify why my winClean is undefined and how to correctly stringify my window object to avoid the "Converting circular structure to JSON" error.
Thank you!

Vue react to Setting array of an Object to another array and seeing reactive changes

I have a v-data-table on vue, which gets data and dynamically adds and deltes rows based on the incoming object of arrays, Vue is reactive to adding and deleting but doesn't seem to react to array replace.
My function to add, delete and replace is the setup the following way:
function update_helper(update_obj, dataObject, colObject) {
update_obj.Data.forEach((item) => {
if (typeof item.RowData !== 'undefined'){
let temp_list = updateRow(item, colObject);
temp_list.forEach((row_obj) => {
var found = dataObject.find(Element => Element.RowID === row_obj.RowID);
if (typeof found !== 'undefined'){
//Replace
var found = dataObject.findIndex(Element => Element.RowID === item.RowID);
//console.log(row_obj);
//console.log(dataObject[found]);
dataObject[found] = row_obj;
}
else{
// Add
dataObject.push(row_obj);
}
});
}
else if (typeof item.RowData === 'undefined') {
// Delete
var found = dataObject.findIndex(Element => Element.RowID === item.RowID);
dataObject = dataObject.splice(found, 1);
}
});
}
The function keeps track of the row Id . My replace function dataObject[found] = rowObj works but isn't reactive, i.e the change can only be seen when I switch tabs or refresh the page.
How do I workaround this.
Instead of passing it as argument, you could better have it as a data variable like
data() {
return {
dataObject: [],
}
}
and then define your function inside the methods section like
methods: {
update_helper(update_obj, colObject) {
update_obj.Data.forEach((item) => {
if (typeof item.RowData !== 'undefined'){
let temp_list = updateRow(item, colObject);
temp_list.forEach((row_obj) => {
var found = dataObject.findIndex(Element => Element.RowID === row_obj.RowID);
if (found !== -1){
this.dataObject[found] = row_obj;
}
else{
// Add
this.dataObject.push(row_obj);
}
});
}
else if (typeof item.RowData === 'undefined') {
// Delete
var found = this.dataObject.findIndex(Element => Element.RowID === item.RowID);
dataObject = this.dataObject.splice(found, 1);
}
});
}
}
If possible you can declare the colObject also in the data() section
Note: If you observe the above function body, I would have accessed the dataObject using this operator.

How to retrieve data when component is loaded

I've got a list of projects, and I want to display the name of the clicked project in a modal on the next page, when I click on one of them, it loads without problem and displays the association name in my modal.
But the problem comes when I reload this project page, it throws an error "Cannot read property 'asso_id' of undefined""
I get the project by it's id with a getter to retrieve the asso_id in the project object, then use this asso_id with my association by id getter to get the related association.
I think it's because my component doesn't retrieve the AssociationTitle soon enough.
Here is my code
computed: {
"getProjectById",
"getAssociationById"
]),
project() {
const projectId = this.$route.params.id;
return this.getProjectById(projectId);
},
associationTitle() {
const project = this.project;
const association = this.getAssociationById(project.asso_id);
return association.title;
}
}
getters: {
getProjects: state => {
return state.projects = [...ProjectData.ProjectData];
},
getProjectById: state => id => {
return state.projects.find(project => project._id === id);
},
getAssociations: state => {
return state.associations = [...AssociationData.AssociationData];
},
getAssociationById: state => id => {
state.associations = [...AssociationData.AssociationData];
return state.associations.find(association => association._id === id);
}
}
<v-card-title class="headline grey lighten-2" primary-title> {{ associationTitle }} </v-card-title>
I think you're guessing it right. You can just check if you have the project e.g.
associationTitle() {
const project = this.project;
const title = project ? this.getAssociationById(project.asso_id).title : '';
// or typeof project !== 'undefined' or something similar
return title;
}

Vue tags input custom validation

I'm using vue-tags-input component. In its docs we can find validation. I'm trying to create validation so valid input must have:
min 3 signs
two numbers
comma between numbers
this is what I have:
validation: [{
classes: 'min-length',
rule: tag => tag.text.length < 3,
},{
classes: 'min-length',
rule: ({ text }) => {
const comma = text.indexOf(',') === -1;
if(comma) {
const arr = text.split(',')
if(arr[0] && arr[1]) {
if(arr[0].typeof === 'number' && arr[1].typeof === 'number') {
return true;
}
}
}
return false;
}
}]
So I'm spliting string to array by ,. In result I should have array with two elements. Then I check if both elemenets are numbers. How ever this not work properly because it treat 111 as valid but it shoudn't.
I've created demo on codesanbox.
To check if comma exists you have to check if indexOf comma not equals -1.
const comma = text.indexOf(",") !== -1;
You have to convert the string to number using Number(string).
if (typeof Number(arr[0]) === "number") {..
You have to return false if validation succeeds and true if there is an error,
you are doing the opposite.
The complete code will be:
{
classes: "custom",
rule: ({ text }) => {
const comma = text.indexOf(",") !== -1;
if (comma) {
const arr = text.split(",");
if (arr[0] && arr[1]) {
if (typeof Number(arr[0]) === "number" && typeof Number(arr[1]) === "number") {
return false;
}
}
}
return true;
}
}
A shorter regex rule will be:
{
classes: "custom",
rule: ({ text }) => {
return !text.match(/^\d+,\d+$/);
}
}

React native picker, get selected option text

I am using my react native picker with options like so:
<Picker
selectedValue={student}
label="Student"
style={styles.picker}
onChange={this.onStudentChange}
options={
categories
.find(category => {
return category.id == year;
})
.options.find(category => {
return category.id == group;
})
.options
}
/>
I then dispatch and action in my click handler, where e is the id of the student:
onStudentChange(e) {
if (e !== "") {
this.props.setStudent(e);
}
}
How can I get the selected option text as well as the value?
You can find the selected option by id from the list of options. After that you can get a text from the selected option. In the example below I moved the code that gets the options into a separate function which I also use in onStudentChange:
getOptions(year, group) {
return categories
.find(category => {
return category.id == year;
})
.options.find(category => {
return category.id == group;
})
.options
}
onStudentChange(e) {
if (e !== "") {
// Find option by id and get text.
const text = this.getOptions().find(entry => entry.id === e).text;
this.props.setStudent(e);
}
}
render() {
...
<Picker
selectedValue={student}
label="Student"
style={styles.picker}
onChange={this.onStudentChange}
options={this.getOptions(year, group)}
/>
}