Insert dynamically a JSON items using Angular JS and Ionic - javascript

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

Show the second value based on the first one in Angularjs

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/

ng-repeat splice showing double

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>

Angular Js Object Filter in ng-repeat

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.

Update json file based on input values using AngularJS

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

ng-repeat over a non-array/object / dealing with arrays and non-arrays the same way

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>

Categories