I have offers table and users table on parse server. I did a query for he offers table and it worked great (both console log and html - I had issues with async and the Q.promise helped). Now I'm trying to add two elements that are in the users table. I get it on the console, but not on the page. Here is what I have on the offers.service:
this.getAllOffers = function () {
var Q = $q.defer();
console.log('getAllOffers called');
//all offers filter is selected
this.allOffersFilter = false;
var offers = Parse.Object.extend("Offer");
var exchanges = Parse.Object.extend("Exchanges");
var users = Parse.Object.extend("User");
var query = new Parse.Query(offers);
var userQuery = new Parse.Query(users);
var results = [];
query.descending("createdAt");
query.limit(4);
userQuery.find().then(function(users) {
for (i = 0; i < users.length; i++) {
foundUsers = users[i];
query.find().then( function(offers){
for(i = 0; i < offers.length; i++){
found = offers[i];
var result = {};
result.date = found.get("createdAt");
result.price = found.get("price");
result.status = found.get("accepted");
result.lastName = foundUsers.get("lastName");
result.companyName = foundUsers.get("companyName");
console.log(result.companyName);
console.log(result.price);
}
});
results.push(result);
}
Q.resolve(results);
});
return Q.promise;
};
Then my HTML:
<!--List of offers-->
<div class="col-md-3">
<h4>List of offers</h4>
<div ng-if="offersList">
<div ng-repeat="offer in offersList">
<div class="offer card">
<div>{{offer.username}}</div>
<div>{{offer.companyName}}</div>
<div>{{offer.date}}</div>
<div>{{offer.price}}</div>
<div>{{offer.status}}</div>
</div>
</div>
</div>
<div ng-if="!(offersList)">There are no offers</div>
</div>
Then my component:
angular.module('offersPage')
.component('offersPage', {
templateUrl: 'pages/offers-page/offers-page.template.html',
controller: function(AuthService, PageService, OffersService,
$scope) {
// Functions for offers-page
// Check if user is logged in and verified on page load
AuthService.userLoggedin(function(loggedIn, verified) {
if(!verified) {
PageService.redirect('login');
}
});
this.$onInit = function() {
OffersService.getAllOffers().then(function(offersList) {
$scope.offersList = offersList;
});
}
}
});
THANKS IN ADVANCE !
You are resolving $q before results is populated, so, you list is empty.
I don't know about Parse server, but if userQuery.find().then is async, then need to move Q.resolve(results); inside it, or probably inside query.find().then.
When you do an ng-if in angularjs it literally takes out the element and when it puts it in it is as a child scope. To fix this you need to make sure and put $parent on any child element inside an ng-if. See below. Make sure to use track by $index to when you are doing repeats its good practice. Also notice you dont need to $parent anything in the repeat since it is referencing offerwhich is defined.
Code:
<div ng-if="offersList">
<div ng-repeat="offer in $parent.offersList track by $index">
<div class="offer card">
<div>{{offer.username}}</div>
<div>{{offer.companyName}}</div>
<div>{{offer.date}}</div>
<div>{{offer.price}}</div>
<div>{{offer.status}}</div>
</div>
</div>
</div>
Related
It is not like it is slow on rendering many entries. The problem is that whenever the $scope.data got updated, it adds the new item first at the end of the element, then reduce it as it match the new $scope.data.
For example:
<div class="list" ng-repeat="entry in data">
<h3>{{entry.title}}</h3>
</div>
This script is updating the $scope.data:
$scope.load = function() {
$scope.data = getDataFromDB();
}
Lets say I have 5 entries inside $scope.data. The entries are:
[
{
id: 1,
title: 1
},
{
id: 2,
title: 2
},
......
]
When the $scope.data already has those entries then got reloaded ($scope.data = getDataFromDB(); being called), the DOM element for about 0.1s - 0.2s has 10 elements (duplicate elements), then after 0.1s - 0.2s it is reduced to 5.
So the problem is that there is delay about 0.1s - 0.2s when updating the ng-repeat DOM. This looks really bad when I implement live search. Whenever it updates from the database, the ng-repeat DOM element got added up every time for a brief millisecond.
How can I make the rendering instant?
EDITED
I will paste all my code here:
The controller:
$scope.search = function (table) {
$scope.currentPage = 1;
$scope.endOfPage = false;
$scope.viewModels = [];
$scope.loadViewModels($scope.orderBy, table);
}
$scope.loadViewModels = function (orderBy, table, cb) {
if (!$scope.endOfPage) {
let searchKey = $scope.page.searchString;
let skip = ($scope.currentPage - 1) * $scope.itemsPerPage;
let searchClause = '';
if (searchKey && searchKey.length > 0) {
let searchArr = [];
$($scope.vmKeys).each((i, key) => {
searchArr.push(key + ` LIKE '%` + searchKey + `%'`);
});
searchClause = `WHERE ` + searchArr.join(' OR ');
}
let sc = `SELECT * FROM ` + table + ` ` + searchClause + ` ` + orderBy +
` LIMIT ` + skip + `, ` + $scope.itemsPerPage;
sqlite.query(sc, rows => {
$scope.$apply(function () {
var data = [];
let loadedCount = 0;
if (rows != null) {
$scope.currentPage += 1;
loadedCount = rows.length;
if (rows.length < $scope.itemsPerPage)
$scope.endOfPage = true
for (var i = 0; i < rows.length; i++) {
let item = rows.item(i);
let returnObject = {};
$($scope.vmKeys).each((i, key) => {
returnObject[key] = item[key];
});
data.push(returnObject);
}
$scope.viewModels = $scope.viewModels.concat(data);
}
else
$scope.endOfPage = true;
if (cb)
cb(loadedCount);
})
});
}
}
The view:
<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
<div class="row note-list" ng-if="showList">
<h3>Notes</h3>
<input ng-model="page.searchString" id="search"
ng-keyup="search('notes')" type="text" class="form-control"
placeholder="Search Notes" style="margin-bottom:10px">
<div class="col-12 note-list-item"
ng-repeat="data in viewModels track by data.id"
ng-click="edit(data.id)"
ontouchstart="touchStart()" ontouchend="touchEnd()"
ontouchmove="touchMove()">
<p ng-class="deleteMode ? 'note-list-title w-80' : 'note-list-title'"
ng-bind-html="data.title"></p>
<p ng-class="deleteMode ? 'note-list-date w-80' : 'note-list-date'">{{data.dateCreated | displayDate}}</p>
<div ng-if="deleteMode" class="note-list-delete ease-in" ng-click="delete($event, data.id)">
<span class="btn fa fa-trash"></span>
</div>
</div>
<div ng-if="!deleteMode" ng-click="new()" class="add-btn btn btn-primary ease-in">
<span class="fa fa-plus"></span>
</div>
</div>
<div ng-if="!showList" class="ease-in">
<div>
<div ng-click="back()" class="btn btn-primary"><span class="fa fa-arrow-left"></span></div>
<div ng-disabled="!isDataChanged" ng-click="save()" class="btn btn-primary" style="float:right">
<span class="fa fa-check"></span>
</div>
</div>
<div contenteditable="true" class="note-title"
ng-bind-html="selected.title" id="title">
</div>
<div contenteditable="true" class="note-container" ng-bind-html="selected.note" id="note"></div>
</div>
</div>
<script src="../js/pages/note.js"></script>
Calling it from:
$scope.loadViewModels($scope.orderBy, 'notes');
The sqlite query:
query: function (query, cb) {
db.transaction(function (tx) {
tx.executeSql(query, [], function (tx, res) {
return cb(res.rows, null);
});
}, function (error) {
return cb(null, error.message);
}, function () {
//console.log('query ok');
});
},
It is apache cordova framework, so it uses webview in Android emulator.
My Code Structure
<html ng-app="app" ng-controller="pageController">
<head>....</head>
<body>
....
<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
....
</div>
</body>
</html>
So there is controller inside controller. The parent is pageController and the child is noteController. Is a structure like this slowing the ng-repeat directives?
Btw using track by is not helping. There is still delay when rendering it. Also I can modify the entries as well, so when an entry was updated, it should be updated in the list as well.
NOTE
After thorough investigation there is something weird. Usually ng-repeat item has hash key in it. In my case ng-repeat items do not have it. Is it the cause of the problem?
One approach to improve performance is to use the track by clause in the ng-repeat expression:
<div class="list" ng-repeat="entry in data track by entry.id">
<h3>{{entry.title}}</h3>
</div>
From the Docs:
Best Practice: If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance, e.g. item in items track by item.id. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones. For large collections, this significantly improves rendering performance.
For more information, see
AngularJS ngRepeat API Reference -- Tracking and Duplicates
In your html, try this:
<div class="list" ng-repeat="entry in data">
<h3 ng-bind="entry.title"></h3>
</div>
After thorough research, I found my problem. Every time I reset / reload my $scope.viewModels I always assign it to null / empty array first. This what causes the render delay.
Example:
$scope.search = function (table) {
$scope.currentPage = 1;
$scope.endOfPage = false;
$scope.viewModels = []; <------ THIS
$scope.loadViewModels($scope.orderBy, table);
}
So instead of assigning it to null / empty array, I just replace it with the new loaded data, and the flickering is gone.
Hi i having problem when i retrieve data from firebase in console.log($scope.statusbaca) it display 2 query true and false. but when it showing in the app it only display false. Sorry for my bad english. English is not my native language. Hope you understand my problem
App return false but console log display true and false
and this is my code
Template
<div ng-repeat= "item in users">
<div class="list card" style="padding:1%;">
<div class="col-50" style="float:left;">
<h5>{{item.displayName}} - {{item.handphone}}</h5>
<h5>{{item.email}}</h5>
</div>
<div class="col-33" style="float:right; position: relative;" ng-repeat = "datas in statusbaca">
<h5>{{datas}}</h5>
</div>
</div>
</div>
Controller
var rootRef = new Firebase('https://example-app.firebaseio.com/');
var childUsers = rootRef.child('users');
var childDate = rootRef.child('tanggal');
var rekapId = $stateParams.rekapId;
console.log(rekapId);
childDate.child(rekapId).child('tanggalBaca').once('value',function(snap){
$timeout(function() {
var snapshot= snap.val();
$scope.tanggal = snapshot;
console.log($scope.tanggal);
myAfterFunction();
});
})
function myAfterFunction(){
var dates = $scope.tanggal;
console.log(dates);
var rekapUsers = childUsers.on('value', function(snapshot){
snapshot.forEach(function(childSnapshot){
var key = childSnapshot.key();
console.log(key);
var childStatus = childUsers.child(key+'/status/'+dates);
childStatus.on('value',function(grandsnap){
var snapval =grandsnap.val();
$scope.statusbaca = snapval;
$scope.key = key;
console.log($scope.statusbaca);
console.log($scope.key);
})
})
var snapshotVal = snapshot.val();
$scope.users =snapshotVal;
console.log($scope.users);
})
}
Any idea what's wrong with my code and how to fix this? thanks
I'm not sure if this will fix it but your div elements are unbalanced.
You should add a ending div element like this,
<div class="col-33" style="float:right; position: relative;" ng-repeat = "datas in statusbaca">
<h5>{{datas}}</h5>
</div>
Also, I can't find some of the variables referenced by the HTML in the JavaScript and rather than having your
value output like this:
<h5>{{datas}}</h5>
you should have it like this:
<h5>{{datas.THE NAME OF YOUR KEY:VALUE PAIR}}</h5>
I can't test it since you're using firebase but, I would do quick run through with your code.
I followed information on this answer
But it doesn't work in my situation.
Chrome Inspector console says "ReferenceError: dataResponse is not defined"
maybe that is the problem?
I am trying to GET this JSON from url:
[{"app_id":1,"app_name":"oh yeeah","app_description":"desc","app_price":111,"is_activated":false,"video":"videolink"},{"app_id":2,"app_name":"oh yeaaaeah","app_description":"ewaewq","app_price":222,"is_activated":false,"video":"fuck off"},{"app_id":3,"app_name":"oh yeaaaeah","app_description":"ewaewq","app_price":333,"is_activated":false,"video":"fuck off"}]
This is my javascript code
var appstore = angular.module('appstore', []);
appstore.service('dataService', function($http) {
delete $http.defaults.headers.common['X-Requested-With'];
this.getData = function(callbackFunc) {
$http({
method: 'GET',
url: '/administrator/components/com_apps/loadAppsJson.php'
}).success(function(data){
callbackFunc(data);
}).error(function(){
alert("error");
});
}
});
appstore.controller('app_Ctrl', function($scope, dataService) {
$scope.apps = [
{app_id:1, app_name:'oh yeah', app_description:'$app_description', app_price:111, is_activated:false, video:'$videolink'},
{app_id:2, app_name:'oh yeah', app_description:'$app_description', app_price:111, is_activated:false, video:'$videolink'},
{app_id:3, app_name:'oh yeah', app_description:'$app_description', app_price:111, is_activated:false, video:'$videolink'},
];
//$scope.apps = null;
dataService.getData(function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
});
console.log(dataResponse);
console.log($scope.apps);
//get images thumbs
for(app = 0; app <= $scope.apps.length-1; app++) {
$scope.apps[app].thumb = ("000" + $scope.apps[app].app_id).slice(-3);
}
//separate apps to columns
var columns = [];
for (var i = 0; i < $scope.apps.length; i++ ) {
if (i % 3 == 0) columns.push([]);
columns[columns.length-1].push($scope.apps[i]);
}
$scope.columns = columns;
});
My HTML view
<div ng-controller="app_Ctrl">
<div class="row"></div>
<div class="row">
<div class="row" ng-repeat="apps in columns">
<div id="app_id_{{ app.app_id }}" class="col-lg-4" ng-repeat="app in apps | filter:search">
<div class="thumbnail" ng-class="app.is_activated ? 'activated' : ''">
<!-- -->
<img ng-src="/images/apps/app_images/{{ app.thumb }}_thumb.jpg" alt="{{ app.app_name }}" title="{{ app.app_name }}">
<div class="caption">
<h3>{{ app.app_name }}</h3>
<p class="app_price">{{ app.app_price }} €</p>
<div style="clear:both;"></div>
<p class="app_card_description">{{ app.app_description | limitTo:100 }}...</p>
Info
Video <span class="glyphicon glyphicon-facetime-video"></span>
{{ app.is_activated ? 'Aktivované' : 'Aktivovať' }}
</div>
</div>
</div>
</div>
To elaborate on what #Mritunjay said in the comments; review this code with comments:
dataService.getData(
// this is your callback function which has an argument for dataResponse
// the dataResponse variable will only be defined within the Call back function
function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
// The Curly Brackets that follow mark the end of the callback handler method
});
// This log statement is not in the callback handler and there is no defined dataResponse variable which is probably why you got an error in the console
console.log(dataResponse);
You can fix this by moving the dataResponse log into the callback method, like this:
dataService.getData(function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
console.log(dataResponse);
});
There appear to be other problems with your code, in that you are trying to access $scope.apps before the data is returned; which will hinder your processing. Easiest approach would be to move that processing into the result handler:
// define $scope.columns outside of the result handler
$scope.columns = [];
// call to method in service
dataService.getData(function(dataResponse) {
$scope.apps = dataResponse;
alert(dataResponse);
console.log(dataResponse);
// inside the result handler; you run this code after $scope.apps is defined:
for(app = 0; app <= $scope.apps.length-1; app++) {
$scope.apps[app].thumb = ("000" + $scope.apps[app].app_id).slice(-3);
}
//separate apps to columns
var columns = [];
for (var i = 0; i < $scope.apps.length; i++ ) {
if (i % 3 == 0) columns.push([]);
columns[columns.length-1].push($scope.apps[i]);
}
$scope.columns = columns;
});
That's what promises and asynchronous calls are all about.
console.log(dataResponse);
console.log($scope.apps);
The first one won't work because dataResource is a private variable and not part of the same scope you're trying to print.
The second one won't work either because that get's populated at future time (after X seconds), after the $http request is finished so it will only be availableat that point.
One way to do something after the object is populated is to use
$scope.$watch("apps", function (){
// do stuff
});
I am attempting to create infinite scrolling on my web page using an example I found. However, the page fills up completely with all the items instead of just showing several items at a time. In other words it is not doing infinite scrolling. I noticed in some of the examples they parsed out data in chunks but in the real world how are you supposed to do that?
Below is my html code:
<table class="table table-striped table-bordered"><tr>
<th style="text-align:center;">User ID</th> <th>Username</th><th>Rank</th>
<th>Posts</th><th>Likes</th> <th>Comments</th> <th>Flags</th><th>Status</th><th>Action</th></tr>
<tr><td class="center">
<div ng-app='scroll' ng-controller='Scroller'>
<div when-scrolled="loadMore("")">
<div ng-repeat='item in items'>
<span>{{item.id}}
<span style="position:absolute;left:140px;">{{item.username}}</span>
<span style="position:absolute;left:290px;">{{item.rank}}</span>
<span style="position:absolute;left:360px;">{{item.posts}}</span>
<span style="position:absolute;left:440px;">{{item.likes}}</span>
<span style="position:absolute;left:530px;">{{item.comments}}</span>
<span style="position:absolute;left:640px;">{{item.flags}}</span>
<span class="label label-success" style="position:absolute;left:710px;">Active</span>
<a style="position:absolute;left:790px;" class="btn btn-info" style="width:30px" ng-href='/admin/userDetail?userid={{item.id}}'>
View Detail</a>
<hr>
</div>
</div>
</div>
</td></tr>
</table>
Below is my angularjs code:
function Scroller($scope, $http, $q, $timeout) {
$scope.items = [];
var lastuser = '999999';
$scope.loadMore = function(type) {
todate = document.getElementById("charttype").value;
var url = "/admin/getusers?type=" + todate + "&lastuser=" + lastuser;
var d = $q.defer();
$http({
'url': url,
'method': 'GET'
})
.success(function (data) {
var items = data.response;
for (var i = $scope.items.length; i < items.length; i++) {
$scope.items.push(items[i]);
count++;
if (count > 100)
{
lastuser = $scope.items[i].id;
break;
}
d.resolve($scope.items);
d.promise.then(function(data) {
});
}
)
.error(function (data) {
console.log(data);
});
return d.promise;
};
$scope.loadMore();
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
alert("scroll");
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
My question is why does my web page show all 3200 lines initially rather than allowing me to do infinite scrolling. You will notice I put an alert in the scroll module and it is never displayed. Do I have to incrementally read my database? Any help is appreciated.
You are adding all of the items returned from your API call into $scope.items.
for (var i = 0; i < items.length; i++) {
$scope.items.push(items[i]);
}
Don't you want to add only a subset of those items?
P.S. Might help if you create a Plunkr to show the specific problem.
EDIT:
Based on your comment about the directive not working, I put together this Plunkr, which is a copy of your code but with the $http get code ripped out. The "scroll" alert fires here. I think you're just missing a closing bracket on your for loop (since I don't have your API endpoint to test against, I can't actually run your code live).
EDIT 2:
I'm not sure why you aren't seeing the function fire correctly on scroll. I've set up another plunker where I've changed the result of the scroll event firing to show an alert and load more items from a data variable, so you can see that the scroll event is firing correctly and it will load more items.
I am sure this has been asked before but I cannot find the answer.
I have an AngularJS script that is pulling from a DB and then rendering to content. Everything is working correctly except a couple of places that I am trying to concatenate some words with new lines between them.
**in the script.js**
groupedList[aIndex].CATEGORY = existingCategory+'\n'+thisCategory;
groupedList[aIndex].CATEGORY = existingCategory+'<br>'+thisCategory;
If I use the first line of the above code I don't see anything but there is not a new line in the redered html. If I use the second line the <br> get rendered as text and the output looks like this:
Instinct<br>Media
instead of
Instinct
Media
I can post the full script if that would be helpful but I am guessing there is something simple that I am just not seeing.
UPDATE
Here is the full js
function qCtrl($scope, $filter, $http, $timeout){
$scope.getCategories = function(){$http.post('quote.cfc?method=getCategories').success(function(data) { $scope.categories = data; }); }
$scope.getClassifications = function(){$http.post('quote.cfc?method=getClassifications').success(function(data) { $scope.classifications = data; }); }
$scope.getIndustries = function(){$http.post('quote.cfc?method=getIndustries').success(function(data) { $scope.industries = data; }); }
$scope.getKeywords = function(){$http.post('quote.cfc?method=getKeywords').success(function(data) { $scope.keywords = data; }); }
$scope.getSources = function(){$http.post('quote.cfc?method=getSources').success(function(data) { $scope.sources = data; }); }
$scope.getAllQuotes = function(){$http.post('quote.cfc?method=getAllQuotesJoined').success(function(data) { $scope.allQuotes = data; }); }
$scope.initScopes = function (){
$scope.getCategories();
$scope.getClassifications();
$scope.getIndustries();
$scope.getKeywords();
$scope.getSources();
$scope.getAllQuotes();
}
$scope.initScopes();
// SEARCH QUOTES
$scope.filteredQuotes = $scope.allQuotes;
$scope.search = {searchField:''};
$scope.searchQuote = function(){
var filter = $filter('filter');
var searchCrit = $scope.search;
var newlist = $scope.allQuotes;
var groupedList = [];
var idList = [];
newlist = filter(newlist,{TESTQUOTE:searchCrit.searchField});
for(i=0;i<10;i++){
aIndex = idList.contains(newlist[i].TESTIMONIALID);
if(aIndex >= 0){
thisKeyword = newlist[i].KEYWORD;
thisClassification = newlist[i].CLASSIFICATION;
thisCategory = newlist[i].CATEGORY;
existingKeyword = groupedList[aIndex].KEYWORD;
existingClassification = groupedList[aIndex].CLASSIFICATION;
existingCategory = groupedList[aIndex].CATEGORY;
if(thisKeyword != '' && existingKeyword.indexOf(thisKeyword) == -1){
groupedList[aIndex].KEYWORD = existingKeyword+' - '+thisKeyword;
}
if(thisClassification != '' && existingClassification.indexOf(thisClassification) == -1){
groupedList[aIndex].CLASSIFICATION = existingClassification+' \n '+thisClassification;
}
if(thisCategory != '' && existingCategory.indexOf(thisCategory) == -1){
groupedList[aIndex].CATEGORY = existingCategory+'<br>'+thisCategory;
}
} else {
idList.push(newlist[i].TESTIMONIALID);
groupedList.push(newlist[i]);
}
}
$scope.filteredQuotes = groupedList;
}
}
Array.prototype.contains = function ( needle ) {
for (j in this) {
if (this[j] == needle) return j;
}
return -1;
}
Here is the HTML
<div ng-repeat="q in filteredQuotes" class="well clearfix">
<h3>{{q.TITLE}}</h3>
<div class="row-fluid" style="margin-bottom:5px;">
<div class="span3 well-small whBG"><h4>Classification</h4>{{q.CLASSIFICATION}}</div>
<div class="span3 well-small whBG pipeHolder"><h4>Categories</h4>{{q.CATEGORY}}</div>
<div class="span3 well-small whBG"><h4>Key Words</h4>{{q.KEYWORD}}</div>
<div class="span3 well-small whBG"><h4>Additional</h4>Industry = {{q.INDUSTRY}}<br>Source = {{q.SOURCE}}</div>
</div>
<div class="well whBG">{{q.TESTQUOTE}}</div>
<div class="tiny">
Source comment : {{q.SOURCECOMMENT}}<br>
Additional Comment : {{q.COMMENT}}
</div>
</div>
</div>
You can use \n to concatenate words and then apply this style to container div.
style="white-space: pre;"
More info can be found at https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
<p style="white-space: pre;">
This is normal text.
</p>
<p style="white-space: pre;">
This
text
contains
new lines.
</p>
I could be wrong because I've never used Angular, but I believe you are probably using ng-bind, which will create just a TextNode.
You will want to use ng-bind-html instead.
http://docs.angularjs.org/api/ngSanitize.directive:ngBindHtml
Update: It looks like you'll need to use ng-bind-html-unsafe='q.category'
http://docs.angularjs.org/api/ng.directive:ngBindHtmlUnsafe
Here's a demo:
http://jsfiddle.net/VFVMv/
You need to either use ng-bind-html-unsafe ... or you need to include the ngSanitize module and use ng-bind-html:
with ng-bind-html-unsafe
Use this if you trust the source of the HTML you're rendering it will render the raw output of whatever you put into it.
<div><h4>Categories</h4><span ng-bind-html-unsafe="q.CATEGORY"></span></div>
OR with ng-bind-html
Use this if you DON'T trust the source of the HTML (i.e. it's user input). It will sanitize the html to make sure it doesn't include things like script tags or other sources of potential security risks.
Make sure you include this:
<script src="http://code.angularjs.org/1.0.4/angular-sanitize.min.js"></script>
Then reference it in your application module:
var app = angular.module('myApp', ['ngSanitize']);
THEN use it:
<div><h4>Categories</h4><span ng-bind-html="q.CATEGORY"></span></div>
Why so complicated?
I solved my problem this way simply:
<pre>{{existingCategory+thisCategory}}</pre>
It will make <br /> automatically if the string contains '\n' that contain when I was saving data from textarea.
I've used like this
function chatSearchCtrl($scope, $http,$sce) {
// some more my code
// take this
data['message'] = $sce.trustAsHtml(data['message']);
$scope.searchresults = data;
and in html I did
<p class="clsPyType clsChatBoxPadding" ng-bind-html="searchresults.message"></p>
thats it I get my <br/> tag rendered
You can also use:
String.fromCharCode(10);
with CSS
white-space: pre-line;
Here si working example:
https://jsfiddle.net/Nxja/3xtcqdej/1/