Create highcharts using angularjs ng-repeat - angularjs

I have requested the json data containing all the information about people, and I want to a draw highcharts for each person based on the his information. I saw a solution here, but it seems that the config will always be overridden by the last li's config information. Is there a way to keep different configs for each highchart?
<div data-ng-repeat="a in people">
<h4>Method: {{ a.name }}</h4>
<highchart config="chartConfig"></highchart>
</div>

I encountered the same problem. It turns out that ng-repeat create new scope for each item, therefore the config attribute of highchart directive should correspond to the setting in ng-repeat. In your example, since you repeat the people by <div data-ng-repeat="a in people"> ,changing
<highchart config="chartConfig"></highchart>
to
<highchart config="a.chartConfig"></highchart>
should help.
I had create a plunker as an example.

You can use a partial view with a controller. Using ng-highcharts of course.
Like so, in your main you have:
<div data-ng-repeat="person in people">
<h4>Method: {{ person.name }}</h4>
<div data-ng-include="'/partial/chart.html'"></div>
</div>
Then in your partial, you have:
<div data-ng-controller="ChartController">
<highchart config="chartConfig"></highchart>
</div>
Then in your controller, you have:
app.controller('ChartController', function ($scope) {
$scope.chartConfig = {
chart: {
type: 'pie'
},
title: {
text: $scope.person.name
},
series: [{
data: $scope.person.chartdata
}]
};
});
Hope this helps.

You have to create a list then add to that list each chart configuration. Use ng-repeat in the list of charts:
//See: https://github.com/pablojim/highcharts-ng
var myapp = angular.module('myapp', ["highcharts-ng"]);
myapp.controller('myctrl', function ($scope) {
//The list who will contain each chart
$scope.chartlist = [];
//Chart 1
$scope.chartConfig = {
options: {
chart: {
type: 'bar'
}
},
series: [{
data: [10, 15]
}],
}
//Chart 2
$scope.chartConfig2 = {
options: {
chart: {
type: 'bar'
}
},
series: [{
data: [10, 15, 12, 8, 7]
}],
}
$scope.chartlist.push($scope.chartConfig);
$scope.chartlist.push($scope.chartConfig2);
});
then in your html use ng-repeat on the list of charts:
<div ng-app="myapp">
<div ng-controller="myctrl">
<div ng-repeat="char in chartlist" class="row">
<highchart id="chart1" config="char" class="span10"></highchart>
</div>
</div>
if you want to use dinamic data you can use a foreach to create each chart config, in this example I create a chart foreach object in the array 'a':
$scope.chartlist = [];
var a = [[1, 2],[2,4]];
function chardata(){
for (var i = 0; i < a.length; i++) {
$scope.chartConfig = {
options: {
chart: {
type: 'bar'
}
},
series: [{
data: a[i]
}],
}
$scope.chartlist.push($scope.chartConfig);
}
}
chardata();

Related

AngularJS ng-model not bound to Kendo UI dropdown when datasource data not in range

I'm developing a complex application using angularjs vs Kendo UI.
Here is the simple view:
<div id="example" ng-app="KendoDemos">
<div ng-controller="MyCtrl">
<h4>DropDown</h4>
<select kendo-drop-down-list="" options="options" ng-model="selectedProduct"></select>
</div>
</div>
Here is simple js:
<script>
angular.module("KendoDemos", [ "kendo.directives" ])
.controller("MyCtrl", function($scope){
$scope.selectedProduct = 51;
$scope.options = {
autoBind: true,
dataTextField: "ProductName",
dataValueField: "ProductID",
filter: "contains",
suggest: true,
dataSource: {
transport: {
read: {
dataType: "jsonp",
url: "//demos.telerik.com/kendo-ui/service/Products"
}
},
pageSize: 50
}
};
})
</script>
Now you can see that $scope.selectedProduct = 51; and pageSize: 50 and that means that the dropdown is never get initialized with a predefined value. Feel free to update $scope.selectedProduct with the value 1 to 50 and see the dropdown actually initialized. This is the simplest code to highlight the problem. In real world this is pretty much the same scenario so I'd like to figure out how to make this binding work in an elegant way.
Here is a dojo: http://dojo.telerik.com/OpeqO
You can watch the selectedProduct and if the new value is null you can change the default.
$scope.$watch('selectedProduct',function(newVal,oldVal,$scope){
if(newVal==null){
$scope.selectedProduct = 1;
}
});

Using ng-repeat in order to iterate over object properties and ordering by value instead of key with ng 1.3

I am in reference to the angular documentation about ngRepeat and iterating over object properties which states:
<div ng-repeat="(key, value) in myObj"> ... </div>
You need to be aware that the JavaScript specification does not define
the order of keys returned for an object. (To mitigate this in Angular
1.3 the ngRepeat directive used to sort the keys alphabetically.)
Say I have the following object:
var myObj = {FOO: 0, BAR: 1};
and want it to be ordered by value (i.e. 0 & 1) instead of keys. How can this be achieved with angular? (I use angular 1.3 by the way).
I have tried:
ng-repeat="(key, value) in myObj | orderBy:value"
But it does not work...
Can anyone please help?
Just as with filter, orderBy works with arrays only. One simple solution is to use a function that returns an array from an object:
<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="namesCtrl">
<input type="text" ng-model="searchText"/>
<ul ng-init="nameArray=objArray(names)">
<li ng-repeat="x in nameArray | filter:searchText | orderBy: 'value.name'">
{{x.value.name}}
</li>
</ul>
</div>
<script>
angular.module('myApp', []).controller('namesCtrl', function($scope) {
$scope.searchText='';
$scope.names = {
"1": {
"name": "some"
},
"2": {
"name": "values"
},
"3": {
"name": "are"
},
"4": {
"name": "there"
},
"5": {
"name": "here"
}
};
$scope.objArray=function (obj) {
var result=[];
for (var key in obj) {
result.push({
key: key,
value: obj[key]
});
}
return result;
}
});
</script>
</body>
</html>
I believe the best and elegant solution is to make a filter that changes your object into an array. Therefore you can reuse it thought all your project in your template without coding any js.
Here is what the filter would look like:
(function () {
'use strict';
angular.module('app').filter('toArray', toArray);
toArray.$inject = [];
function toArray() {
return toArrayFilter;
function toArrayFilter(input) {
if (angular.isArray(input)) return input;
var list = [];
angular.forEach(input, iterateProperty, list);
return list;
function iterateProperty(elt, key) {
this.push({ key: key, value: elt });
}
}
}
})();
and here is how you would use it in your html template:
<div ng-repeat="element in myObject | toArray | orderBy:value">
{{element.key}}:
<pre>{{element.value | json}}</pre>
</div>

Angular Grouping Directive starting point

I'am trying to create a grouping and filtering mechanism with several predefined filters. I have a collection of undefined rules and some predefined grouping actions, for example "relativeDate" (today, tomorrow, yesterday, this week, ...), "boolean" or . The set of actions should be expandable.
I've managed to get this working in a controller. But I want to outsource this into a directive to get this working with other object collections. The Problem is: I need to specify the template of the list dynamically.
Imagine the following collections:
$scope.memosReceived = [
{ id: 1, from: 'Henry Ford', title: 'Want your Model T?', received: '2015-05-04T12:30:00', read: true },
{ id: 2, from: 'Oliver Newton', title: 'Look at this!', received: '2015-06-15T08:00:00', read: true }
...
];
$scope.memosSent = [
{ id: 1, to: 'Henry Ford', title: 'That is an old car', sent: '2015-05-04T12:35:21', read: true },
{ id: 2, to: 'Oliver Newton', title: 'Stop Spam', sent: '2015-06-15T08:01:47', read: false }
...
];
Now the markup should be like the following:
<div ng-controller="controller">
<h2>Received</h2>
<grouped-list ng-model="memosReceived" item-var="received" grouping-options="groupingReceived">
<h2>{{ received.title }} <sub>by {{ received.from }}</h2>
</grouped-list>
<h2>Sent</h2>
<grouped-list ng-model="memosSent" item-var="sent" grouping-options="groupingSent">
<h2>{{ sent.title }} <sub>to {{ sent.to }}</h2>
</grouped-list>
</div>
Options could be like:
$scope.groupingReceived = [
{ modelKey: 'received', action: 'relativeDate', options: { [.. passed to grouping action, like value->name mapping ..] },
{ modelKey: 'read', action: 'boolean', options: { [...] } }];
$scope.groupingSent = [
{ modelKey: 'sent', action: 'relativeDate', options: { [.. passed to grouping action, like value->name mapping ..] },
{ modelKey: 'read', action: 'boolean', options: { [...] } }];
The rendered HTML should look like this "PseudoHtml":
<div ng-controller="controller">
<h2>Received</h2>
<div class="grouped-list">
<div class="filter-section">
<button ng-click="openFilters = !openFilters>Open Filters</button>
<div class="filter-options" ng-hide="!openFilters">
<h4>Group by</h4>
[selectbox given group actions] [selectbox sort ascending or descending]
<h4>Filter by</h4>
[build filters by similar to group actions given filter actions]
</div>
</div>
<div class="group">
<div class="group-header">
<h3>Yesterday</h3>
</div>
<ul class="group-list">
<li ng-repeat="received in ngModel | customFilters">
<!-- something like transclusion starts here -->
<h2>{{ received.title }} <sub>by {{ received.from }}</h2>
<!-- something like transclusion ends here -->
</li>
</ul>
</div>
<div class="group">
<div class="group-header">
<h3>Last Week</h3>
</div>
<ul class="group-list">
<li ng-repeat="received in ngModel | customFilters">
<!-- something like transclusion starts here -->
<h2>{{ received.title }} <sub>by {{ received.from }}</h2>
<!-- something like transclusion ends here -->
</li>
</ul>
</div>
</div>
<h2>Sent</h2>
<div class="grouped-list">
[... same like above ...]
</div>
</div>
I am really struggeling how to achieve this behavior, where to store the several parts of the logic (e.g. the grouping actions, the custom filters) and how to transclude this correctly.
Maybe someone can give me a good starting point for that.
You could create a custom filter and call it from the controller of your directive.
Inside of this filter you can decide which filter action should be triggered by passing parameters to the filter.
I would call it from the controller instead of the template because there you can easier chain your filters.
Please have a look at the demo below or in this jsfiddle.
During adding my code to SO I've detected a bug (not displaying the item) in my code with a newer AngularJS version. Not sure what it is but with 1.2.1 it's working.
I'll check this later. Seems like a scoping issue.
angular.module('demoApp', [])
.filter('aw-group', function($filter) {
var filterMethods = {
relativeDate: function(input, action) {
console.log('relative date called', input);
return input; // do the translation to relative date here
},
filterByNumber: function(input, action, options) {
// if you need mor parameters
return $filter('filter')(input, options.number);
},
otherFilter: {
}
};
return function(input, action, options) {
return filterMethods[action](input, action, options);
};
})
.directive('groupedList', function () {
return {
restrict: 'E',
scope: {
model: '=',
itemVar: '=',
filter: '='
},
transclude: true,
template: '<ul><li ng-repeat="item in filteredModel" ng-transclude>'+
'</li></ul>',
controller: function($scope, $filter) {
//console.log($scope.filter);
$scope.filteredModel = $filter('aw-group')($scope.model, 'filterByNumber', { number: 2 }); // passing action from $scope.filter.action as second parameter, third is an options object
}
};
})
.controller('mainController', function () {
this.data = [{
title: 'Test1',
from: 'tester1'
}, {
title: 'Test2',
from: 'tester1'
}, {
title: 'Test3',
from: 'tester1'
}, ];
this.groupingReceived = [{
modelKey: 'received',
action: 'relativeDate',
options: {},
modelKey: 'read',
action: 'boolean',
options: {}
}];
this.memosReceived = [{
id: 1,
from: 'Henry Ford',
title: 'Want your Model T?',
received: '2015-05-04T12:30:00',
read: true
}, {
id: 2,
from: 'Oliver Newton',
title: 'Look at this!',
received: '2015-06-15T08:00:00',
read: true
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="mainController as ctrl">
<grouped-list model="ctrl.data" item-var="received" filter="ctrl.groupingReceived">
<h2>{{item.title}}<sub>{{item.from}}</sub></h2>
</grouped-list>
</div>

Saving a checkbox list in Angularjs with node

I want to save and load a checkbox list using binding in angularjs with a node backend. This SO question (How do I bind to list of checkbox values with AngularJS?) answers how I can load the check box from a static javascript object but how can I save the checkbox values after the user selects them?
I want to save the checkbox values in a single field but how can I tell angular to bind the checkbox values into a single field defined in a model to my mongodb? I cant just use ng-model as there are multiple checkboxes.
Needless to say that I am new to angular so any help here is greatly appreciated ...
Thanks for any help you can provide.
kseudo
Just add ng-model to your checkbox. By this way you can update model in controller on any checkbox state change.
Here is example:
HTML
<div ng-controller="Ctrl">
<label ng-repeat="fruit in fruits">
<input
type="checkbox"
name="fruit.name"
ng-model="fruit.value"
> {{fruit.name}}
</label>
<pre>{{fruits| json}}</pre>
</div>
JS
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.fruits = [{
name: 'apple',
value: false
}, {
name: 'orange',
value: false
}, {
name: 'pear',
value: false
}, {
name: 'naartjie',
value: false
}];
}
Demo Fiddle
[EDIT]
BTW we can make the copy by using angular.copy() method. When we press button, the new copy of fruits model will be created (and you should send it to server as json). Old model fruits will stay the same:
$scope.fruitsCopy = [];
$scope.init = function(){
$scope.fruitsCopy = angular.copy($scope.fruits );
}
To convert data to JSon I would use:
var jsonData = JSON.stringify($scope.fruitsCopy);
Demo2 Fiddle
Let's say you defined your model as such:
function Ctrl($scope) {
$scope.items = [{
name: 'A',
checked: false
}, {
name: 'B',
checked: false
}, {
name: 'C',
checked: false
}, {
name: 'D',
checked: false
}];
}
And created a view for the model:
<ul>
<li ng-repeat="item in items">
<label>
<input type="checkbox" ng-model="item.checked">
{{item.name}}
</label>
</li>
</ul>
<button ng-click="save()">save</button>
Next you have to define save function:
$scope.save = function() {
//angular.toJson converts array to string, something like
// '[{"name":"A","checked":false},{"name":"B","checked":true},...]'
var json = angular.toJson($scope.items);
//Service is angular service for your model that makes http requests to your backend
Service.save(json).then(function(){
//here you can notify user that data is persisted
}, function() {
//here you can notify user that there was a problem with request
});
}
And a simple model service:
.service('Service', function($http) {
return new function() {
this.save = function(data) {
return $http.post('url/to/backend', data);
}
}
});

AngularJS - Is there an easy way to set a variable on "sibling" scopes?

I have this problem where I am trying to make a click on a div hide all the other divs of the same "kind". Basically I'd have to, from a child scope set the variable on all other "sibling" scopes.
To illustrate this, I have created the following:
HTML
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="model in models" ng-controller="MyChildCtrl">
<a ng-click="toggleVisibility()">toggle {{ model.name }} {{ visibility }}</a>
<div ng-show="visibility">
{{ model.name }}
</div>
</div>
</div>
</div>​
JavaScript
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
console.debug('scope');
$scope.models = [
{ name: 'Felipe', age: 30 },
{ name: 'Fernanda', age: 28 },
{ name: 'Anderson', age: 18 }
];
}
function MyChildCtrl($scope) {
$scope.visibility = false;
$scope.toggleVisibility = function() {
$scope.visibility = !$scope.visibility;
}
}
JSFiddle: http://jsfiddle.net/fcoury/sxAxh/4/
I'd like that, every time I show one of the divs, that all other divs would close, except the clicked one.
Any ideas?
#kolrie while your approach works I would suggest a different solution which doesn't require any changes to the model. The basic idea is to keep a reference to a selected item and calculate viability by comparing a current item (inside ng-repeat) with a selected one.
Using this solution the toggle function would become:
$scope.toggleVisibility = function(model) {
$scope.selected = model;
};
and calculating visibility is as simple as:
$scope.isVisible = function(model) {
return $scope.selected === model;
};
Finally the relevant part of the markup is to be modified as follows:
<div ng-controller="MyCtrl">
<div ng-repeat="model in models">
<a ng-click="toggleVisibility(model)">toggle {{ model.name }} {{ isVisible(model) }}</a>
<div ng-show="isVisible(model)">
{{ model.name }}
</div>
</div>
</div>
Here is a complete jsFiddle: http://jsfiddle.net/XfsPp/
In this solution you can keep your model untouched (important if you want to persist it back easily) and have AngularJS do all the heavy-lifting.
OK, I have added a visible attribute to the model, and I managed to get this done:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
console.debug('scope');
$scope.models = [
{ name: 'Felipe', age: 30, visible: false },
{ name: 'Fernanda', age: 28, visible: false },
{ name: 'Anderson', age: 18, visible: false }
];
}
function MyChildCtrl($scope) {
$scope.toggleVisibility = function() {
angular.forEach($scope.models, function(model) {
model.visible = false;
});
$scope.model.visible = true;
}
}
Live here: http://jsfiddle.net/fcoury/sxAxh/5/
Is this the most efficient way? Do you think it's a good practice if I inject this visible attribute into my model data after getting it via AJAX?

Resources