Save and restore graph with cytoscape - cytoscape.js

I would like to have a button to save and one to restore my graph manipulated. I have tested this code but it doesn't work. Can you help me please?
I would like to save and restore in differents moments of the graph.
$("#clickMe").click(function () {
var jsonBlob = new Blob([ JSON.stringify( cy.json() ) ], { type: 'application/javascript;charset=utf-8' });
saveAs( jsonBlob, 'graph.json' );
});

Related

How to check a Specific Properties of a Shape in BPMNjs and update them dynamically

In the bpmnjs I wanted to access or change the right panel properties dynamically on Selection of a Shape or shape type
Below snippet I am trying to use to updated the properties:
updateProperties(e) {
try {
let canvas = this.bpmnModeler.get("canvas");
let rootElement = canvas.getRootElement();
var modeling = this.bpmnModeler.get("modeling");
this.$nextTick(() => {
modeling.updateProperties(e.element, {
id: "ABC",
name: "ABCD",
isExecutable: true,
});
});
} catch (e) {}
}
Please help me to find how to get a specific property of selected shape and update the property in the right panel. Or I have to know how the right panel properties of the bpmnjs is actually configured and loading . I am very much new for the BPMNjs. appreciate for the reply.
I have tried bpmnjs above vuejs framework. I wanted to know how the properties on the right panel of each Task or Shape on the COMUNDA bpmnjs

Vuejs - update array of an object which is in an array

I'm developing a helpdesk tool in which I have a kanban view.
I previously used nested serializers in my backend and I managed to have everything working with a single query but it's not scalable (and it was ugly) so I switched to another schema :
I query my helpdesk team ('test' in the screenshot)
I query the stages of that team ('new', 'in progress')
I query tickets for each stage in stages
So when I mount my component, I do the following :
async mounted () {
if (this.helpdeskTeamId) {
await this.getTeam(this.helpdeskTeamId)
if (this.team) {
await this.getTeamStages(this.helpdeskTeamId)
if (this.stages) {
for (let stage of this.stages) {
await this.getStageTickets(stage)
}
}
}
}
},
where getTeam, getTeamStages and getStageTickets are :
async getTeam (teamId) {
this.team = await HelpdeskTeamService.getTeam(teamId)
},
async getTeamStages (teamId) {
this.stages = await HelpdeskTeamService.getTeamStages(teamId)
for (let stage of this.stages) {
this.$set(stage, 'tickets', [])
}
},
async getStageTickets (stage) {
const tickets = await HelpdeskTeamService.getTeamStageTickets(this.helpdeskTeamId, stage.id)
// tried many things here below but nothing worked.
// stage.tickets = stage.tickets.splice(0, 0, tickets)
// Even if I try to only put one :
// this.$set(this.stages[this.stages.indexOf(stage)].tickets, 0, tickets[0])
// I see it in the data but It doesn't appear in the view...
// Even replacing the whole stage with its tickets :
// stage.tickets = tickets
// this.stages.splice(this.stages.indexOf(stage), 1, stage)
},
In getTeamStages I add an attribute 'tickets' to every stage to an empty list. The problem is when I query all the tickets for every stage. I know how to insert a single object in an array with splice or how to delete one object from an array but I don't know how to assign a whole array to an attribute of an object that is in an array while triggering the Vue reactivity. Here I'd like to put all the tickets (which is a list), to stage.tickets.
Is it possible to achieve this ?
If not, what is the correct design to achieve something similar ?
Thanks in advance !
EDIT:
It turns out that there was an error generated by the template part. I didn't think it was the root cause since a part of the view was rendered. I thought that it would have prevent the whole view from being rendered if it was the case. But finally, in my template I had a part doing stage.tickets.length which was working when using a single query to populate my view. When making my API more granular and querying tickets independently from stages, there is a moment when stage has no tickets attribute until I set it manually with this.$set(stage, 'tickets', []). Because of that, the template stops rendering and raises an issue. But the ways of updating my stage.tickets would have worked without that template issue.
I could update the stages reactively. Here is my full code; I used the push method of an array object and it works:
<template>
<div>
<li v-for="item in stages" :key="item.stageId">
{{ item }}
</li>
</div>
</template>
<script>
export default {
data() {
return {
stages: [],
};
},
methods: {
async getTeamStages() {
this.stages = [{ stageId: 1 }, { stageId: 2 }];
for (let stage of this.stages) {
this.$set(stage, "tickets", []);
}
for (let stage of this.stages) {
await this.getStageTickets(stage);
}
},
async getStageTickets(stage) {
const tickets = ["a", "b", "c"];
for (let ticket of tickets) {
this.stages[this.stages.indexOf(stage)].tickets.push(ticket);
}
},
},
mounted() {
this.getTeamStages();
},
};
</script>
It should be noted that I used the concat method of an array object and also works:
this.stages[this.stages.indexOf(stage)].tickets = this.stages[this.stages.indexOf(stage)].tickets.concat(tickets);
I tried your approaches some of them work correctly:
NOT WORKED
this.$set(this.stages[this.stages.indexOf(stage)].tickets, tickets)
WORKED
this.$set(this.stages[this.stages.indexOf(stage)].tickets, 0, tickets[0]);
WORKED
stage.tickets = tickets
this.stages.splice(this.stages.indexOf(stage), 1, stage)
I'm sure it is XY problem..
A possible solution would be to watch the selected team and load the values from there. You seem to be loading everything from the mounted() hook, and I suspect this won't actually load all the content on demand as you'd expect.
I managed to make it work here without needing to resort to $set magic, just the pure old traditional vue magic. Vue will notice the properties of new objects and automatically make then reactive, so if you assign to them later, everything will respond accordingly.
My setup was something like this (showing just the relevant parts) -- typing from memory here, beware of typos:
data(){
teams: [],
teamId: null,
team: null
},
watch:{
teamId(v){
this.refreshTeam(v)
}
},
methods: {
async refreshTeam(id){
let team = await fetchTeam(id)
if(!team) return
//here, vue will auomaticlly make this.team.stages reactive
this.team = {stages:[], ...team}
let stages = await fetchStages(team.id)
if(!stages) return
//since this.team.stages is reactive, vue will update reactivelly
//turning the {tickets} property of each stage reactive also
this.team.stages = stages.map(v => ({tickets:[], ...v}))
for(let stage of this.team.stages){
let tickets = await fetchTickets(stage.id)
if(!tickets) continue
//since tickets is reactive, vue will update it accordingly
stage.tickets = tickets
}
}
},
async mounted(){
this.teams = fetchTeams()
}
Notice that my 'fetchXXX' methods would just return the data retrieved from the server, without trying to actually set the component data
Edit: typos

How to use the sharp library to resize a Parse file img?

I have a Parse Cloud afterSave trigger from where I can access the obj and inside the obj a field that has a store parse file img.
I want to use sharp to resize it and save it in another field but I'm struggling and getting an error when I use sharp. Here is a summary of the code I already have inside the cloud trigger:
let file = obj.get("photo");
sharp(file)
.resize(250, 250)
.then((data) => {
console.log("img-----", data);
})
.catch((err) => {
console.log("--Error--", err);
});
After some research, I managed to figure out how to create Parse Cloud afterSave trigger which resizes and then saves the img, I couldn't find much information on it so ill post my solution so others can use it if it's helpful.
Parse.Cloud.afterSave("Landmarks", async (req) => {
const obj = req.object;
const objOriginal = req.original;
const file = obj.get("photo");
const condition = file && !file.equals(objOriginal.get("photo"));
if (condition) {
Parse.Cloud.httpRequest({ url: file.url() })
.then((res) => {
sharp(res.buffer)
.resize(250, 250, {
fit: "fill",
})
.toBuffer()
.then(async (dataBuffer) => {
const data = { base64: dataBuffer.toString("base64") };
const parseFile = new Parse.File(
"photo_thumbnail",
data
);
await parseFile.save();
await obj.save({ photo_thumb: parseFile });
})
.catch((err) => {
console.log("--Sharp-Error--", err);
});
})
.catch((err) => {
console.log("--HTTP-Request-Error--", err);
});
} else {
console.log("--Photo was deleted or did not change--");
}
});
So to break this down a bit, what i did first was get the obj and the objOriginal so i can compare them and check for a change in a specific field. This condition is necessery since in my case i wanted to save the resized img in parse which would cause an infinite loop otherwise.
After that i did a Parse.Cloud.httpRequest({ url: file.url()}).then() which is the way i found to get the buffer from the photo. The buffer is stored inside res.buffer and we need it for sharp.
Next i use sharp(res.buffer) since sharp also accepts buffers and resize it to the desired dimensions (i used the fit config for it). Then we turn the resulted img into another buffer using .toBuffer(). Furthermore, i use a .then().catch() blocks and if sharp is succesful i turned the outputed buffer into a base64 and passed it in Parse.File(), note that the specific syntax { base64: 'insert buffer here' } is important.
And finally i just save the file and the obj. Is this the best way to do it, absolytely not, but its the one i found that works. Another possible solution is instead of using buffers and base64 is to create a temporary dir which you save the images there, use them and then delete the directory. I tried this as well but had issues making it work.

Getting All series on a given axis must be of the same data type, with Vue + Google Spreedsheet

I used the exact example and even created the same spreedsheet as in the vue example https://codesandbox.io/embed/yqxxkp32mj?module=%2Fsrc%2FApp.vue here, if i copy the spreedsheet from their example it works fine but when I add my own spreedsheet (which is practically a copy of what they have it gives me this error)
Getting All series on a given axis must be of the same data type
methods: {
onChartReady(chart, google) {
const query = new google.visualization.Query(
"https://docs.google.com/spreadsheets/d/1GpwJaxUPYv9MV2uAvW4qkKanFlwrzTHWS55vzK3J9VE/edit?usp=sharing"
);
query.send(response => {
const options = {
title: "Creating a Chart from a Separate Spreadsheet"
};
const data = response.getDataTable();
chart.draw(data, options);
});
}
}
};
This is my google sheet
https://docs.google.com/spreadsheets/d/1GpwJaxUPYv9MV2uAvW4qkKanFlwrzTHWS55vzK3J9VE/edit?usp=sharing
I am using vue-google-charts
Thanks

Chartist.js and events

I am trying to add click events on the graphs that I am rendering. From chart.click to chart.on('click', function (e){ }).
What I am trying to do is allow the user to select points on the graph and for me to now what selections the user made. Is that at all possible using chartist.js?
I read through the documentation: CHARTIST.JS
My code:
if (item.GraphType.Description == "Line") {
var chart = new Chartist.Line(
container[0],
{
labels: d.Labels,
series: d.SeriesData
},
{
axisY: {
offset: 60
}
}
);
chart.click(function (e) {
console.log(e);
});
}
It is entirely possible, yes. Chartist renders SVG nodes to the page, so using a library like jQuery you can easily find all nodes that you want and attach events to them. You can be as specific or broad in the nodes you're looking for to only attach events to very specific nodes or elements on the chart.
For completeness sake, here is a short example of how to attach events that log the value of a data point when clicked upon to the console using jQuery:
$('.ct-chart-line .ct-point').click(function () {
var val = $(this).attr("ct:value");
console.log(val);
});
You should, however, make sure that the events attach only when the chart is created or drawn if you want to ensure the data points are on the page, which can be triggered by the "created" or "draw" events:
var chart = new Chartist.Line(...);
// attach an event handler to the "created" event of the chart:
chart.on("created", function () {
// attach the necessary events to the nodes:
$('.ct-chart-line .ct-point').click(function () {
var val = $(this).attr("ct:value");
console.log(val);
});
});