I'm trying to use Dagre-D3 to create a simple directed graph that adds nodes from a text input.
Unfortunately, the graph just refuses to draw.. and I think it's because my "Nodes.find({}).forEach(function (n) {..." doesn't appear to run.
Any ideas on what might be wrong? Am I using dagre-d3 or meteor wrongly..? Thank you for the help!
main.html:
<body>
<div id = "mapspace">
{{> map}}
</div>
<div id = "gennodespace">
{{>gennode}}
</div>
</body>
<template name="map">
<div>
<svg id="svg-canvas" width=650 height=680></svg>
</div>
</template>
<template name = "gennode">
<form class="node-entry">
<input type="text" name="nodedesc" placeholder="Enter a node title">
</form>
</template>
main.js -- client:
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Nodes = new Meteor.Collection("nodes");
Edges = new Meteor.Collection("edges");
Template.map.rendered = function(){
var g = new dagreD3.graphlib.Graph()
.setGraph({})
.setDefaultEdgeLabel(function () {
return {};
});
// Establish nodes
Nodes.find({}).forEach(function (n) {
g.setNode(n.nodeid, {
label: n.description
});
});
// Establish edges
Edges.find({}).fetch().forEach(function (e) {
g.setEdge(e.source, e.target, {
lineTension: .8,
lineInterpolate: "bundle"
});
});
var render = new dagreD3.render();
var svg = d3.select("svg"),
svgGroup = svg.append("g");
render(d3.select("svg g"), g);
}
Template.gennode.events = ({
'submit .node-entry': function(event,template){
event.preventDefault();
var desc = event.target.nodedesc.value;
var nodeid = Nodes.find().count();
// Update Nodes
Nodes.insert({
nodeid: nodeid,
description: desc
});
// Update edges
Edges.insert({
source: 0, //placeholder for now
target: nodeid
});
// Reset form
template.find(".node-entry").reset();
return false;
}
});
main.js -- server:
Nodes = new Meteor.Collection("nodes");
Edges = new Meteor.Collection("edges");
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// code to run on server at startup
});
Figured it out... The database simply hasn't loaded on rendered. so I added a find().observe to wrap around it.
Nodes.find().observe({
added: function (){
// Establish nodes
Nodes.find({}).forEach(function (n) {
g.setNode(n.nodeid, {
label: n.description
});
});
// Establish edges
Edges.find({}).fetch().forEach(function (e) {
g.setEdge(e.source, e.target, {
lineTension: .8,
lineInterpolate: "bundle"
});
});
var render = new dagreD3.render();
var svg = d3.select("svg"),
svgGroup = svg.append("g");
render(d3.select("svg g"), g);
}
});
Related
I have developed an application, and in this application I want to import file Excel to multiple table in my DB, and since i am a beginner in angularJS and .NET, I work with .net web api and angularjs, and I develop a function, it works when I import the data into a singe table, but the problem how to import the data into 3 table in DB !!! . and the 3 tables are linked to each other (in my exemple code there 2 table Candidature and Candidat). My question is: how to import Excel file to multiple table in DB and thank's. ( i work with asp.net web API and angularJS )
controller.cs:
/////
[Route("api/Candidature/SaveData")]
[HttpPost]
[ResponseType(typeof(Candidat))]
public IHttpActionResult SaveData(List<Candidature> Candidatures, List<Candidat> candidat)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
foreach (var data in Candidatures)
{
db.Candidature.Add(data);
}
db.SaveChanges();
foreach (var data in candidat)
{
db.Candidat.Add(data);
}
db.SaveChanges();
return StatusCode(HttpStatusCode.OK);
////
service.js:
SaveData: {
method: 'POST' ,
url: serviceBase + 'Candidature/SaveData',
isArray: true,
headers: {
'Content-Type' : 'application/json'
}
},
CandidatureCtrl.js :
$scope.Importation = function (data) {
$scope.SelectedFileForUpload = null;
$scope.UploadFile = function (files) {
$scope.$apply(function () { //I have used $scope.$apply because I will call this function from File input type control which is not supported 2 way binding
$scope.Message = "";
$scope.SelectedFileForUpload = files[0];
})
};
//Parse Excel Data
$scope.ParseExcelDataAndSave = function () {
var file = $scope.SelectedFileForUpload;
if (file) {
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
//XLSX from js-xlsx library , which I will add in page view page
var workbook = XLSX.read(e.target.result, { type: 'binary', cellDates:true, cellStyles:true });
var sheetName = workbook.SheetNames[0];
var excelData = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
if (excelData.length > 0) {
//Save data
Candidature.SaveData(excelData).then(function (data) {
if (data.status) {
$scope.Message = excelData.length + " record inserted";
}
else {
$scope.Message = "Failed";
}
}, function (error) {
$scope.Message = "Error";
});
// Candidature.SaveDatacandidature(excelData).then(function (data) {
// if (data.status) {
// $scope.Message = excelData.length + " record inserted";
// }
// else {
// $scope.Message = "Failed";
// }
// }, function (error) {
// $scope.Message = "Error";
// });
// $scope.SaveData(excelData);
}
else {
$scope.Message = "No data found";
}
}
reader.onerror = function (ex) {
console.log(ex);
}
reader.readAsBinaryString(file);
}
};
var dialogOpts = {
backdrop: 'static',
keyboard: false,
scope: $scope,
size: 'lg',
templateUrl: 'views/candidature/Importation.html',
controller: ['$scope', '$uibModalInstance','$sce',
function ($scope, $uibModalInstance, $sce) {
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}
]
};
$uibModal.open(dialogOpts);
};
Importation.html :
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.8.1/xlsx.full.min.js"></script>
<script src="http://oss.sheetjs.com/js-xlsx/jszip.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
<!--<script src="app/views/candidature/candidatureCtrl.js"></script>-->
</head>
<body ng-app="agu">
<div ng-controller="candidatureCtrl" class="container" style="margin-top:50px;">
<div class="form-inline">
<input type="file" name="file" class="form-control"
onchange="angular.element(this).scope().UploadFile(this.files)"/>
<input type="button" value="Import" class="btn btn-success" ng-disabled="!SelectedFileForUpload"
ng-click="ParseExcelDataAndSave()" />
<br/>
<span style="color:red">
{{Message}}
</span>
</div>
</div>
</body>
</html>
In your web api, you are expecting two arrays for Candidatures and candidat.
But from your controller you are passing only one array of data excelData.
So when it comes to api it doesn't execute this code,
foreach (var data in candidat)
{
db.Candidat.Add(data);
}
Because candidat is either null or undefined. So it can't go through the loop which the below code is never executed.
db.Candidat.Add(data);
I am learning React and ASP.net( not core).
I try to create a small project for practice.
I have a number of checkboxes and a submit button.
The idea is when I click on the button I want to create a Jason that will contain a list of the checkboxes text and their states (check \uncheck).
That Jason I want to send to the server.
I mange to create the components and the relevant events.
But fail to create the following
1) Get the state of the checkbox from the submit component and create a Jason out of it.
2) Send that Jason to the server side (ASP.net).
I try to flow the tutorial but didn’t get it, I tried to find solution on the internet but with no lack.
If someone can show me how to do it – or give me some direction that will help
Thanks
the jsk:
var data = [
{ Id: 1, ActionItem: "Action1" },
{ Id: 2, ActionItem: "Action2" },
{ Id: 3, ActionItem: "Action3" }
];
var Checkbox = React.createClass({
getInitialState: function() {
return {
isChecked: false
};
},
toggleChange: function() {
this.setState({
isChecked: !this.state.isChecked
},
function() {
console.log(this.state);
}.bind(this));
},
render: function() {
return (
<label>
<input type="checkbox"
checked={this.state.isChecked}
onChange={this.toggleChange} />{this.props.chkboxlabel}<br />
</label>
);
}
});
var ActionBox = React.createClass({
render: function() {
return (
<div className="actionBox">
<h1>Select Action </h1>
<CheckboxList data={this.props.data} />
<SubmitForm/>
</div>
);
}
});
var CheckboxList = React.createClass({
render: function() {
var actionNodes = this.props.data.map(function(action) {
return (
<Checkbox chkboxlabel={action.ActionItem} key={action.id}>
{action.ActionItem}
</Checkbox>
);
});
return (
<form className="actionList">{actionNodes}
</form>
);
}
});
var SubmitForm = React.createClass({
handleSubmit: function(e) {
//??
},
render: function() {
return (
<form className="actionForm" onSubmit={this.handleSubmit}>
<input type="submit" value="Run" />
</form>
);
}
});
ReactDOM.render(
<ActionBox data={data} />,
document.getElementById('content')
);
I found a solution that works I don’t think it’s the best way but maybe it will help other.
I loop thought the element to get the value- I believe there is a better way of doing it with react.
var data = [
{ Label: "Action 1", IsChecked: false, Name: "a1" },
{ Label: "Action 2", IsChecked: false, Name: "a2" },
{ Label: "Action 3", IsChecked: false, Name: "a3" }
];
handleCommentSubmit: function () {
var data = new FormData();
var inputs = document.getElementsByName('mycheckbox');
for (var i = 0; i < inputs.length; i++) {
var name = inputs.item(i).value;
var ischecked = inputs.item(i).checked;
data.append(name, ischecked);
}
var xhr = new XMLHttpRequest();
xhr.open('post', this.props.submitUrl, true);
xhr.send(data);
},
i am trying to show a dynamic data on the ionic slide box with ng-repeat. i am using services to get the data from my sqlite DB but i get nothing. i don't know what to do after 3 days with this issue, here is my code:
template.html
<ion-view>
<ion-nav-title>{{simple.title}}</ion-nav-title>
<ion-content>
<div class="row">
<div class="col col-33 col-offset-33">
<h1 style="font-size: 72px !important;">{{simple.content}}</h1>
</div>
</div>
<div class="row">
<div class="col">
<ion-slide-box show-pager="false" does-continue="true">
<ion-slide ng-repeat="senci in sencillo">
<div class="box">
<h1 style="font-size: 52px !important;">{{senci.sound_title}}</h1>
</div>
</ion-slide>
</ion-slide-box>
</div>
</div>
</ion-content>
</ion-view>
my service.js
angular.module('starter.services', [])
.factory('DBA', function($cordovaSQLite, $q, $ionicPlatform) {
var self = this;
// Handle query's and potential errors
self.query = function (query, parameters) {
parameters = parameters || [];
var q = $q.defer();
$ionicPlatform.ready(function () {
$cordovaSQLite.execute(db, query, parameters)
.then(function (result) {
q.resolve(result);
}, function (error) {
console.warn('I found an error');
console.warn(error);
console.log(error.code + ' / ' + error.message);
q.reject(error);
});
});
return q.promise;
};
// Proces a result set
self.getAll = function(result) {
var output = [];
for (var i = 0; i < result.rows.length; i++) {
output.push(result.rows.item(i));
}
return output;
};
// Proces a single result
self.getById = function(result) {
var output = null;
output = angular.copy(result.rows.item(0));
return output;
};
return self;
})
.factory('Sounds', function(DBA) {
var self = this;
self.getSimple = function(simpleId) {
var parameters = [simpleId];
return DBA.query("SELECT * FROM letters WHERE Id = (?)", parameters)
.then(function(result) {
return DBA.getById(result);
});
};
self.getSimpleArr = function(Id) {
var parameters = [Id];
return DBA.query("SELECT * FROM words WHERE letter_id = (?)", parameters)
.then(function(result) {
return DBA.getById(result);
});
};
return self;
});
controller.js
.controller('SoundsSimpleCtrl', function($scope, Sounds, $stateParams, $ionicSlideBoxDelegate) {
$scope.sencillo = [];
$scope.getSimple = function($stateParams) {
Sounds.getSimple($stateParams.simpleId).then(function(single){
$scope.simple = single;
$scope.getArrSimple($scope);
});
};
$scope.getArrSimple = function($scope){
Sounds.getSimpleArr($scope.simple.Id).then(function(detalle){
$scope.sencillo = detalle;
$ionicSlideBoxDelegate.update();
});
};
$scope.getSimple($stateParams);
});
i hope you guys can help me, regards.
Hi people i resolved my problem, i had a bad SQl Request, im so sorry for be annoying, i just changed to use my service function from getById() to getAll() (these functions are being taken from DBA Factory) like this:
self.getSimpleArr = function(value) {
var parameters = [value];
return DBA.query("SELECT * FROM words WHERE letter_id = (?)",parameters)
.then(function(result){
return DBA.getAll(result);
});
};
the getById() function was returning only the first row of the request, absolutely MY BAD. Regards
I try to add a button to a menu with Ractive.js and on click on this button sidebar should be opened.
js:
var ractive_setup = Ractive.extend({
data: {
data: {
sidebar: false,
}
},
onrender: function ( options ) {
this.on({
sidebar: function () {
this.toggle('sidebar');
console.log ( this.get('sidebar') );
}
});
}
});
var ractive_sidebar_open = new ractive_setup({
template: '#sidebar-open',
el: '[data-ractive=sidebar-open]'
});
var ractive_sidebar = new ractive_setup({
template: '#sidebar',
el: '[data-ractive=sidebar]'
});
html:
<nav data-ractive="sidebar-open"></nav>
<script id="sidebar-open" type="text/ractive">
<button class="open" on-click="sidebar">open sidebar</button>
</script>
<aside data-ractive="sidebar"></aside>
<script id="sidebar" type="text/ractive">
{{ #if sidebar }}
<button on-click="sidebar">close sidebar</button>
<div class="sidebar-content">sidebar content</div>
{{ /if sidebar }}
</script>
On button.open click, data changes only for one instance of ractive_setup—for the first one.
How to modify Ractive data globally, for both ractive_setup instances?
You need to declare your data-object outside, and pass it into both instances of your ractive_setup. With magic mode (docs) option on, both instances will then re-render when your data is modified.
Like this:
var ractive_setup = Ractive.extend({
magic: true, //Magic mode to ensure re-render when data is modified
onrender: function ( options ) {
this.on({
sidebar: function () {
this.toggle('sidebar');
console.log ( this.get('sidebar') );
}
});
}
});
//declaring your data-object outisde, so you can pass it into both instances
var dataObj = {
sidebar: false
};
var ractive_sidebar_open = new ractive_setup({
template: '#sidebar-open',
el: '[data-ractive=sidebar-open]',
data: dataObj //passing in data to the first instance
});
var ractive_sidebar = new ractive_setup({
template: '#sidebar',
el: '[data-ractive=sidebar]',
data: dataObj //passing in data to the second instance
});
I've created a working fiddle of your example here: http://jsfiddle.net/08huhfar/3/
I have an app that when you select an industry from a drop down list a collection is updated where the attribute equals the selected industry.
JavaScript:
Template.selector.events({
'click div.select-block ul.dropdown-menu li': function(e) {
var selectedIndex = $(e.currentTarget).attr("rel");
var val = $('select#industryPicker option:eq(' + selectedIndex + ')').attr('value');
var oldVal = Session.get('currentIndustryOnet');
if(val != oldVal) {
Session.set('jobsLoaded', false);
Session.set('currentIndustryOnet', val);
Meteor.call('countByOnet', val, function(error, results){
if(results > 0) {
Session.set('jobsLoaded', true);
} else {
getJobsByIndustry(val);
}
});
}
}
});
var getJobsByIndustry = function(onet) {
if(typeof(onet) === "undefined")
alert("Must include an Onet code");
var params = "onet=" + onet + "&cn=100&rs=1&re=500";
return getJobs(params, onet);
}
var getJobs = function(params, onet) {
Meteor.call('retrieveJobs', params, function(error, results){
$('job', results.content).each(function(){
var jvid = $(this).find('jvid').text();
var job = Jobs.findOne({jvid: jvid});
if(!job) {
options = {}
options.title = $(this).find('title').text();
options.company = $(this).find('company').text();
options.address = $(this).find('location').text();
options.jvid = jvid;
options.onet = onet;
options.url = $(this).find('url').text();
options.dateacquired = $(this).find('dateacquired').text();
var id = createJob(options);
console.log("Job Created: " + id);
}
});
Session.set('jobsLoaded', true);
});
}
Template.list.events({
'click div.select-block ul.dropdown-menu li': function(e){
var selectedIndex = $(e.currentTarget).attr("rel");
var val = $('select#perPage option:eq(' + selectedIndex + ')').attr('value');
var oldVal = Session.get('perPage');
if(val != oldVal) {
Session.set('perPage', val);
Pagination.perPage(val);
}
}
});
Template.list.jobs = function() {
var jobs;
if(Session.get('currentIndustryOnet')) {
jobs = Jobs.find({onet: Session.get('currentIndustryOnet')}).fetch();
var addresses = _.chain(jobs)
.countBy('address')
.pairs()
.sortBy(function(j) {return -j[1];})
.map(function(j) {return j[0];})
.first(100)
.value();
gmaps.clearMap();
$.each(_.uniq(addresses), function(k, v){
var addr = v.split(', ');
Meteor.call('getCity', addr[0].toUpperCase(), addr[1], function(error, city){
if(city) {
var opts = {};
opts.lng = city.loc[1];
opts.lat = city.loc[0];
opts.population = city.pop;
gmaps.addMarker(opts);
}
});
})
return Pagination.collection(jobs);
} else {
jobs = Jobs.find()
Session.set('jobCount', jobs.count());
return Pagination.collection(jobs.fetch());
}
}
In Template.list.jobs if you console.log(addresses), it is called 4 different times. The browser console looks like this:
(2) 100
(2) 100
Any reason why this would fire multiple times?
As #musically_ut said it might be because of your session data.
Basically you must make the difference between reactive datasources and non reactive datasources.
Non reactive are standard javascript, nothing fancy.
The reactive ones however are monitored by Meteor and when one is updated (insert, update, delete, you name it), Meteor is going to execute again all parts which uses this datasource. Default reactive datasources are: collections and sessions. You can also create yours.
So when you update your session attribute, it is going to execute again all helper's methods which are using this datasource.
About the rendering, pages were rendered again in Meteor < 0.8, now with Blaze it is not the case anymore.
Here is a quick example for a better understanding:
The template first
<head>
<title>test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>{{getSession}}</h1>
<h1>{{getNonReactiveSession}}</h1>
<h1>{{getCollection}}</h1>
<input type="button" name="session" value="Session" />
<input type="button" name="collection" value="Collection" />
</template>
And the client code
if (Meteor.isClient) {
CollectionWhatever = new Meteor.Collection;
Template.hello.events({
'click input[name="session"]': function () {
Session.set('date', new Date());
},
'click input[name="collection"]': function () {
CollectionWhatever.insert({});
}
});
Template.hello.getSession = function () {
console.log('getSession');
return Session.get('date');
};
Template.hello.getNonReactiveSession = function () {
console.log('getNonReactiveSession');
var sessionVal = null;
new Deps.nonreactive(function () {
sessionVal = Session.get('date');
});
return sessionVal;
};
Template.hello.getCollection = function () {
console.log('getCollection');
return CollectionWhatever.find().count();
};
Template.hello.rendered = function () {
console.log('rendered');
}
}
If you click on a button it is going to update a datasource and the helper method which is using this datasource will be executed again.
Except for the non reactive session, with Deps.nonreactive you can make Meteor ignore the updates.
Do not hesitate to add logs to your app!
You can read:
Reactivity
Dependencies