How to reference divs by class by index in AngularJS? - angularjs

I am new to AngularJS. This is my first Plunker.
I am trying to make it so that if a user clicks on a link in a section, the value of the link appears in the results. The other two results should be cleared of their values.
I can't find any documentation on how to reference specific items in a class by index. In jQuery, I would normally do something like...
// get this index of the thing that was clicked
$this = $(this),
Idx = $BUTTONS.BigButtons.index($this),
ThisButton.eq(Idx).doSomething();
<body ng-controller="MainController as main">
<div class="title" ng-repeat="i in [1,2,3]">
<p>This is section {{i}}</p>
<ul>
<li ng-repeat="j in [1,2,3]">
section {{i}} - link {{j}}</li>
</ul>
<div class="results" ng-bind="main.results"></div>
</div>
</body>
var app = angular.module('controllerAsDemo', []);
app.controller('MainController', function() {
this.results = "Lame default";
this.displayContent = function(firstIdx, secondIdx) {
this.results = "populate section "
+ firstIdx
+ " with the number "
+ secondIdx
+ " - other two results should be empty"
;
};
});
Here is the Demo

I think you should not be looking at classes to achieve what you want. Angular repeat and various other directives give you the ability to do that.
I have touched your code a little and it works now. See below.
var app = angular.module('controllerAsDemo', []);
app.controller('MainController', function($scope) {
$scope.name = 'Cool Clicking';
$scope.results = [];
$scope.displayContent = function(firstIdx, secondIdx) {
$scope.results = [];
$scope.results[firstIdx] = "populate results "
+ firstIdx
+ " with the number "
+ secondIdx
+ " - other two results should be empty"
;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="controllerAsDemo" ng-controller="MainController as main">
<p>{{main.name}}!</p>
<!-- LOOP THROUGH A GALLERY THEME -->
<div class="title" ng-repeat="i in [1,2,3]">
<p>This is section {{i}}</p>
<ul>
<li ng-repeat="j in [1,2,3]">
section {{i}} - link {{j}}
</li>
</ul>
<div class="results" ng-bind="results[i]"></div>
</div>
</body>
That said, if you want to use jQuery in Angular then you simply can.
I will suggest you read about Angular Dom Manipulation here
Another approach can be seen in this plunkr where each item in the gallery has his result.

Related

Filter the object in the array by dynamic filter - AngularJS

Right now my data structure is like
product = [{att1:'2',att2:'red',att3:'gold'},
{att1:'1',att2:'blue',att3:'wood'},
{att1:'2',att2:'green',att3:'plastic'},
{att1:'1',att2:'red',att3:'plastic'}]
And I have a filter on the web page, it has three parts: att1, att2, att3. The user doesn't have to choose options for every part.
For filter att1 it has 2 options: "1" and "2".
Filter att2 it has 2 options: "red" "blue" and "green"
Filter att3 it has 3 options: "gold", "wood" and "plastic".
I can get the options that are selected. For example:
{att1:['2'],att3:['gold','plastic']} or {att1:['1']}
My question is, how do I use product.filter to filter the product data?
Thanks!
You can use a custom filter function which is easy to use, I used att1 but you can expand it to all fields:
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function($scope) {
$scope.products = [{att1:'2',att2:'red',att3:'gold'},
{att1:'1',att2:'blue',att3:'wood'},
{att1:'2',att2:'green',att3:'plastic'},
{att1:'1',att2:'red',att3:'plastic'}];
$scope.filterFunction = function(element) {
return element.att1.match(/^Ma/) ? true : false;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyCtrl">
<form class="form-inline">
<input ng-model="query" type="text"
placeholder="Filter by" autofocus>
</form>
<ul ng-repeat="product in products | filter:query">
<li>{{product}}</li>
</ul>
<ul ng-repeat="product in products | filter:filterFunction">
<li>{{product}}</li>
</ul>
</div>
</div>
I banged out the particular logic for this one, using the three nested loops isn't super great but it does do the job. I'm sure you can optimize this further by using some maps or something but I just went with the get it done by brute force approach :)
angular.module('myApp',[])
.service('ProductService', function(){
return {
products:[
{att1:'2',att2:'red',att3:'gold'},
{att1:'1',att2:'blue',att3:'wood'},
{att1:'2',att2:'green',att3:'plastic'},
{att1:'1',att2:'red',att3:'plastic'}
]
}
})
.controller('TestCtrl', function(ProductService){
this.ProductService = ProductService;
this.filterObject1 = {att1:['2'],att3:['gold','plastic']};
this.filterObject2 = {att1:['1']};
})
.filter('productFilter', function(){
return function(input,filterObj){
if(!filterObj){
return input;
}
var newArray = [];
var filterKeys = Object.keys(filterObj);
for(var i=0;i<input.length;i++){
var curElement = input[i];
innerLoops:
for(var j=0;j<filterKeys.length;j++){
var curKey= filterKeys[j];
for(var k=0;k<filterObj[curKey].length;k++){
var curFilterValue = filterObj[curKey][k];
if(curElement[curKey].match(curFilterValue)){
//We found a match keep this element and move on to checking the next one by breaking out of the inner loops that are checking particular keys/values
newArray.push(curElement);
break innerLoops;
}
}
}
}
return newArray;
};
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.js"></script>
<div ng-app="myApp" ng-controller="TestCtrl as ctrl">
Unfiltered
<div ng-repeat="product in ctrl.ProductService.products|productFilter">
{{product}}
</div>
<hr/>
<strong>Filtered with: {{ctrl.filterObject1}}</strong>
<div ng-repeat="product in ctrl.ProductService.products|productFilter:ctrl.filterObject1">
{{product}}
</div>
<hr/>
<strong>Filtered with {{ctrl.filterObject2}}</strong>
<div ng-repeat="product in ctrl.ProductService.products|productFilter:ctrl.filterObject2">
{{product}}
</div>
</div>

Problems with `track by $index` with Angular UI Carousel

$index in track by index does not start at zero when pagination is used
I have created a carousel using angular ui bootsrap.
Since am loading so many images(over 1,000), I used a filter to display 500 pictures in the pagination.
.filter('pages', function () {
return function (input, currentPage, pageSize) {
if (angular.isArray(input)) {
var start = (currentPage - 1) * pageSize;
var end = currentPage * pageSize;
return input.slice(start, end);
}
};
})
The controller:
self.currentPage = 1;
self.itemsPerPage = 500;
self.maxSize = 10;
//imagesUrls is response.data from http call
angular.forEach(imagesUrls, function (parent) {
var date = uibDateParser.parse(parent.fileCreationTime, 'M/d/yyyy
hh:mm:ss a');
// fill our slide arrays with urls
// model update here
self.slides.push({
image: parent.fileName,
time: date,
id: parent.id
});
});
self.totalItems = self.slides.length;
And I use it like this:
<div uib-carousel
active="$ctrl.active"
interval="$ctrl.myInterval"
no-wrap="$ctrl.noWrapSlides"
no-pause="true">
<div uib-slide ng-repeat="slide in $ctrl.slides | pages:
$ctrl.currentPage : $ctrl.itemsPerPage track by $index"
index="$index">
<img id="carousel-img" ng-src="{{slide.image}}">
<div class="carousel-caption">
<p>Index {{$index}}</p>
</div>
</div>
</div>
<div class="row">
<ul uib-pagination
total-items="$ctrl.totalItems"
items-per-page="$ctrl.itemsPerPage"
ng-model="$ctrl.currentPage"
max-size="$ctrl.maxSize"
boundary-link-numbers="true"
force-ellipses="true"
class="pagination-sm">
</ul>
</div>
This works as expected.
When the carousel is first loaded, the index is 0. When it moves to the next slide the index is 1, when you move to the next slide the index is 2.
When you display the slide.id of the current image it is also 2.
The problem:
However, when you click the second pagination link, the index does not go back to zero, its starts at the last index the slide was in the carousel.
So now the index is 2 and slide id of the current image is 502.
If you slide till index 20, and you click the pagination link, the index is still at 20. When you display the slide id of the current image it becomes 520.
Is there a way to make the index start at 0 again so the slide.id is 500 and not 502 or 520?
I hope my question is clear.
Avoid track by $index when there is a unique property identifier to work with. When working with objects that are all unique (as is this case), it is better to let ng-repeat to use its own tracking instead of overriding with track by $index.
<div uib-slide ng-repeat="slide in $ctrl.slides | pages:
$ctrl.currentPage : $ctrl.itemsPerPage track by ̶$̶i̶n̶d̶e̶x̶ slide.id"
index="$index">
<img id="{{slide.id}}" ng-src="{{slide.image}}">
<div class="carousel-caption">
<p>Index {{ ̶$̶i̶n̶d̶e̶x̶ slide.id}}</p>
</div>
</div>
From the Docs:
If you are working with objects that have a unique identifier property, you should track by this identifier instead of the object instance. 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.
— AngularJS ng-repeat Directive API Reference - Tracking
The DEMO
angular.module("app",['ngAnimate', 'ngSanitize', 'ui.bootstrap'])
.controller("ctrl", function(uibDateParser) {
var self = this;
self.currentPage = 1;
self.itemsPerPage = 10;
self.maxSize = 10;
var url = '//unsplash.it/200/100';
var imagesUrls = [];
for (let i=0; i<40; i++) {
var slide = {
fileName: url+"?image="+(1000+i),
id: 'id'+(0+i+100),
fileCreationTime: new Date()
}
imagesUrls.push(slide);
}
self.slides = [];
//imagesUrls is response.data from http call
angular.forEach(imagesUrls, function (parent) {
var date = uibDateParser.parse(parent.fileCreationTime,
'M/d/yyyy hh:mm:ss a');
// fill our slide arrays with urls
// model update here
self.slides.push({
image: parent.fileName,
time: date,
id: parent.id
});
});
//console.log(self.slides);
self.totalItems = self.slides.length;
})
.filter('pages', function () {
return function (input, currentPage, pageSize) {
if (angular.isArray(input)) {
var start = (currentPage - 1) * pageSize;
var end = currentPage * pageSize;
return input.slice(start, end);
}
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/angular-animate/angular-animate.js"></script>
<script src="//unpkg.com/angular-sanitize/angular-sanitize.js"></script>
<script src="//unpkg.com/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js"></script>
<link href="//unpkg.com/bootstrap/dist/css/bootstrap.css" rel="stylesheet">
<body ng-app="app" ng-controller="ctrl as $ctrl">
<div class="container" uib-carousel
active="$ctrl.active"
interval="$ctrl.myInterval"
no-wrap="$ctrl.noWrapSlides"
no-pause="true">
<div uib-slide ng-repeat="slide in $ctrl.slides | pages:
$ctrl.currentPage : $ctrl.itemsPerPage track by slide.id"
index="$index">
<img id="{{slide.id}}" ng-src="{{slide.image}}">
<div class="carousel-caption">
<p>Index {{slide.id}}</p>
</div>
</div>
</div>
<div class="row container">
<ul uib-pagination
total-items="$ctrl.totalItems"
items-per-page="$ctrl.itemsPerPage"
ng-model="$ctrl.currentPage"
max-size="$ctrl.maxSize"
boundary-link-numbers="true"
force-ellipses="true"
class="pagination-sm">
</ul>
</div>
</body>

how do i write ng-repeat for this in my html code

for(var i=0;i<a.length;i++){
$scope.inputs=[
{name:a[i],value:b[i]}
];
}
this is my Javascript code i want to know how to write (ng-repeat) for arrays
Your JS is invalid, will produce length 1 array. Replace it with this:
$scope.inputs=[];
for(var i=0;i<a.length;i++){// be sure that a.length >=b.length
$scope.inputs.push({name:a[i],value:b[i]}); // push will add new entry to your inputs array.
}
The you can use it in ng-repeat:
<div ng-repeat="entry in inputs"> {{entry.name}} : {{entry.value}} </div>
You don't write loops surrounding a global variable. You leave the variable by itself and then you call the loop. Later you just use the global variable in the html code.
I made a cool snippet so you understand how it works:
angular.module('demo', [])
.controller('Ctrl', ['$scope', function ($scope) {
$scope.inputs = [];
var a = ['name1', 'name2', 'name3'];
var b = [133,233,456];
//this code has to be called somewhere else. It might be part of a function.
for(var i=0; i < a.length; i++){
$scope.inputs.push( {name:a[i],value:b[i]} );
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demo">
<div ng-controller="Ctrl">
<ul>
<li ng-repeat="item in inputs">
<input ng-model="item.name"/>
</li>
</ul>
<!--This is only to display the content of $scope.inputs -->
<pre>{{inputs | json}}</pre>
</div>
</div>
If you have an array in your controller, with a scope that is visible in your html
angular.module('appName').controller('mainCtrl', mainCtrl);
function mainCtrl($scope) {
$scope.inputs = [
key: value,
...
];
}
In your html you would use ng-repeat within the scope of the controller. You can use the ng-repeat directive on several different html tags, such as <ul> lists, a div, select dropdowns and more
<div ng-controller="mainCtrl">
<ul>
<li ng-repeat="item in inputs">{{item.key}} <!-- Prints 'value' --></li>
</ul>
</div>

AngularJS NG-Repeat JSON

I apologize for this simple question. I've been scouring other questions and still can't get it to work.
I have a function returning some key/value pairs
function(data){
console.log(data.message);
}
Returns...
Object {name: "mpierce486", body: "asfsf", time: "1 second ago"}
I have the following when not logging to console...
$scope.message = data.message
Lastly, here's the markup. I'm using a Laravel app so I'm escaping the {{ with #. Nothing shows up and I know it's a simple mistake. Please assist! Thanks!
<div ng-app="myApp" ng-controller="messageCtrl">
<div ng-repeat="x in message" class="main-user-post">
<h1>#{{ x.body }}</h1>
</div>
</div>
I don't think you can do an ng-repeat on an object. Since it's already an object, not an array, you can access it directly, without ng-repeat.
<div ng-app="myApp" ng-controller="messageCtrl">
<div class="main-user-post">
<h1>#{{ message.body }}</h1>
</div>
</div>
your message variable is an object, not an array. So in your iteration, x will take the value of each object properties (body, name, time).
So either use a different approach, or transform your message to an array:
$scope.message = [data.message];
you can iterate through object properties with angular like this:
<div ng-app="myApp" ng-controller="messageCtrl">
<div ng-repeat="(key, value) in message" class="main-user-post">
<h1>#{{value}}</h1>
</div>
</div>
Create object array as below.
JSFiddle - https://jsfiddle.net/L7k6g7ua/ for reference w.r.t AngularJs -
Hope this helps!
<body ng-app="SampleApp">
<div ng-controller="messageCtrl">
<div ng-repeat="m in message" class="main-user-post">
<h1>{{m.body}}</h1>
</div>
</div>
</body>
var sampleApp = angular.module("SampleApp", []);
sampleApp.controller('messageCtrl', function($scope) {
var myObject = {
name: "mpierce486",
body: "asfsf",
time: "1 second ago"
};
var myArray = [];
myArray.push(myObject);
$scope.message = myArray;
});

Create a list without ngRepeat

I would like to create a directive, that does not need ngRepeat, because there is some additional functionality on the directive, that doesn't play good with ngRrepeat.
This is my directive with ng-repeat:
<div>
<ul>
<li ng-repeat="item in items track by $index" ng-class="attrs.itemTheme" data-item="{{ item[attrs.itemId]}}">
<div ng-include="attrs.tpl"></div>
</li>
</ul>
</div>
attrs.tpl, nt-dimension is another directive, that uses the items values from ngRepeat:
<script type="text/ng-template" id="dimension-item-tpl.html">
<div nt-dimension x-attrs="item"></div>
</script>
Without ngRepeat:
<div>
<ul></ul>
</div>
Can some please give me an example, I am quit struggling with this.
Example of code:
http://jsfiddle.net/mato75/4zhLtjbw/
Not working example:
http://jsfiddle.net/mato75/ztLhpf2g/
Got to compile and append the ngIncluded template, but the problem is, that it compiles only the last one, because the digest cycle is to slow.
var el = jqElm.find('ul');
scope.attrs.list.forEach(function (vl) {
var tmp =
'<li class="' + attrs.itemTheme + '" data-item="' + vl.id + '">' +
'<div ng-include="\'' + attrs.itemTpl + '\'"></div>' +
'</li>';
scope.item = vl; // this is to slow :(
var b = $compile(tmp)(scope);
el.append(b);
});
You need to manually create an own scope for each li, so each item has its own data.
var ul = jqElm.find('ul');
scope.list.forEach(function (vl) {
var li = '<li><div ng-include="\'item-tpl2.html\'"></div></li>';
var newScope = scope.$new();
newScope.item = vl;
var cLi = $compile(li)(newScope);
ul.append(cLi);

Resources