Updating NVD3 /D3 chart as per user input radio button - angularjs

http://nvd3.org/examples/cumulativeLine.html
This example show 4 lines chart. What i want to achieve below.
currently, Series 1, Series 2, Series 3, Series 4 (Charts and Respective legends)
I want keep Series 1, Series 2, Series 3 as common
and I have few radio buttons(Outside chart Area) like Series 5, Series 6, Series 7 and on and on.
Now clicking on those radio button i want to show respective chart and common charts(in this case series 1 ,2 and 3)
for Example
Series 5 radio button clicked
Now Series 1, Series 2, Series 3, Series 5 is shown, [This would removed chart3 legend-chart and add legend-chart of Series 5].
I am trying to achieve above in AngularJS and NVD3 Directive. But i am ok i am known to D3 code logic i should be able to change to angular way. Providing Angular work around is warm welcomed.
[Update]
Strange, instead of cumulative i used simple line chart. I changed the data and chart is being updated as anticipated. I dont what is wrong with cumulative. Any Idea?

With angularjs you can try angular-nvd3 directive. It allows you to manipulate with chart data and chart options via JSON, like:
//html
<nvd3 options="options" data="data"></nvd3>
//javascript
$scope.options = { /*some options*/ }
$scope.data = [ /*some data*/ ]
So, in your case, we just extend this approach to when we interactively update data. We can write something like (using checkboxes):
//html
<div ng-repeat="series in initData">
<input type="checkbox" ng-model="checkboxes[series.key]" ng-init="checkboxes[series.key]=true" ng-change="onchange()"/>{{series.key}}
</div>
<nvd3 options="options" data="data"></nvd3>
where initData is a whole static dataset. And in controller
//javascript
$scope.options = { /*some options*/ }
$scope.initData = [
{
key: "Long",
mean: 250,
values: [ ... ]
},
...
];
$scope.data = angular.copy($scope.initData);
$scope.checkboxes = {};
$scope.onchange = function(){
$scope.data = [];
angular.forEach($scope.initData, function(value, index){
if ($scope.checkboxes[value.key]) $scope.data.push(value);
})
}
See live example: http://plnkr.co/edit/ZLcS6M?p=preview

Note: angular-nvd3
http://embed.plnkr.co/tsJntqq5YJnNUGK6goLZ/preview
See plunkr, I did for a button adding a new serie so it shouldn't have too different just changing for the radio. Notice that if you are getting the data from Web Services or similar resources you probably have to redraw the graph
See https://github.com/krispo/angular-nvd3/issues/37
In the directive
<nvd3 options="options" data="data" api="api"></nvd3>
In the Controller
$scope.api.refresh();
Hope this help

Related

Kendo Progress Bar Modifications

Inside a Kendo Tree List for Angular Js I have added a Graph as a kendo template.
<script id="progressStatus" type="text/x-kendo-template">
<div ng-if="'#:Status#' == 'Loading'">
<div kendo-progress-bar="progressBar1" k-min="0" k-max="100" k-value="#:Percentage#" style="width: 100%;"></div>
</div>
</script>
And I bind it to the tree list as part of column declaration
{ field: "Status", template: $("#progressStatus").html(), width: "170px" }
So far good. And I am able to display the value in UI.
However I am not sure how to show following
How to make it of type percent, i tried with k-type='percent' but no luck
If Percentage > 50 show the graph in yellow and text (50%) in red
Unfortunately, some options seems not to work with angular directives. I could not get to work k-type (like you). In my dojo that attribute seems to break the widget. After checking this page, I could use type the following way:
<div kendo-progress-bar="progressBar1" k-options="progressBarOptions" style="width: 100%;"></div>
.controller("MyController", function($scope) {
$scope.dataSource = [
'foo', 'bar', 'baz'
];
$scope.progressBarOptions = {
min: 0,
max: 100,
value: 50,
type: "percent"
};
});
Demo. That will make percent type work.
Now, changing the color of the widget based on the value is another problem. The ProgressBar don't have any kind templates and it is poor in events(only complete and change). It seems that your bar doesn't changes it's value, it's is static, right? So I tried to realize a way to call change event with animation which should call change after being complete. It would be like an initialization event. But, animation doesn't seems to work either. I tried with k-animation and in the init options, but no luck. Double checked for typos but I'm sure that wasn't the case. It's a shame.
Anyway, you can use the ugly and non-straightforward way using a function which you should call in your grid's dataBound event:
var changeBarColor = function()
{
$('[data-role="progressbar"]').each(function() {
$(this).find(".k-state-selected").addClass(
$(this).data("kendoProgressBar").value() < 50
? "yellow-bar"
: "red-bar"
);
});
};
Demo. Again: It's a shame the widget lacks of such a simple and useful feature like that.
I hope I'm wrong but that is the far I could get on this. Good luck.

Placeholder on canvas of ChartJs and Angular

I am using ChartJS with Angular (with this angular directive: http://jtblin.github.io/angular-chart.js).
I wanted to know if it is possible to put a placeholder from see when the chart data are all 0.
For example I have a chart with
$scope.data = [0,0,0];
$scope.labels = ["first", "second", "third"];
Is possible to print only the placeholder? or put one by default?
You can trun on the legend and override the legend template to show the values in the legend
$scope.options = {
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%>: <%=segments[i].value%><%}%></li><%}%></ul>"
};
If you want to show the value only if they are 0, just add an if condition
$scope.options = {
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%if(segments[i].value === 0){%>: <%=segments[i].value%><%}}%></li><%}%></ul>"
};
Working fiddle - http://jsfiddle.net/s2t3quwp/
If all your data points are 0, you will see not chart, but you can still see the legend.

How can I move this code from static AngularJS/jQuery to dynamic AngularJS?

My goal is to create in pure AngularJS a fill-in-the-blank activity which takes collection of records that contain phrases with markers where the blanks are, the correct answers, and incorrect answers to display along with them.
I've programmed a prototype in AngularJS/jQuery, below, which has the interactive functionality that I want, also here: http://jsfiddle.net/kkLdzngv/2/
What do I need to do to now is:
replace all the jQuery code with AngularJS code
make it dynamic so that not just one phrase is displayed, but all phrases in the collection are displayed with input fields each with the functionality that is in my prototype
In particular, I don't understand the correct AngularJS approach. I assume I need to use a ng-repeat to iterate through $scope.texts and inside the ng-repeat I would have my <div class="item">. But at that point, what functionality of AngularJS do I need to use to create the functionality shown in the prototype?
HTML:
<div class="item">
<div class="text">Customer 1 is from <input id="record_1_blank_1" ng-change="checkEntry_record_1_blank_1()" ng-model="record_1_blank_1"/> and Customer 2 is from <input id="record_1_blank_2" ng-model="record_1_blank_2"/>.</div>
<div class="answers">Munich, <span id="answer2">Berlin</span>, Frankfurt, Stuttgart, Hamburg</div>
</div>
JavaScript:
$scope.texts = [
{
body: 'Customer 1 is from [##blank] and Customer 2 is from [##blank].',
correctAnswers: 'Berlin;Hamburg',
incorrectAnswers: 'Stuttgart;Munich;Frankfurt'
},
{
body: 'Company 3 is located in [##blank].',
answers: 'Bremen'
}
];
$scope.checkEntry_record_1_blank_1 = function (obj) {
if ($scope.record_1_blank_1 == 'Berlin') {
$('#record_1_blank_1').css(
{
background: 'lightgreen'
})
.attr('disabled', 'disable');
$('#record_1_blank_2').focus();
$('#answer2').css('text-decoration', 'line-through');
}
};

Unselected radio button value in Angularjs

I've got an angularjs application that has a form/controller that look essentially like this (boiled down to the pertinent stuff):
angular.module('testApp', [])
.controller('testCtrl', function ($scope) {
$scope.envelopes = [{
id: 1,
name: 'first',
default_spend: '1'
}, {
id: 2,
name: 'second',
default_spend: '0'
}, {
id: 3,
name: 'third',
default_spend: '0'
}, ];
});
And a form that looks roughly like this:
<div ng-app="testApp">
<div ng-controller="testCtrl">
<div ng-repeat="envelope in envelopes">
<div>{{envelope.name}}
<input type="radio" name="default_spend" ng-model="envelope.default_spend" ng-value="1" />
Default Spend: {{envelope.default_spend}}
</div>
</div>
</div>
</div>
You can see this in action with this fiddle.
As you can see, the first envelope is marked as the default_spend envelope and the other two aren't. When I select a different envelope, that envelope also gets marked as the default_spend, but when the radio button is unselected, the model value stays the same ("1"). I understand that I'm dealing with a child scope here due to ng-repeat, but is there a way for me to set an "unselected" value without having to jump through hoops with ngChange?
Not really. When you use ng-value it is what is going to get bound to the ng-model and in your case all of them are having value 1. Also i really did not get the purpose of toggling 1 and 0, however you could just achieve it this way:-
<input type="radio" name="default_spend"
ng-click="selected.envelope = envelope" /> <!--Register an ng-click and set selected one
Default Spend: {{getDefSpend(envelope)}}</div> <!-- Show the text based on selection-->
And in your controller:-
$scope.selected = {envelope: $scope.envelopes[0]};
$scope.getDefSpend = function(envelope){
return $scope.selected.envelope === envelope ? 1 : 0;
}
//$scope.selected.envelope will be your selected option at any point.
Demo
It's because what you have is actually 3 different model values. To work as you want it, you would have ONE model value for all 3. You can either restructure your data, or use ng-change to modify your model manually.

Push rows in a table rendered with ng-repeat in angular

I want to push in an extra row inline in a table when the client clicks on the row. The data should not be prefetched, since I expect there to be at most 30 rows but where each row has associated data that would be unreasonable to fetch in one get.
My approach so far is to use ng-repeat to iterate my collection and render a table. When the client presses the row, the client expects details about the row to be shown inline as an extra row under the pressed row.
<tr ng-repeat="court in courts">
<td>{{court.name}}</td>
<td>{{court.number}}</td>
<td>{{court.nrOfPlayers}}</td>
<td>
<a href ng:click="toggle(court.number)">Details</a> <!-- click toggles extra row with data loaded async -->
</td>
</tr>
<!-- extra row here -->
I have managed to show the details beneath the table with a ng-show in a hacky way, but that is not what I want.
How do you accomplish this with angular.js? What is the angular way to do this?
Here is a fiddle with a stupid squash court example http://jsfiddle.net/HByEv/
I think a possible solution could be http://jsfiddle.net/HByEv/2/.
There is also an alternative for the "No players" message commented in the fiddle if you want to also get rid of the extra <tr ng-show="..."></tr>.
Edit:
As pointed in the comments, in AngularJS 1.2+ you can now use ng-repeat-start and ng-repeat-end to solve this problem.
Jossef Harush provided a fiddle: http://jsfiddle.net/3yamebfw/
One sample
var app = angular.module('test-app', []);
app.controller('MyController', function($scope, $rootScope, $timeout){
$scope.copy = {
p1: ['c1 p1', 'c1 p2'],
p3: ['c3 p1', 'c3 p2', 'c3 p3', 'c3 p4', 'c3 p5']
}
$scope.courts = [
{
"number": 1,
"name": "the best court",
"nrOfPlayers": 2
}, {
"number": 2,
"name": "the bad court",
"nrOfPlayers": 0
}, {
"number": 3,
"name": "the other court",
"nrOfPlayers": 5
}
];
$scope.loadPlayers = function(court){
//Implement your logic here
//Probably using ajax
$timeout(function(){
$scope.players = $scope.copy['p' + court.number] || [];
}, Math.random() * 2000);
}
$scope.shouDetails = function(court){
if(court.nrOfPlayers) {
delete $scope.players;
$scope.loadPlayers(court);
} else {
$scope.players = [];
}
}
})
Demo: Fiddle
Well. In fact, the main issue with your design is that you want to show thousands of rows in the same table. It will work, but it might be hard to render in some browsers (IE). For each rows, you will have a few bindings and each binding add watchers. You should always try to minimize the amount of binding in the page. I suggest you to use a pagination system in your array. Pagination on a list using ng-repeat
If you really want to do what you want without prerendering the rows, you will have to edit the dom, which is not a good practice in angular when avoidable. In my case, i would place the data somewhere else on the page, in a static zone. When i did something like this, I added a twitter bootstrap modal in my page and when the user clicked on "more info" the modal was opened with the info of the selected object.

Resources