Binding CSV Data to SVG Drawing with AngularJS - angularjs

I have started a scaled down, proof-of-concept floor plan application for my employer that is using an SVG floor plan drawing generated by Adobe Illustrator CC, HTML, CSS and AngularJS.
The idea is to have a floor plan that contains static cubicle/office numbers and to marry that floor plan with a CSV file with personnel data that updates periodically and make the information available on our intranet.
Although I am new at AngularJS/SVG, I was following an example for a map of the US and have made some pretty good progress so far, however am now at a point where I need to map the CSV data (personnel) to the SVG (cubicles) and am not sure how to proceed.
From the example in the link above, the author appears to have injected an "sg-click" directive into the SVG DOM with Angular.$compile that fires a JS Alert() of the ID of the SVG DOM when clicked:
link: function (scope, element, attrs) {
scope.elementId = element.attr("id");
scope.regionClick = function () {
alert(scope.elementId);
};
element.attr("ng-click", "regionClick()");
element.removeAttr("region");
$compile(element)(scope);
}
I am wondering if the same thing is possible with the CSV data, IE:
Outer loop over the SVG (Cubicle ID)
Inner loop over the CSV (Personnel/Cubicle ID) data
If Cubicle ID matches Personnel ID, inject the CSV data into that SVG node.
Since I am having some difficulty including the functional code here due to tag and external file restrictions, I have created a Plunk, where you can view everything in context along with external files that is operational in its current state.
Thanks.

After talking with a colleague, he suggested that I:
Convert the CSV list into an array.
Load the array into some kind of container.
When a user clicks on a cubicle.
Iterate through the array and see if there's a match based on the cubicle ID.
Since this my first attempt at Angular, my implementation may well be incorrect, but I've created a new plunk to reflect the latest code changes.
...code in plunker.
Thanks.
Plunk

Related

Angularjs - Charts.js: Same chart element doesn't redraw on other view

I am new to angularjs, trying to create my first directive. I am creating a directive to load Charts.js2.0(beta) into my application.
I have 2 views managed by angular-route, both html view has ng-included a html page that contains only charts-element.
The problem is the first page properly draws the chart, when i go to other view the charts div is loaded but charts is not re-drawn. And now if i go back to first view its blank.
Link to Plunker
What i am doing wrong? Is there any issue with my directive?
Thanks in advance.
There appears to be an issue with the Charts library modifying the existing object on the root scope, and thereby ignoring it forever afterward. I can't really trace down what is doing it, but here's a fix for you: http://plnkr.co/edit/jDQFV62FSeXAQJ6o7jE8
Here is what you had
scope.$watch('config', function(newVal) {
if(angular.isDefined(newVal)) {
if(charts) {
charts.destroy();
}
var ctx = element[0].getContext("2d");
charts = new Chart(ctx, scope.config);
//scope.$emit('create', charts);
}
});
Above, you can see that you're passing scope.config directly into the charts method. That appears to be modifying the data somehow, and since that's passed by reference, you're actually modifying $rootScope.sales.charts. If you copy that object and use it locally like below, you don't have that problem.
Here's how I fixed it.
scope.$watch('config', function(newVal) {
var config = angular.copy(scope.config);
if(angular.isDefined(newVal)) {
if(charts) {
charts.destroy();
}
var ctx = element[0].getContext("2d");
charts = new Chart(ctx, config);
//scope.$emit('create', charts);
}
});
You can see that instead of passing that object directly in, we use angular to make a copy (angular.copy()), and that's the object we pass in.
I think it has relation with the id of the canvas where you are drawing. I've had this problem too amd it was because i was using the same id for the canvas of two graphs in different views. Be sure that those ids are different and that the javasrcipt of each graph is in the controller of each view or in each view itself.
Taking a look at your pluker I see that you are using the same html for the graph and I guess that when angular moves from one of your views to the other thinks that the graph is already drawn. Differentiating two graphs will solve the problem. I don't know of there is any other approach that allows using the same html for the canvas of the graph.
Hope it helps you solve it

Drive Time and Distance from Leaflet Routing Machine

I’m building embedded content for a CRM using JQuery Datatables, Leaflet with OSM tiles, and Leaflet Routing Machine. Markers on the map and the rows of the table are based on the SAME JSON data, and I’m building interactions (using SHARED JavaScript functions) between the two libraries. For example, when a Datatable row is clicked, the row is highlighted, a popup is opened over the corresponding map marker, and a route is calculated using LRM which places the route line on the map. Conversely, when a map marker is clicked, all the same events happen because I'm calling the SAME function.
By default, the Itinerary LRM creates is hidden on the map, but I would like to parse out the Drive Time and Distance, and insert them in the popup opened by the shared function. I have spent four days pouring over the API documentation and searching the internet looking for clear instructions or code samples on how to access these values from the Itinerary object, but with no success. I have inspected every object I can figure out how to log to the console, but the data I need is in properties that come up ‘undefined’ when I try to access them.
Please, from start to finish, how do I access the Itinerary Summary?
When I init the map, I also init the Routing Control with null waypoints:
ctrl = L.Routing.control({
waypoints: null,
units: 'imperial',
show: false,
createMarker: function() { return null; }
}).addTo(map);
When a marker/row is clicked, I call this function:
function clickEffects(id, latlon) {
// set waypoints for routing control
ctrl.setWaypoints([ ctr, latlon])
// scroll the table to the row for the clicked marker
table.$('tr.selected').removeClass('selected');
var idx = table.row("#" + id).index()
table.row(idx).show().draw(false);
table.row(idx).nodes().to$().addClass('selected');
// create and open a popup
var popup = L.popup()
.setLatLng(latlon)
.setContent("Dan was here!!!")
.openOn(map);
}
Figured it out:
Declared popup in global scope
Bound default "Loading..." content when creating marker
In shared function, set div's with target id's in new popup content
(in addition to freshly retrieved ajax content.
In "routesfound" event listener, used JQuery to insert formatted
drive time and distance on target divs.
Works like a charm. Just need to expand on ajax data now...

Where to put a function to create a pdf

I have an application which deals with projects evolving according to a process defined by a series of status transitions. I have a button to set a project to the next status. The button calls a function in the ProjectsController. This function calls another function in the Project model, where I search for the correct transition, depending on the current status, the user_group and some other parameter, and set the new status. After everything successfully accomplished, I return to the original page with 'return $this->redirect($this->referer());' from the controller.
Some of the transitions have side effects. One is to create a PDF, save it on the server and add a new entry to the 'documents' table referenced to the current project.
Problem: where should I put the function to create the PDF? I would like to put it to the Model. But I need some View Files to first render a html page and then convert it to the PDF. I could put it to the Controller. But then I have a controller function which should not be called directly an doesn't render a view.
The functions all work, but where to put them? Is there another possibility? Would it be possible to use a component?
Implement it as PdfView that will render any (pdf) view for you as Pdf. There is already a plugin that does this, search for CakePdf on Github.
A component is clearly the wrong place. A Pdf is output, so it's a kind of view. Model gets you the data, controller sets it, view renders it.

Update real time d3 chart by socket.io

I am writing a d3.js-based real time chart directive. The structure looks like this:
myDirective.js:
app.directive('myDirective', function(socketio) {
return {
restrict: 'EA',
templateUrl: '../../views/partials/chart.html',
controller: DataController,
link: function(scope, elem, attrs, ctrl) {
scope.Watch = scope.$watch(function() {
return ctrl.data;
}, function(newVal) {
// d3 code part
});
socketio.on(event, function(newdata) {
ctrl.data.push(newdata);
// redraw chart
});
}
};
});
In the above d3 code part, I refer to http://bl.ocks.org/gniemetz/4618602. The main code is almost the same and the chart is displayed well.
Now I want to use socket.io to update the chart via code
socketio.on(event, function(newdata) {
ctrl.data.push(newdata);
// redraw chart
});
but do not know how to redraw the chart efficiently with the updated 'ctrl.data'. I know in Morris.js, we could do this by '.setData(ctrl.data)' method, but do not know how to update in d3. Any idea?
ps: I tried to copy/paste the d3 code above to this place, but there was always an error said: "TypeError: t.slice is not a function"
Thanks,
The quick and dirty way: every time you have an update, empty the <div> and redraw the entire graph.
The more elegant way: write your own update function to add new data. This is by far the most visually appealing way, since the graph will actually be animated. D3 is well built for this sort of thing, so its by no means a stretch of its capabilities. This will take a bit longer, but usually provides a much more pleasing experience, as seen on some of the graphs in D3's gallery (examples: http://square.github.io/crossfilter/ , http://bl.ocks.org/NPashaP/96447623ef4d342ee09b) Here's how I would do it using your desired graph:
Keep your code the same
Add an update function, say named Update(), that's called in the socketio.on(...) section where you commented // redraw chart
Update() will then redefine all D3 variables and animate the change
Update() will essentially do the above bullet by performing a subset of the steps used in creating the graph from scratch. Here's an overview of what it'll do: rescale both the x and y axes, graphically update the axes, translate the original points and lines to their new positions on new axes, add new points, and add any lines it may need to complete the graph
I'm working on a jsFiddle for you with a working Update to demo the above and, hopefully, implementing it in your code will be simple. I'll edit this post when its done, but I wanted to give you the "quick" answer while I work on it in an attempt to help you meanwhile. If you want some reading meanwhile, check out
http://blog.visual.ly/creating-animations-and-transitions-with-d3-js/
UPDATE
https://jsfiddle.net/npzjLng9/2/. I took the example you provided and had to modify the data to be loaded to be local, not from a text file; however, it is in the same format, and also a lot less entries for readability. Other than that, I did not make any changes to the example. Here's what I added: scroll down and find the last function, Update. Here is where the update and animation happens. Notice the flow of the function:
"update" the data as if your socket.io was the one who received it and then appended it to the dataset.
Redefine the axes
Redefine the points and paths
Specify a transition duration (feel free to play with whatever number suits you)
Actually update and animate changes on the plot
I added a button to simulate a socket.io receiving event.
For your application, ignore the data.push and console.log in the Update() function and I think that's all you'd need -- other than pointing the data to your ctrl.data array and running the Update() function in your socketio.on(...), of course.
The same basic outline applies to animating/updating most graphs.
I hope this helps you!

How to change image source attribute through directive

I have the following image tag:
<img src="default.png" data-new-image/>
newImage is a directive that I have defined that will fetch the image from the server (based on some criteria) and while it calculates and fetches the image, I have the default.png image file shown.
In this directive, I have defined the link function as:
return {
link: function (scope, element, attrs) {
//My custom logic here to determine which image to show
//and then fetch from the server
//After HTTP request, assigning image to image source
attrs.src = "image_fetched_from_server.png";
}
};
But this does not update the images src attribute. I can see the image fetched clearly and a console.log(attrs) after assigning the image shows that the source attribute was updated with new image. But the DOM inspector in the browser shows no change to the source - it still shows default.png
I am using directive here and not controller - I understand I can use controller and use ng-src but I have this logic across multiple controllers and a directive is the best option I have. How do I change the source of the image tag? I wonder how ng-src does it?
I found the cause.
To set values in the attribute, I needed to use attrs.$set(attribute_name, value).
Thus, I replaced attrs.src with attrs.$set('src', 'image_fetched_from_server.png'); and it worked!
ng-src is nothing but another directive which passes a "source" attribute (or binds it) to a given directive / whatever.
So - what is the name of the directive you are trying to make? One option might be to encompass the entire <img.../> tag in your image-loader-directive
The other issue that might be occurring, could have to do with the fact that your DOM defines src="default.png" instead of something like src="{{ image_src }}". This way the minute your directive decides to change the meaning of image_src - the DOM will refresh and hence pull the correct image from its source.

Resources