I am trying to bind data from a web service and then use that data to pre-populate a form. All form controls are binding correctly except for a single multi-select element. If I manually select an option the model does update. Below is my controller:
myApp.controller('AdminVideosEditCtrl', [
'$scope',
'$http',
'$routeParams',
'$location',
function($scope, $http, $routeParams, $location) {
$http.get('/videos/' + $routeParams.videoId + '?embed=presenters').success(function(data) {
$scope.video = data.data
// Load providers
$http.get('/providers').success(function(data) {
$scope.providers = data.data;
// Load Presenters
$http.get('/presenters').success(function(data) {
$scope.presenters = data.data;
});
});
});
}
]);
Once the final request returns, my model looks like this (output via {{ video | json }}):
{
"id": "ca3ca05a-834e-47b1-aaa1-3dbe38338ca9",
"title": "Doloremque iure consequatur quam ea.",
"is_public": false,
"is_visible": true,
"url": "http://someurl.com/",
"provider_id": "1b4d18eb-d56c-41ae-9431-4c058a32d651",
"level_id": "38ed7286-da05-44b9-bfb9-e1278088d229",
"duration": "17:38",
"transcript_file": "rerum-sint-voluptatum.md",
"presenters": [
{
"id": "5111531d-5f2a-45f5-a0c4-4fa3027ff249",
"first_name": "First",
"last_name": "Last",
"full_name": "First Last"
}
],
"provider": {
"id": "1b4d18eb-d56c-41ae-9431-4c058a32d651",
"title": "You Tube"
}
}
Here is how the multi-select looks in my view:
<div class="form-group">
<label for="presenters" class="col-lg-2 control-label">Presenters</label>
<div class="col-lg-10">
<select multiple="multiple" class="form-control" id="presenters" ng-model="video.presenters" ng-options="presenter.full_name for ( id , presenter ) in presenters">
</select>
</div>
</div>
The select element populates correctly, and I would expect for it to default with the "First Last" element selected, however nothing is selected. I know my model is initialized correctly because if I manually select the element nothing in the model changes (if I select a different element it does, but still retains the same structure as it does on initial page load).
I tried adding a $scope.$apply call, and I also tried $scope.$root.$eval(), neither of which worked.
Update
The presenters model (containing all of the presenters) looks like this after it is fetched from the service (names have been changed to protect the innocent):
[
{
"id": "47b6e945-2d4b-44c2-b44b-adb96460864d",
"first_name": "First",
"last_name": "Last",
"full_name": "First Last"
},
{
"id": "5111531d-5f2a-45f5-a0c4-4fa3027ff249",
"first_name": "One",
"last_name": "Two",
"full_name": "One Two"
},
{
"id": "7cb1e44b-2806-4576-80b2-ae730ad356f7",
"first_name": "Three",
"last_name": "Four",
"full_name": "Three Four"
}
]
Solution
Just put this at the bottom of your controller
$scope.video.presenters.forEach(function(obj, idx){
$scope.presenters.forEach(function(presenter, jdx){
if (obj.id === presenter.id) {
$scope.video.presenters = [$scope.presenters[jdx]]
}
});
});
JSFIDDLE
More Robust Solution
This is more robust as you might want to preselect multiple options. This solution pushes each selected option into an array and then assigns it to $scope.video.presenters model
var selectedOptions = [];
$scope.video.presenters.forEach(function (obj, idx) {
$scope.presenters.forEach(function (presenter, idj) {
if (obj.id === presenter.id) {
selectedOptions.push($scope.presenters[idj]);
$scope.video.presenters = selectedOptions;
}
});
JSFIDDLE
Note: Ideally you should be using the id key as the unique for the objects.
This solution assumes and only caters for preselecting one option.
Related
I am getting problem while binding my dropdown value with associative array.
Problem is with track by so like when I don't add track by to my dropdown then I have my binding with dropdown and when I add track by then O am unable to auto select dropdown value.
I want to use track by with ng-options so that angular js doesn't add
$$hashKey and leverage performance benefit associated with track by.
I am not getting why this behaviour is happening.
Note: I only want to bind name of choices like Pizza or burger for each of my $scope.items and not whole object.
Update: As I understand and with so much trying with current data structure of my $scope.items it is not working with ng-options and I want to use ng-options with track by to avoid generating hash key by Angular js. I also tried ng-change as suggested by #MarcinMalinowski but I am getting key as undefined.
So what should be my data structure of $scope.items so that when I need to access any item from my $scope.items? I can access it without doing loop (like we access items from associative array) like how I can access now with correct data structure and using ngoptions only with track by.
var app = angular.module("myApp", []);
app.controller("MyController", function($scope) {
$scope.items = [
{
"title": "1",
"myChoice" :"",
"choices": {
"pizza": {
"type": 1,
"arg": "abc",
"$$hashKey": "object:417"
},
"burger": {
"type": 1,
"arg": "pqr",
"$$hashKey": "object:418"
}
}
},
{
"title": "2",
"myChoice" :"",
"choices": {
"pizza": {
"type": 1,
"arg": "abc",
"$$hashKey": "object:417"
},
"burger": {
"type": 1,
"arg": "pqr",
"$$hashKey": "object:418"
}
}
}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app="myApp" ng-controller="MyController">
<div ng-repeat="data in items">
<div>{{data.title}}
</div>
<select ng-model="data.myChoice"
ng-options="key as key for (key , value) in data.choices track by $index"><option value="">Select Connection</option></select>
</div>
</ul>
The problems in your code are:
1) track by $index is not supported by ngOptions, it will result the value of the option to be undefined(in your case it will be an $index of ngRepeat);
2) track by doesn't work well with object data-sources (it is supposed to be used with array data-sources), from the docs:
trackexpr: Used when working with an array of objects. The result of
this expression will be used to identify the objects in the array.
Of course, you can use ngRepeat to generate option elements, but personally, I would prefer using ngOptions without track by due to the benefits it has over ngRepeat.
UPDATE: Here is the code that illustrates how you can change your initial data-source and use track by to pre-select an option in case the model is an object. But even in the first example console.log() shows that $$hashKey was not added to choices object.
var app = angular.module("myApp", []);
app.controller("MyController", ['$scope', '$timeout', function($scope, $timeout) {
$scope.items = [
{
"title": "1",
"myChoice" :"burger",
"choices": {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
}
},
{
"title": "2",
"myChoice" :"",
"choices": {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
}
}
];
$scope.itemsTransformed = angular.copy($scope.items).map(function(item){
delete item.myChoice;
item.choices = Object.keys(item.choices).map(function(choice){
item.choices[choice].name = choice;
return item.choices[choice];
});
return item;
});
//select an option like an object, not a string
$scope.itemsTransformed[1].myChoice = $scope.itemsTransformed[1].choices[0];
$timeout(function() {
//changes a prop in opts array - options are not-re-rendered in the DOM
//the same option is still selected
$scope.itemsTransformed[1].choices[0].arg = "xyz";
}, 3000);
$scope.selectionChanged =function(key, items){
console.log(items); //as we can see $$hashKey wasn't added to choices props
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app="myApp" ng-controller="MyController">
<p>Without track by:</p>
<div ng-repeat="data in items track by data.title">
<div>{{data.title}} - {{data.myChoice}}</div>
<select ng-model="data.myChoice"
ng-options="key as key for (key , value) in data.choices"
ng-change="selectionChanged(key, items)">
<option value="">Select Connection</option>
</select>
</div>
<hr/>
<p>Using track by name to pre-select an option:</p>
<div ng-repeat="data in itemsTransformed track by data.title">
<div>{{data.title}} - {{data.myChoice}}</div>
<select ng-model="data.myChoice"
ng-options="choice as choice.name for choice in data.choices track by choice.name"
ng-change="selectionChanged(key, itemsTransformed)">
<option value="">Select Connection</option>
</select>
</div>
</ul>
UPDATE 2: A simple example that shows us the fact $$hashKey property is not added to the objects when using ngOptions without track by:
var app = angular.module("myApp", []);
app.controller("MyController", ['$scope', '$timeout', function ($scope, $timeout) {
$scope.items = {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
};
$scope.selectionChanged = function (key, items) {
console.log($scope.items);
};
}]);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController">
<hr/>
<p>Example without track by:</p>
<select ng-model="myChoice"
ng-options="key as key for (key , value) in items"
ng-change="selectionChanged(myChoice, items)">
<option value="">Select Connection</option>
</select>
<hr/>
{{myChoice}}
</div>
UPDATE 3: Final result below (that work with angularjs versions < 1.4, for 1.4+ I would recommend changing the data structure as $scope.itemsTransformed in the first code snippet):
angular.module("myApp", [])
.controller("MyController", ['$scope', function ($scope) {
$scope.items = [
{
"title": "1",
"myChoice": "burger",
"choices": {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
}
},
{
"title": "2",
"myChoice": "",
"choices": {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
}
}
];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController">
<div ng-repeat="data in items track by data.title">
<div>{{data.title}} {{data.myChoice}}</div>
<select ng-model="data.myChoice"
ng-options="key as key for (key , value) in data.choices">
<option value="">Select Connection</option>
</select>
</div>
</div>
ngOptions doesn't create new scope like ngRepeat directive per item therefore you don't need to take care about to get rid of $$hashKey
I would use ng-repeat to iterate on <option> (suppose you don't create long lists):
<select ng-model="data.myChoice">
<option value="">Select Connection</option>
<option ng-repeat="(key , value) in data.choices track by key" ng-value="key" title="{{key}}"
>{{key}}</option>
</select>
Working Demo Fiddle
Take look on this issue: github.com/angular/angular.js/issues/6564 - ng-options track by and select as are not compatible
I believe this issue still exists so suggest you to use ngRepeat with track by instead. For small list there is no performance penalty
ngOptions attribute can be used to dynamically generate a list of elements for the element using the array or object
ngModel watches the model by reference, not value. This is important to know when binding the select to a model that is an object or a collection.
1.If you set the model to an object that is equal to an object in your collection, ngOptions won't be able to set the selection, because the objects are not identical. So by default, you should always reference the item in your collection for preselections, e.g.: $scope.selected = $scope.collection[3]
ngOptions will track the identity of the item not by reference, but by the result of the track by expression. For example, if your collection items have an id property, you would track by item.id.
For Example :
$scope.items = [
{
"title": "1",
"myChoice" :"",
"choices": {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
}
},
{
"title": "2",
"myChoice" :"",
"choices": {
"pizza": {
"type": 1,
"arg": "abc"
},
"burger": {
"type": 1,
"arg": "pqr"
}
}
}
];
From the above 2nd point, track the identity of the item not by reference.
Add keyName of key in the object and track by keyName or track by arg , type.
Track by arg or type :
<select ng-model="data.myChoice"
ng-options="choice as choice.arg for choice in data.choices track by choice.arg">
<option value="">Select Connection</option>
</select>
Or add keyName inside the choice object
$scope.items = $scope.items.filter(function(item){
delete item.myChoice;
item.choices = Object.keys(item.choices).map(function(choice){
item.choices[choice].keyName = choice;
return item.choices[choice];
});
return item;
});
HTML Code:
<div ng-controller="MyCtrl">
<ul>
<div ng-repeat="data in items">
<select ng-model="data.selected"
ng-options="choice as choice.keyName for choice in data.choices track by choice.keyName"
ng-change="selection(data.selected)">
<option value="">Select</option>
</select>
</div>
</ul>
</div>
Demo Link Example
You need to add ng-change and pass/use your ng-model value there to get any property you wish.
<select class="form-control pickupaddress ng-pristine ng-valid ng-touched m-r-sm m-t-n-xs" ng-model="item.pickup_address" tabindex="0" aria-invalid="false" ng-options="add._id as add.nick_name for add in addPerFood[item.food._id] | unique:'nick_name'" ng-change="dropDownSelect(item.pickup_address,allCarts,item,$index)">
My problem is a bit complex, so i will try to explain it as detailed as possible.
I have a directive in a SPA that render their components based on a JSON data that i'm getting from an API. Based on the elements and their types (the JSON is an array of different objects) i'm rendering every object in an specific directive:
Objects Type 1: Renders in a Directive Type 1.
Objects Type 2: Renders in a Directive Type 2.
Objects Type 3: Renders in a Directive Type 3.
Directives Type 1-2-3 are contained in the parent directive and every directive has different controls (select, checkbox). This is a very simple Sketch:
And the "sub-directives":
I'm rendering my elements as follows (Container directive):
<div ng-repeat="element in elementList | customFilter:itemsType1">
<div class="line"></div>
<div class="form-group">
<directivetype1 itemdata="element" modeldata="data"></directivetype1>
</div>
</div>
<div ng-repeat="element in elementList | customFilter:itemsType2">
<div class="line"></div>
<div class="form-group">
<directivetype2 itemdata="element" modeldata="data"></directivetype2>
</div>
</div>
...
And this is the Directive 1 code:
<div class="container-fluid">
<div class="form-group">
<div class="col-xs-6 col-sm-3">
<div class="checkbox">
<label><input type="checkbox"/>{{itemdata.metadata.description}}</label>
</div>
</div>
<div class="col-xs-6 col-sm-3">
<label>Option</label>
<select class="form-control" ng-model="" ng-options="list.id as list.label for list in item.optionData"></select>
</div>
</div>
</div>
My problem goes when i try to attach the model to every element rendered, because of:
The model data comes from another API, in another structure.
I'm iterating the list of controls with ng-repeat, but, when i pass the model data to the sub-directive i'm passing all the possible data (as Array) and i'm not being capable of filter and know what object in that array belongs to an specific view element.
The data has the following structure:
View data:
[
{
"elementA": {
"metadata": {
"id": "001",
"subId": "016",
"description": "Element 1"
},
"optionData": [
{
"id": "5",
"label": "Option 1"
},
{
"id": "6",
"label": "Option 2"
},
{
"id": "7",
"label": "Option 3"
}
]
}
},
{
"elementB": {
"metadata": {
"id": "002",
"subId": "024",
"description": "Element 2"
},
"optionData": [
{
"id": "1",
"label": "Option 1"
},
{
"id": "2",
"label": "Option 2"
},
{
"id": "3",
"label": "Option 3"
}
]
}
}
]
Model data:
[
{
"metadata": {
"id": "002",
"subId": "024",
"description": "Element 2",
"selected": "1"
},
...(Some other data belonging to the model)
},
{
"metadata": {
"id": "001",
"subId": "016",
"description": "Element 1",
"selected": "5"
},
...(Some other data belonging to the model)
},
...
]
As you can see, the only way to correlate both models is with id and subId Fields in the metadata object (because the metadata itself can vary having more or less fields).
QUESTION
How can i filter my model object, based on the view object? My goal is to get the model object that correlates to the view object and pass it to the sub-directive for setting it as the model of the control that i'm rendering at that point.
EDIT:
As cmw pointed out, i've coded a function to correlate every model object with their respective view object, but that object is not reflected in the directive scope. itemdata and modeldata are passed to the directive using a bi-directional scope ('='). I think (but i'm not entirely sure) that, when i pass a function to modeldata the directive is not being capable of setting the returned object. The solution that i've coded based on the cmw answer is as follows:
Directive:
<directivetype1 itemdata="element" modeldata="getModelObject(data)"></directivetype1>
JS (coded in the Ctrl of the parent):
$scope.getModelObject = function(element){
var id = typeof element.metadata === 'undefined' ? null : element.metadata.id;
var subid = typeof element.metadata === 'undefined' ? null : element.metadata.subid;
var modelElement = null;
for (var i = 0; i < $scope.data.length; i += 1){
element = $scope.data[i];
if (modelElement.metadata.id === id && modelElement.metadata.subid === id) return element;
}
return null;
};
But when i try to work in the directive with modeldata i see "null" in FF/Chrome Console.
Any guideline to know what's happening?
Thanks.
EDIT 2:
I've added a version of my code here: http://plnkr.co/edit/xjp1l3PuWczdqYf5LP8q?p=preview. Sadly, in that Plunkr it works as expected but my code does not (i'm expecting to see the output of <h1>{{modeldata}}</h1>). I'm comparing the two versions to see any difference (note that i've included the same AngularJS version that i'm using in my project).
As you've pointed out, I believe the key is simply to make use of the id and subId properties on the meta object.
Something like this would probably work...
<div ng-repeat="element in elementList | customFilter:itemsType1">
<div class="line"></div>
<div class="form-group">
<directivetype1 itemdata="element" modeldata="modelDataFor(element)">
</directivetype1>
</div>
</div>
Then, in your controller, define a function like the following...
$scope.modelDataFor = function (element) {
var id = element.meta.id,
subId = element.meta.subId,
curr;
for (var i = 0; i < $scope.data.length; i += 1) {
curr = $scope.data[i];
if (curr.meta.id === id && curr.meta.subId === subId) {
return curr;
}
}
return null;
}
This seems like the most natural place to pluck out the relative data model object to pass into your nested directives.
I have been following the Angular tutorials, and I am trying to get my JSON data to appear, yet I know I am doing something wrong, but can't figure out the proper method.
I know that somewhere in my app.js my scope is messed up.
How can I display the Family Name of each product?
Here is the layout I have:
app.js
var eloApp = angular.module('eloMicrosite', []);
eloApp.controller('homeListController', ['$scope', '$http',
function($scope, $http) {
$http.get('/Elo/eloMS.min.json')
.success(function(data) {
$scope.products = data;
});
}]);
eloApp.controller('HomeController', function(){
this.products = $scope.products;
});
HTML
<div ng-controller="HomeController as home">
{{home.products[o]["Family Name"]}}
</div>
JSON Layout
{
"products": [
{
"Family Name": "3201L",
"Type": "IDS",
"Size (inches)": 32,
"Aspect Ratio": "16:9",
"Part Number": "E415988",
"Product Description": "ET3201L-8UWA-0-MT-GY-G",
"Marketing Description": "3201L 32-inch wide LCD Monitor",
"Advance Unit Replacement": "",
"Elo Elite": "",
"Package Quantity": 1,
"Minimum Order Quantity": 1,
"List Price": 1800
},
.
.
.
],
"families": [
{
category: "Category1"
},
{
category: "Category2"
}
],
"accessories": [
{
category: "Category1"
},
{
category: "Category2"
}
]
}
You should add homeListController on your page instead of HomeController, Also need to use this instead of using $scope as you wanted to follow controllerAs syntax, 2nd controller is useless in this scenario, you could remove that from app.js.
Markup
<div ng-controller="homeListController as home">
{{home.products[0]["Family Name"]}}
</div>
Controller
eloApp.controller('homeListController', ['$http',
function($http) {
var home = this;
$http.get('/Elo/eloMS.min.json')
.success(function(data) {
home.products = data.products; //products should mapped here
});
}]);
Demo Plunkr
So the overview of the problem; I am retrieving data from an api and creating a CRUD page for it. The data has a set of labels that the user can select.
Below is a code sample representing my problem. The labels selected by the user are represented by the user.labels relationship and the total available labels that can be selected are represented by user.parent.grandparent.labels.
I'm able to sync the selection. What I can't seem to figure out is how to get rid of options that have already been selected from the list of options on any other subsequent select field.
angular.module('app', [])
.controller('select', ['$scope', '$filter', '$location',
function($scope, $filter, $location) {
$scope.user = {
"parent": {
"grandparent": {
"labels": [{
"id": 28,
"name": "Label 1",
}, {
"id": 17,
"name": "Label 2",
}, {
"id": 39,
"name": "Label 3",
}, {
"id": 77,
"name": "Label 4"
}, {
"id": 100,
"name": "Label 5"
}]
}
},
"labels": [{
"id": 28,
"name": "Label 1",
"meta": {
"score": 3
}
}, {
"id": 17,
"name": "Label 2",
"meta": {
"score": 5
}
}]
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="select">
<div ng-repeat="label in user.labels track by $index">
<div class="form-field">
<span>Label</span>
<select ng-model="user.labels[$index]" ng-options="department.name for department
in user.parent.grandparent.labels track by department.id">
</select>
</div>
<div>
<span>Score</span>
<select ng-model="label.meta.score">
<option value="1">1 (lowest)</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5 (highest)</option>
</select>
</div>
</div>
<button ng-click="user.labels.push({})">Add Label</button>
</div>
You can use a filter function inside the ng-repeat to achieve this, here is a sample Codepen showing you how:
http://codepen.io/anon/pen/ZYveOo
You need to pass the filter in the repeat definition:
<select ng-model="user.labels[$index]" ng-options="department.name for department in user.parent.grandparent.labels | filter:removeSelected track by department.id ">
Which refers to this function on scope:
$scope.removeSelected = function(val){
return !_.find($scope.user.labels, function(label) {
return label.id === val.id;
});
};
Even then though I think you are missing one use case which is that you want to be able to have the currently selected label included in the options, by removing all selected options you are removing that ability.
Updated:
Ok then, so after giving this some thought I have come up with the following filter which could be optimised but does seem to work as expected:
.filter('duplicatesFilter', function() {
return function(items, index, selected) {
var res = [selected[index]];
_.forEach(items, function(item){
if(!_.find(selected, function(label) {
return label.id === item.id;
})){
res.push(item);
}
});
return res;
};
})
Use it like so:
<select ng-model="user.labels[$index]" ng-options="department.name for department in user.parent.grandparent.labels | duplicatesFilter:$index:user.labels track by department.id "></select>
This is something I have hit a few times and each time I've worked around it. I'll take a look later if I can find a custom filter that better solves the problem and if I can't I'll tidy up this code and release one; however this should be good to go for your use-case.
Working code-pen:
http://codepen.io/anon/pen/ZYveOo
I am reading the below json value from a module.js
.controller('home.person',['$scope','$filter','personResource',function($scope,$filter,personResource) {
$scope.searchPerson = function() {
var params = $scope.search || {};
params.skip=0;
params.take =10;
$scope.personDetails =
{
"apiversion": "0.1",
"code": 200,
"status": "OK",
"mydata": {
"myrecords": [
{
"models": [
{
"name": "Selva",
"dob": "10/10/1981"
}
],
"Address1": "ABC Street",
"Address2": "Apt 123",
"City": "NewCity1",
"State": "Georgia"
},
{
"models": [
{
"name": "Kumar",
"dob": "10/10/1982"
}
],
"Address1": "BCD Street",
"Address2": "Apt 345",
"City": "NewCity2",
"State": "Ohio",
"Country":"USA"
},
{
"models": [
{
"name": "Pranav",
"dob": "10/10/1983"
}
],
"Address1": "EFG Street",
"Address2": "Apt 678",
"City": "NewCity3",
"State": "NewYork",
"Country":"USA",
"Zipcode" :"123456"
}
]
}
}
}
}])
Now i am able to statically build the UX. But my each record set's key value pair count is different. So i want to build my html dynamically as per the current record set's count.Country & Zipcode is not exist in all records so i need to build dynamically the build and populate the html output.Most of the time, my json output is dynamic. Instead of persondetails, i may get the json output of a product details instead of PersonDetails.
<div ng-show="personDetails.mydata.myrecords.length > 0" ng-repeat="recordSingle in personDetails.mydata.myrecords">
<div >
<span >Address1: {{recordSingle.Address1}}</span>
<span >Address2: {{recordSingle.Address2}}</span>
<span>City: {{recordSingle.City}}</span>
<span>State: {{recordSingle.State}}</span>
<span>Country: {{recordSingle.Country}}</span>
<span>Zipcode: {{recordSingle.Zipcode}}</span>
</div>
</div>
One way is to use ng-if statement, for the optional span elements:
<span ng-if="recordSingle.Address1">Address1: {{recordSingle.Address1}}</span>
[Update #1: updated based on revised comments to question]
[Update #2: fixed typos in function and included plunkr]
I now understand that you want to dynamically build the display objects based on properties from the JSON object. In this case, I would iterate through the properties of the object. I would use a function to produce this array of properties for each object so that you can filter out any prototype chains. I would also remove out any unwanted propoerties, such as the internal $$hashKey and perhaps the array objects e.g.
In your controller:
$scope.getPropertyNames = getPropertyNames;
function getPropertyNames(obj) {
var props = [];
for (var key in obj) {
if (obj.hasOwnProperty(key) && !angular.isArray(obj[key]) && key !== '$$hashKey') {
props.push(key);
}
}
return props;
}
Then in your HTML view:
<div ng-repeat="record in personDetails.mydata.myrecords">
<div ng-repeat="prop in getPropertyNames(record)">
<span ng-bind="prop"></span>: <span ng-bind="record[prop]"></span>
</div>
</div>
This works for me... see this plunker. It is displaying each of the properties of the object in the array dynamically (you could have any property in the object). Is this not what you are trying to achieve?