Angularjs ng-repeat with conditions - javascript

Lets say I have 10 articles objects array and each with their own article title in it ( assume some of them has the same title )
when i do ng-repeat="art in articles" and {{ art.article_title }} it will print the title 10 times which is not what I want.
I want to do something like
Title-1:
article 1
article 2
article 3
Title-2:
article 4
article 5......
something like that if articles share the same title.
Thanks

You should write a custom filter, then you will be able to proceed like this:
<li ng-repeat="unique_article in articles|dedup">
{{unique_article.article_title}}
<span ng-repeat="related in unique_article.related">
Article {{related.id}}
</span>
</li>
Your filter may look for example like this (assuming your articles are sorted by title):
.filter('dedup', function() {
return function(articles) {
var deduped = [];
var last_article = null;
for(var i=0,max=articles.length;i<max;i++) {
var article = articles[i];
if(!last_article || last_article.article_title !== article.article_title)
{
article.related = [];
deduped.push(article);
last_article = article;
} else {
last_article.related.push(article);
}
}
return deduped;
};
});
(I did not test it, just written it ad hoc as a quick example, also if your articles are not sorted by title you will have to modify it)

Maybe re-thinking it would help, the ideal way to do this would be to re-arrange your object so that the articles fall under the titles, like so.
var arrangeArticles = function() {
var result = {};
angular.forEach($scope.articles, function( article ) {
var title = article.article_title;
if( !result[title] ) {
result[title] = [article];
} else {
result[title].push(article);
}
});
$scope.articles = result;
$scope.$apply(); // Might be needed
};
I don't think that you can do this in the ng-repeat, with the layout that you expressed.
Then you would need to change your repeat to something like this
<div ng-repeat="(title, group) in articles">
{{title}}
<div ng-repeat="article in group">
{{article.description}}
</div>
</div>

Related

ng-repeat and ng-if syntax for arrays within arrays

I have an array within an array and I'm having some issues with ng-repeats and ng-ifs. My array looks something like this:
data.stage = [];
task_obj = {};
data.stage.push({
workflow_stage: wf.getDisplayValue('name'),
task: []
});
task_obj.number = userTasks.getValue('number');
task_obj.parent = userTasks.getValue('parent');
task_obj.state = userTasks.getValue('state');
task_obj.url = userTasks.getDisplayValue('url');
task_obj.incompleteCounter = incompleteCounter;
task_obj.icon = icon;
task_obj.status = status;
task_obj.style = style;
task_obj.short_description = userTasks.getValue('short_description');
task_obj.bundle_name = bundle.getValue('bundle_name');
task_obj.workflow = bundle.getDisplayValue('workflow');
task_obj.workflow_stage = bundle.getDisplayValue('workflow_stage');
for(var a=0; a < data.stage.length; a++){
if(bundle.getDisplayValue('workflow_stage') == data.stage[a].workflow_stage) {
data.stage[a].task.push(task_obj);
}
}
}
If I have an ng-repeat that looks like ng-repeat="item in data.stage track by $index", how would I access the short_description for example? Would it be like {{item.task.short_description}}?
Similarly, if I wanted to write an ng-if where bundle_name is "MyBundle", how would I write it? I've tried ng-if="item.task.bundle_name=='MyBundle'", but it obviously doesn't work.
Can anyone guide me on the correct syntax?
You need to use ng-repeat for task also
<div ng-repeat=ng-repeat="item in data.stage track by $index">
<div ng-repeat=ng-repeat="task in item.task track by $index">
<div ng-if="task.bundle_name == 'MyBundle' ">
My bundle condition
</div>
<span>{{task.short_description}}</span>
</div>
</div>

Displaying data on the page from the data gotten from parse

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>

How to group all product of single brand together - angular JS

Please see this JS fiddle link.
http://jsfiddle.net/4Dpzj/174/
This is the logic for group by
app.filter('groupBy', ['$parse', function ($parse) {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
// this is a new field which is added to each item where we append "_CHANGED"
// to indicate a field change in the list
//was var new_field = group_by + '_CHANGED'; - JB 12/17/2013
var new_field = 'group_by_CHANGED';
// loop through each item in the list
angular.forEach(list, function (item) {
group_changed = false;
// if not the first item
if (prev_item !== null) {
// check if any of the group by field changed
//force group_by into Array
group_by = angular.isArray(group_by) ? group_by : [group_by];
//check each group by parameter
for (var i = 0, len = group_by.length; i < len; i++) {
if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
group_changed = true;
}
}
}// otherwise we have the first item in the list which is new
else {
group_changed = true;
}
// if the group changed, then add a new field to the item
// to indicate this
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
I want to group all the products together.
what changes i need to do ?
I come up with this in my mind. Without using any custom filters.
I simply use this ng-repeat syntax :
ng-repeat="(key,item) in MyList | orderBy:orderKey"
Thanks to it i can get the key to compare the value with the previous object.
Here is my ng-show attribute. It can be improved by sorting the list somewhere else (like in the controller)
<h2 ng-show="(MyList | orderBy:orderKey)[key-1][orderKey] !== (MyList | orderBy:orderKey)[key][orderKey]"
Thanks to this you can populate your var "orderKey" with any of your attribute name and this will works.
See it working in this JSFiddle
Hope it helped.
EDIT :
I think it would be a bit cleaner to use a temporary list to manage the visual order (see it in this JSFiddle):
JS :
$scope.orderList = function(){
$scope.orderedList = $filter('orderBy')($scope.MyList,$scope.orderKey);
}
HTML :
ng-change="orderList()" To trigger the list sort
The cleaner ng-repeat / ng-show
<div ng-repeat="(key,item) in orderedList">
<h2 ng-show="orderedList[key-1][orderKey] !== orderedList[key][orderKey]">{{item[orderKey]}} </h2>
<ul>
<li>{{item.ProductName}}</li>
</ul>
</div>
Have a look at this:
http://jsfiddle.net/4Dpzj/176/
<div ng-repeat="item in MyList | orderBy:['SubCategoryName','BrandName'] | groupBy:['SubCategoryName']" >
<h2 ng-show="item.group_by_CHANGED">{{item.SubCategoryName}} </h2>
<ul>
<li>{{item.ProductName}} --- {{item.BrandName}}</li>
</ul>
</div>

How to write VISIBILITY if-else condition in ANGULARJS?

How to Write Condition on data which is fetching from Json to Angularjs?
Example : if user FIRM NAME exists Show else if user FULL NAME exists Show else Show REALNAME
I have a working Example of fetching data
at line number 25 <h3 class="moduletitle">Name : {{ module.realname }}</h3>
Please See that in PLUNKER
I hope i will get the working code update along with PLUNKER
I can suggest you have a function that returns the entity in which you want to display. Then using ng-show / ng-hide to display/hide the things you want.
Example:
function pseudoDecide(){
var displaythis = "";
if(/*boolean exp*/){ displaythis = "firm" }
else if(/*boolean exp*/) { displaythis = "full" }
else(/*boolean exp*/) { displaythis = "real" }
return displaythis;
}
Then <div ng-show="{{psedoDecide() === 'firm'}}>" etc etc, something like that.
With AngularJS 1.1.5+, you can use the ternary operator inside an expression. In your case, I believe you want something like:
<h3 class="moduletitle">Name : {{ module.firmname ? module.firmname : (module.fullname ? module.fullname : module.realname)) }}</h3>
If you don't want a nested ternary in your template, you could also go this route:
Somewhere in your controller:
$scope.pickName = function (module) {
var val;
if (module.firm_name) {
val = module.firm_name;
} else if (module.full_name) {
val = module.full_name;
} else {
val = module.realname;
}
return val;
};
And in your template:
<h3 class="moduletitle">Name : <span ng-bind="pickName(module)"></span></h3>

AngularJS is rendering <br> as text not as a newline

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/

Categories