In AngularJS, the data is showing up only in ng-repeat - javascript

I've got the following getUserData.js:
var App = angular.module('App', []);
App.controller('UserdataController', function($scope, $http) {
$http.get('data/getUserData.php')
.then(function(res){
$scope.users = res.data;
});
});
Which works perfectly, a Json is created inside the getUserData.php file, nothing wrong with that.
But when I want to display it inside the index.php file, I only get it to work using ng-repeat, like this:
<div ng-controller="UserdataController">
<ul>
<li ng-repeat="user in users">
{{user.voornaam}}
</li>
</ul>
</div>
But how can I display this data ("voornaam" is dutch for first name) without using ng-repeat? I thought it should be something like this, but it doesn't work..
<div ng-controller="UserdataController">
{{users.voornaam}}
</div>
Tried for several hours now, and searched all over the place. If someone can tell me what I'm doing wrong, that would be great!!

Well that's because users.vornaam just does not exists. However instead of showing an error Angular will just show a "blank value".
Check your object strucutre to understand your error. It will be something like
Users {
0:{ vornaam: xyz, ...}, //this is a single "user" which has a vornaam attribute
1:{ vornaam: xyz, ...},
2:{ vornaam: xyz, ...}
}
As you can see your Users object/array itself just does not have a "vornaam" but consists of several objects which then have the requested attribute.
In the above example
Users.vornaam
will be undefined. But if you try to access it like
Users[0].vornaam
it will work.

Related

Trouble using ng-repeat in AngularJS to render HTML values

I came across this post, which entails more or less exactly what I want to do: AngularJS - Is it possible to use ng-repeat to render HTML values?
As of right now, the code looks like this, rendering correctly as text:
<div ng-repeat="item in items.Items">
{{item}}<br>
</div>
I want this to render as HTML now, so taking from that post, I have it looking something like this:
<div ng-repeat="item in items.Items" ng-bind-html-unsafe="item">
<br>
</div>
However, nothing renders as a result. Does anyone know why that is? I have also tried setting ng-bind-html-unsafe to "{{item}}", which had no effect
You should use ng-bind-html,
and you could do that:
$scope.myHTML = $sce.trustAsHtml('<b>some</b> html');
See the updated fiddle.
In your case use some function or pass the array in the controller first:
$scope.getHtml = function(v){
return $sce.trustAsHtml(v);
};
It is always a good practice to mark the html that one is binding from the .js file to the .html which are in string format, as safe.
One way, it can be achieved is by making use of AngularJS's dependency injection with $sce. One can read more about it, in length here.
I have created a demo using a list, since you have shared only the HTML but not the JS, so I have assumed one and illustrated how to make use of it by trusting the HTML string using $sce.trustAsHtml().
Refer demo.
Please have a look at the code below:
HTML:
<div ng-app="app" ng-controller="test">
<div ng-repeat="item in items" ng-bind-html="item">
</div>
</div>
JS:
var app = angular.module('app', []);
app.controller('test', function($scope, $sce) {
$scope.data = [{
"Items": [{
"data": "<p>Item 1</p>"
}, {
"data": "<p>Item 2</p>"
}, {
"data": "<p>Item 3</p>"
}, {
"data": "<p>Item 4</p>"
}]
}];
$scope.items = [];
angular.forEach($scope.data[0].Items, function(v, k) {
$scope.items.push($sce.trustAsHtml(v.data));
});
});

dynamic routing with array item in angularjs after filtering array

I have a problem with my angularjs app where my app is routing to the wrong page when using an ng-repeat array to determine the route.
data looks like this and is accessed in the person controller:
[
{
"name":"AJ lastname",
"img_name":"AJ_lastname",
"location":"Baltimore, Maryland",
"info":"stuff"
},
{
"name":"Albert lastname",
"img_name":"Albert_lastname",
"location":"Boston, Massachusetts",
"info":"stuff"
} // ... more data
]
html: (the anchor tag links to the person based on their index in the array (I believe this may be what I need to change to fix the problem, but I'm not sure)
<ul class="main-list">
<li class="list-item fade" ng-repeat="student in students | filter:filter">
<a href="/#person/{{$index}}">
<img class="portrait listimg" ng-src="/images/{{student.img_name}}.jpg" alt="portrait of {{student.name}}">
<h2>{{student.name}}</h2>
<h4>{{student.location}}</h4>
</a>
</li>
</ul>
Routing from angular: (the route with '/person/:itemId' is routing to a page specific to a specific user, where their index in the array determines their id)
app.config(function ($routeProvider, $httpProvider) {
$routeProvider
.when('/list', {
templateUrl: './js/templates/list.html',
controller: 'ListController'
})
.when('/person/:itemId', {
templateUrl: './js/templates/person.html',
controller: 'PersonController'
})
.otherwise('/list');
});
Here is the controller for the dynamic page. It works perfectly for the original array, but once I attempt to sort the array, the index no longer corresponds to the correct student.
app.controller('PersonController', function ($scope, $http, $routeParams) {
$scope.person = 'Someone\'s name';
$http.get('../js/students.json').success(function (data) {
$scope.allStudents = data;
$scope.studentId = $routeParams.itemId;
$scope.student = data[$scope.studentId];
});
So the functional problem is that the index applies to the first student in the large array of data. It appears to work perfectly, and the correct data populates the page, but when I use the html/text input to filter the list, the original indices are updated on the html side, and they do not correspond to the original array. So the routing sends them to the wrong page.
How can I make the routing work even for a filtered list?
One way you can do this is by using a function which returns you the index a student had in the original array for each student in your ng-repeat.
$scope.getIndex = function(student) {
return $scope.students.indexOf(student);
}
You can then call the function in your list like:
<a ng-href="/#person/{{getIndex(student)}}">
This though is not quite the most performant code you could imagine.
Another way would be to just temporarily store the index of the student as a property and use that one to reference it, again not quite the nicest solution:
$scope.students = $scope.students.map(function(student, index) {
student.index = index;
return student;
});
And in the list:
<a ng-href="/#person/{{student.index}}">
However, if you can somehow assign the students a unique id that would definitely be the preferred way. That way you also make sure that you always reference the same student. If your students.json somehow changes between the time you create the list and the time the user clicks on an item you may reference the wrong one again...
By the way always use ng-href when including placeholders in the link. Why you should do so is well described in the Angular API docs:
Using Angular markup like {{hash}} in an href attribute will make the link go to the wrong URL if the user clicks it before Angular has a chance to replace the {{hash}} markup with its value. Until Angular replaces the markup the link will be broken and will most likely return a 404 error. The ngHref directive solves this problem.
You are creating the ng-repeat using some object on $scope called students, correct? If this is being built from the same students.json as in your controller then their student ids should logically be equivalent. So just change the href from "/#person/{{$index}}" to "/#person/{{student.studentId}}".
If for some reason they aren't the same then when you create the students object you can add a new attribute, studentId, which holds the value of their index in the array and then use the previous suggestion.
Just remember that when using ng-repeat if you have identical objects it'll throw an error so you have to add the "track by $index" to it.

Using trustAsHTML for an array of Objects

I'm attempting to feed an array of objects from an Angular Controller to an ng-repeat directive.
The objects returned in the array have several properties, a few of which may contain HTML that needs to be output by the result of ng-repeat. I can't seem to figure out how to trustAsHTML the entire object that is returned.
My view looks like this:
<li ng-repeat="user in searchedUsers" ng-bind-html="user">
I've attempted it like this:
$scope.searchedUsers = data;
for(var user in $scope.searchedUsers){
$sce.trustAsHtml($scope.searchedUsers[user].matched);
$sce.trustAsHtml($scope.searchedUsers[user].unmatched);
}
And also attempted structuring my directives like this:
<li ng-repeat="user in searchedUsers"><span ng-bind-html="user.matched">{{user.matched}}</span> <span ng-bind-html="user.unmatched">{{user.unmatched}}</span></li>
But I get the error back:
Error: [$sce:unsafe] Attempting to use an unsafe value in a safe context.
The JSON object I'm using is as follows:
[{"id":2,"name":"Jonny","email":"jonnyasmar#me.com","created_at":"2015-10-25 00:58:10","updated_at":"2015-10-25 02:11:59","matched":"jonny<span class=\"match\">as<\/span>mar#me.com","unmatched":"Jonny"}]
Any idea how this can be accomplished or do I need to rethink my implementation?
You need to include ngSanitize js and dependency:
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-sanitize.min.js"></script>
and, on module:
angular.module('yourModule', ['ngSanitize'])
after this, the ng-bind-html will work. For example:
<div ng-bind-html="user.matched"></div>
take a look at complete code on jsbin

Using dynamic layouts from json response: angularJS

In my web app, in a search result, each item can have a different look according to the type of item. To achieve this, I was thinking of including the layout with data placeholders for that particular item in the json response. How do I bind the scope data to the placeholders in the json response template?
for eg.
My search result would look something like :
<li ng-repeat="item in items">
<div ng-bind-html="item.template"></div>
</li>
And the JSON response would look like :
[{
"template_name":"One",
"template":"<div ng-repeat='subitem in item.subitems'>{{subitem.name}}</div>",
"subitems":[{name:"John"},{name:"Jane"}]
},
{
"template_name":"Two",
"template":"<div ng-repeat='subitem in item.subitems'>{{subitem.name}}</div>",
"subitems":[{name:"John"},{name:"Jane"}]
}
]
Like you can see, I need to bind the placeholders in the json response with the scope data. Is there any way to do it? If not, how do you suggest i should address this situation?
Thank You!
While this will obviously not work:
<li ng-repeat="item in items">
<div ng-bind-html="item.template"></div>
</li>
Since you are telling angular to display html that is inside item.template but what you really want is to tell it to treat item.template as it would be markup that should be parsed in a similar way to a template inside directive.
What you can do however is to define custom directive that will treat item.template in a desired manner. For instance:
<li ng-repeat="item in items">
<div dynamic-template="item.template" />
</li>
Where dynamic-template is defined as follows:
module.directive('dynamicTemplate', function($compile){
return {
scope: {
dynamicTemplate:'='
},
replace:true,
transclude: true,
link: function($scope, $element, $attrs, _, $transcludeFn){
// since we have isolate scope - transclude the element so it has access
// to item and anything else available inside ng-repeat
$transcludeFn(function($childElement, $childScope){
var link = $compile($scope.dynamicTemplate);
var bound = link($childScope);
$element.append(bound);
});
}
};
});
Of course this is not ideal since changes to item.template wont be reflected but you can easily extend this by adding $watch.
You can find working example here.

How to access a specific element in ng-repeat generated collection with angularJS

I was trying to generate a list of items using ng-repeat. however, now I've done this step easily but I got stuck with another issue. so I was trying to access and update a specific element in the collection but I really can't figure out how to do this. Does anyone know how to do this? or can point me to the right direction?
here's a fiddle sample: http://jsfiddle.net/VNLPY/ (haven't done much really)
<body ng-app>
<div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
I have {{friends.length}} friends. They are:
<ul>
<li ng-repeat="friend in friends">
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
</li>
</ul>
</div>
</body>
one more thing, lets say the data I have is getting pulled from a JSON file. Now how can I access each value of each row inside the collection and if I update a specific row in JSON object. does the interface gonna be in-sync with the updated JSON?
Thanks
This fiddle has an example of incrementing the age of a specific friend via an expression in ng-click as well as ng-click calling a scope function in a simple controller.
Age + 1
Age - 1
With a controller defined as:
function TestController ($scope) {
$scope.decrementAge = function (friend) {
friend.age = friend.age - 1;
};
}
Angular will handle keeping the model and the view in sync.
You need to structure your app properly first. Angular Seed is a good starting point.
Once you have a proper controller & view you can use the $scope to achieve what you're asking.
NOT TESTED
Controller:
angular.module('myApp').controller('FriendsController', function($scope, $http) {
$scope.doSomethingWithAFriend = function(friend) {
// do something with your friend!
}
// $http is almost the same as jQuery.ajax except all the methods
// return promises based on the $q (q.js) spec.
$http.get('/api/friends').then(function(resp) {
$scope.friends = resp.data;
}, function(err) {
console.log(err);
});
});
View
<li ng-repeat="friend in friends">
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
Do Something
</li>
Edit: I should mention the proper place to do API calls (or any AJAX/data manipulation) is in Services. I just put it in the controller as a simple example.

Categories

Resources