I have been struggling on how to insert JSON items into a dynamic list :
I have a json file which look like :
[
{
"id":"34",
"City":"New York",
"Country":"USA"
},
{
"id":"22",
"City":"Las vegas",
"Country":"USA"
},
{
"id":"44",
"City":"Paris",
"Country":"France"
},
{
"id":"45",
"City":"Lyon",
"Country":"France"
}
]
I want to show it like that :
Here is my code :
<div ng-controller="customersCtrl">
<div class="list">
<div ng-repeat="c in cities">
<div class="item item-divider" >
{{ c.Country }}
</div>
<a class="item" href="#" ng-repeat="ci in cities" ng-if="c.Country == ci.Country"> {{ ci.City }} </a>
</div>
var app = angular.module('starter', []);
app.controller('customersCtrl', function($scope, $http) {
$http.get('data.json')
.then(function(result){
$scope.cities = result.data;
});
});
it's showing like that :
The easiest way to do this is pre-process the data to show the inherent structure of your data. Then you can use nested repeaters.
angular.module('cityModule', []).
controller('CityController', ['$scope',
function($scope) {
var rawData = [{
"id": "34",
"City": "New York",
"Country": "USA"
}, {
"id": "22",
"City": "Las vegas",
"Country": "USA"
}, {
"id": "44",
"City": "Paris",
"Country": "France"
}, {
"id": "45",
"City": "Lyon",
"Country": "France"
}];
$scope.citiesByCountry = {};
for (var i = 0; i < rawData.length; i++) {
var city = rawData[i];
if ($scope.citiesByCountry[city.Country] == undefined) {
$scope.citiesByCountry[city.Country] = {};
$scope.citiesByCountry[city.Country].name = city.Country;
$scope.citiesByCountry[city.Country].cities = [];
}
$scope.citiesByCountry[city.Country].cities.push(city);
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="cityModule">
<div ng-controller="CityController">
<div ng-repeat="(countryName, country) in citiesByCountry">
<h1>{{country.name}}</h1>
<div ng-repeat="city in country.cities">
{{city.City}}
</div>
</div>
</div>
</body>
This is due to the first loop, there are 4 items in the list and there is not any logic to stop the list from repeating on countries which have already been written.
Issue
<div ng-repeat="c in cities">
<div class="item item-divider" >
{{ c.Country }}
Create a filter to loop over unique Countries (this may involve creating a new list of unique countries).
Unique Filter
app.filter('unique', function() {
return function(collection, keyname) {
var output = [],
keys = [];
angular.forEach(collection, function(item) {
var key = item[keyname];
if(keys.indexOf(key) === -1) {
keys.push(key);
output.push(item);
}
});
return output;
};
});
<div ng-repeat="c in cities | unique: 'Country'"></div>
Related
I have a list on ng-repeat with names. I want to take this name and pass to url as parameter to take his subjects. What i have done until now is that i can pass the name and get the subjects from another API. But the problem is that the last name replaces all the dropdowns with his values:
<ul ng-repeat="person in persons">
<li ng-init="pass(person.name)">{{person.name}}</li>
<li>
<select>
<option ng-repeat="(key, value) in choices">{{value}}</option>
</select>
</li>
</ul>
Controller.js
$scope.person = {
[
{
"id": 1,
"name": "Mark"
},
{
"id": 2,
"name": "Jakob"
}
]
}
$scope.pass = function(name) {
$scope.name = name;
$http.get('url?name=' + $scope.name, {})
.then(function(response) {
$scope.choices = response.data;
},
function(errResponse) {
console.error('Error while fetching choices');
});
}
Note: There is a get method to pass the person.name at the end of the URL.
How can it be isolated to pass from HTML to controller.
Thanks
Make your controller code like:
$scope.persons = [{
"id": 1,
"name": "Mark"
},
{
"id": 2,
"name": "Jakob"
}
];
$scope.pass = function(person) {
$scope.name = person.name;
$http.get('url?name=' + $scope.name, {}).then(function(response) {
person.choices = response.data;
},
function(errResponse) {
console.error('Error while fetching choices');
});
}
And your html like:
<ul ng-repeat="person in persons">
<li ng-init="pass(person)">{{person.name}}</li>
<li><select>
<option ng-repeat="(key, value) in person.choices">{{value}}</option>
</select></li>
</ul>
Here's a sample fiddle: https://jsfiddle.net/Lt7aP/4019/
For some reason, when I splice an object into my ng-repeat array, it doubles what I splice in and hides the last object in the array.
However, if I click the toggle hide and "refresh" the ng-repeat, it shows the right data.
Does anyone know why this would be happening and what I can do to fix it?
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.workflow = {
flow: [{
"id": "1334f68db820f664",
"step_number": 1,
"tasks": [{
"id": "1334f68e3f20f665"
}]
}, {
"id": "134967a5ba205f5b",
"step_number": 2,
"tasks": [{
"id": "134972c5b420e027"
}]
}, {
"id": "1334f68e7d209ae6",
"step_number": 3,
"tasks": [{
"id": "1334f68ef6209ae7"
}]
}]
};
$scope.insertStep = function() {
var insertStepIndex = 1,
task_data = {
"id": null,
"step_number": (insertStepIndex + 2),
"tasks": []
};
//go through each item in the array
$.each($scope.workflow.flow, function(index, step) {
//if the step number is greater then the place you want to insert it into, increase the step numbers
if (step.step_number > $scope.workflow.flow[insertStepIndex].step_number) step.step_number++;
});
$scope.workflow.flow.splice((insertStepIndex + 1), 0, task_data);
}
$scope.toggleHide = function() {
$scope.hide = !$scope.hide;
}
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-click="insertStep()">Insert Step</div>
<br />
<br />
<div ng-click="toggleHide()">Toggle Repeat</div>
<br />
<br />
<div ng-if="!hide">
<div ng-repeat="data in workflow.flow | orderBy:'+step_number'" ng-init="$stepIndex = workflow.flow.indexOf(data)">
{{ workflow.flow[$stepIndex].step_number }}
</div>
</div>
</div>
I got the problem. This is a tricky but the simple part. The ng-init directive only execute once. So your assignment to $stepIndex = workflow.flow.indexOf(data) will not updated when you push a new data to the array/list.
So adding a scope function will fix this problem since Angular will auto call the function when it's returning value changes.
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.workflow = {
flow: [{
"id": "1334f68db820f664",
"step_number": 1,
"tasks": [{
"id": "1334f68e3f20f665"
}]
}, {
"id": "134967a5ba205f5b",
"step_number": 2,
"tasks": [{
"id": "134972c5b420e027"
}]
}, {
"id": "1334f68e7d209ae6",
"step_number": 3,
"tasks": [{
"id": "1334f68ef6209ae7"
}]
}]
};
$scope.insertStep = function() {
var insertStepIndex = 1
var task_data = {
"id": null,
"step_number": (insertStepIndex + 2),
"tasks": []
};
//go through each item in the array
angular.forEach($scope.workflow.flow, function(step, index) {
//if the step number is greater then the place you want to insert it into, increase the step numbers
if (step.step_number > $scope.workflow.flow[insertStepIndex].step_number) step.step_number++;
});
$scope.workflow.flow.splice((insertStepIndex + 1), 0, task_data);
}
// This is a new function which I added to fix the problem
$scope.getIndex = function(data) {
return $scope.workflow.flow.indexOf(data);
};
$scope.toggleHide = function() {
$scope.hide = !$scope.hide;
};
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-click="insertStep()">Insert Step</div>
<br />
<br />
<div ng-click="toggleHide()">Toggle Repeat</div>
<br />
<br />
<div ng-if="!hide">
<div ng-repeat="data in workflow.flow | orderBy:'+step_number'">
{{ workflow.flow[getIndex(data)].step_number }}
</div>
</div>
</div>
How can i filter based on object values like 1 or 2 or 3
i am trying to filter my json which looks similar to the names object
This is my code i tried to apply filter but its not working
<!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">
<ul>
<li ng-repeat="x in names ">
{{ x.name }}
</li>
</ul>
</div>
<script>
angular.module('myApp', []).controller('namesCtrl', function($scope) {
$scope.names = {
"1": {
"name": "some"
},
"2": {
"name": "values"
},
"3": {
"name": "are"
},
"4": {
"name": "there"
},
"5": {
"name": "here"
}
}
});
</script>
</body>
</html>
filter and orderBy do not work on object properties, only arrays. That being said, I think I found a solution that you will like:
<!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">
{{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 don't think you can filter on property names. I assume these property names are some kind of IDs that you want to filter on. Then your data structure should reflect that and be like this:
$scope.names = [
{ id: "1", name: "some" },
{ id: "2", name: "values" },
{ id: "3", name: "are" },
{ id: "4", name: "there" },
{ id: "5", name: "here" }
]
Then you can filter like this:
<ul>
<li ng-repeat="x in names | filter: { id: searchId } >
{{ x.name }}
</li>
</ul>
Replace the searchId with your specific scope variable which contains the ID you are looking for.
I've written the following code to read data from json file and draw a graph. now I want to get the data from user through input fields and update the json with new values. I wrote the code to copy the value and made it accessible outside the scope, I just wonder how to update the existent json with the new values.
json looks like this:
{
"nodes" : [
{ "id": 1, "label": "End Product", "level": 0 },
{ "id": 2, "label": "A 1", "level": 1 },
{ "id": 3, "label": "A 2", "level": 1 },
{ "id": 4, "label": "A 3", "level": 1 },
{ "id": 5, "label": "B 1", "level": 2 },
{ "id": 6, "label": "C 1", "level": 3 },
{ "id": 7, "label": "B 2", "level": 2 },
{ "id": 8, "label": "B 3", "level": 2 }
],
"edges" : [
{ "from": 1, "to": 2 },
{ "from": 1, "to": 3 },
{ "from": 1, "to": 4 },
{ "from": 2, "to": 5 },
{ "from": 5, "to": 6 },
{ "from": 3, "to": 7 },
{ "from": 4, "to": 8 }
]
}
var app = angular.module('bomApp', ['bomGraph']);
app.controller('bomController', ['$scope', 'appService', '$rootScope', function ($scope, appService, $rootScope) {
var get = function () {
appService.get().then(function (promise) {
$scope.graph = {
options: {
"hierarchicalLayout": {
"direction": "UD"
},
"edges": {
"style":"arrow-center",
"color":"#c1c1c1"
},
"nodes": {
"shape":"oval",
"color":"#ccc"
}
},
data: {
nodes: promise.nodes,
edges: promise.edges
}
};
});
};
$scope.newNode = {
id: undefined,
label: undefined,
level: undefined,
parent: undefined,
};
$scope.arrNode = {};
$scope.update = function (nodes) {
$scope.arrNode = angular.copy(nodes);
$rootScope.nodex = angular.copy(nodes);
};
$scope.newEdge = {
id: undefined,
from: undefined,
to: undefined
};
$scope.arrEdge = {};
$scope.updateE = function (edges) {
$scope.arrEdge = angular.copy(edges);
$rootScope.edgex = angular.copy(nodes);
};
get();
}]);
app.factory('appService', ['$q', '$http', '$rootScope', function ($q, $http, $rootScope) {
return {
get: function (method, url) {
var deferred = $q.defer();
$http.get('data.json')
.success(function (response) {
deferred.resolve(response);
})
return deferred.promise;
},
};
}]);
<body data-ng-app="bomApp">
<div data-ng-controller="bomController">
<h3>Create Your Own Graph</h3>
<div class="node">
<h4>Nodes:</h4>
<div class="panel">
<label>Id:</label>
<input type="text" id="idNode" data-ng-model="newNode.id"/>
<br />
<label>Label:</label>
<input type="text" id="label" data-ng-model="newNode.label" />
<br />
<label>Level:</label>
<input type="text" id="level" data-ng-model="newNode.level" />
</div>
<div>
<button id="addNode" data-ng-click="update(newNode)">Add</button>
</div>
</div>
<div class="node">
<h4>Edges:</h4>
<div class="panel" >
<label>Id:</label>
<input type="text" id="idEdge" data-ng-model="newEdge.id"/>
<br />
<label>From:</label>
<input type="text" id="from" data-ng-model="newEdge.from"/>
<br />
<label>To:</label>
<input type="text" id="to" data-ng-model="newEdge.to"/>
</div>
<div>
<button id="addEdge" data-ng-click="updateE(newEdge)">Add</button>
</div>
</div>
<div data-custom-dir="" id="graph" data="graph.data" options="graph.options"></div>
<div>
<h3>Monitor The Changes</h3>
<div class="node">
<h4>Nodes:</h4>
<div class="col">
<div id="nodes">
<pre>{{newNode | json}}</pre>
<pre>{{arrNode | json}}</pre>
</div>
</div>
</div>
<div class="node">
<h4>Edges:</h4>
<div class="col">
<div id="edges">
<pre>{{newEdge | json}}</pre>
<pre>{{arrEdge | json}}</pre>
</div>
</div>
</div>
</div>
</div>
</body>
You can add items to a javascript object using .push(). To make an update, you'll have to loop through the object until you find a matching id (or some other value).
Finally, you can use .appendTo() to update the section that indicates what changes have been made.
Example:
$('#addNode').click(function () {
// pseudo code to add item to javascript object:
// 'node' would be presumably nested based on the OP,
// but information on js object name is not provided
var nodeId = $('#idNode').val();
var nodeLabel = $('#label').val();
var nodeLevel = $('#level').val();
nodes.push({
"id": nodeId,
"label": nodeLabel,
"level": nodeLevel,
});
// psudeo code for updating js object
// loop through object to find matching id:
var locate = 0;
for (var i = 0; i < nodes.length; i++) {
if(node[i].id == nodeId){
locate = i;
node[i].label = nodeLabel;
node[i].level = nodeLevel;
}
}
// Adding results to element
var nodesElem = $('#nodes');
var addElem = "<pre> { newNode | " + JSON.stringify(node[locate]) + " }";
addElem.appendTo(nodesElem);
});
If you need it to be able to change existing records try :
$scope.graph.data.nodes[$scope.newNode.id] = $scope.newNode;
and
$scope.graph.data.edges.push($scope.newEdge);
These methods function calls should update the existing data with the newNode and the newEdge
Often times I want to deal with both arrays and single objects in the same fashion. For example I have the property of an object that can either be an array or just a string (look at the scale property):
[
{
"name": "Experiment type14",
"id": "00000000014",
"scale": ["Whole Brain", "Cell"],
},
{
"name": "Experiment type15",
"id": "00000000015",
"scale": "Cell",
}
]
What I want is to show my scale like here:
<span ng-repeat="scale in experimentType.scale">
<!--some decoration here--> {{scale}}
</span>
Of course this won't work for a single string values. Is there any elegant way not to worry whether I'm dealing with a string or with an array?
You can create your custom filter, please see below
var app = angular.module("app", []);
function MainCtrl() {
this.message = "Welcome";
this.data = [{
"id": "00000000014",
"name": "Experiment type14",
"scale": ["Whole Brain", "Cell"],
},{
"id": "00000000015",
"name": "Experiment type15",
"scale": "Cell",
}];
}
function toArray() {
return function(input) {
console.log(input);
if (angular.isArray(input)) {
return input;
} else {
return [input];
}
};
}
angular.module("app").controller("MainCtrl", MainCtrl);
angular.module("app").filter('toArray', toArray);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="MainCtrl as vm">
<div class="container">
<h3>{{ vm.message }}</h3>
<div ng-repeat="item in vm.data">
<p>{{ item.name }}</p>
<ul>
<li ng-repeat="scale in item.scale | toArray">{{ scale }}</li>
</ul>
</div>
</div>
</div>
</body>
That's my best try. I just implement the following function in the scope:
$scope.toArrayIfNot = function (input) {
if (Object.prototype.toString.call(input) === '[object Array]') {
return input;
}
return [input];
};
I then use it like that:
<span ng-repeat="scale in toArrayIfNot(experimentType.scale)">
<!--some decoration here--> {{scale}}
</span>