ng-click function using $index as parameter throws Unexpected token error - angularjs

So I ma trying to loop through an array and create an list item for each object in it.
However when then adding an ng-click to that item i get the following error, even though the code prints out correctly.
Syntax Error: Token '$index' is unexpected, expecting [:]
I have the following code:
<body ng-controller="VideoController as VidCtrl">
<div class="row" id="grid">
<div ng-repeat="array in videos" ng-show="VidCtrl.isActive({{$index}})">
<ul class="small-block-grid-3">
<li ng-repeat="video in array" ng-click="VidCtrl.setVideo({{$index}})">
<img src="{{video.image}}">
<div>
<h5>{{video.title}}</h5>
<p>{{video.ingress}}</p>
</div>
</li>
</ul>
</div>
<ul class="pagination">
<li ng-class="{'current':isActive($index)}" ng-repeat="array in videos"><a ng-click="VidCtrl.setPanel($index)" href="#">{{$index + 1}}</a></li>
</ul>
</div>
And This is my js:
var videos = [];
var activeVideo = null;
var app = angular.module('webb-tvApp', []);
app.controller('VideoController', function($scope){
$scope.videos = videos;
this.activePanel = 0;
this.setPanel = function(val){
this.activePanel = val;
}
this.isActive = function(val){
return this.activePanel === val;
}
this.setVideo = function(vidIndex){
console.log(videos[this.activePanel][vidIndex]);
}
});
The videos array has the following structure.
var videos = [
[
{
image: "foo",
title: "bar",
ingress: "foobar",
},
{},
{}
],
[
{},
{},
]
]
I am new to angular so go easy on me.

When using these directives you can use the variables without interpolation {{}}. Additionally, you can use ng-src with your images instead of using src.
<body ng-controller="VideoController as VidCtrl">
<div class="row" id="grid">
<div ng-repeat="array in videos" ng-show="VidCtrl.isActive($index)">
<ul class="small-block-grid-3">
<li ng-repeat="video in array" ng-click="VidCtrl.setVideo($index)">
<img ng-src="{{video.image}}">
<div>
<h5>{{video.title}}</h5>
<p>{{video.ingress}}</p>
</div>
</li>
</ul>
</div>
<ul class="pagination">
<li ng-class="{'current':isActive($index)}" ng-repeat="array in videos"><a ng-click="VidCtrl.setPanel($index)" href="#">{{$index + 1}}</a></li>
</ul>
</div>

Few things I noticed:
Remove interpolation from inside ng-show and ng-click
<div ... ng-show="VidCtrl.isActive($index)">
...
<li ... ng-click="VidCtrl.setVideo($index)">
And replace src with ng-src
<img ng-src="{{video.image}}">

Related

Display a long message in desired format using AngularJS

I have long message string. Each parent message is separated by a symbol # and child message separated by a symbol ^.
$scope.messages = "Message1#Message2#Message3#Message4^Message41^Message42#Message5^Message51^Message52^Message53^Message54^Message55";
I want to display these messages in an unordered list format like below.
I used ng-repeat with ul-li to show the messages but the child messages are not properly aligned as nested ul-li elements and also show repeated messages.
<ul>
<li ng-repeat="message in messages.split('#')">
{{message}}
<ul>
<li ng-repeat="msg in message.split('^')">
{{msg}}
</li>
</ul>
</li>
</ul>
But this code doesn't show the desired output. My effort resides here.
Its better to convert the string into an object beforehand. I've tried to convert it into an object form so that it becomes easier for the template to read and conditionally show the parent-children relationship.
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.messages = "Message1#Message2#Message3#Message4^Message41^Message42#Message5^Message51^Message52^Message53^Message54^Message55";
$scope.messages = $scope.messages
.split("#").reduce((acc, item) => {
if (item.includes('^')) {
let c = item.split('^');
acc[c[0]] = c.slice(1, -1);
} else {
acc[item] = null;
}
return acc;
}, {});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="(key, value) in messages">
{{key}}
<ul ng-if="value">
<li ng-repeat="child in value track by $index">
{{child}}
</li>
</ul>
</li>
</ul>
</div>
You almost done it, check out some modifications:
angular.module('app', []).controller('MyCtrl', function($scope) {
$scope.messages = "Message1#Message2#Message3#Message4^Message41^Message42#Message5^Message51^Message52^Message53^Message54^Message55";
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<ul ng-app='app' ng-controller="MyCtrl">
<li ng-repeat="message in messages.split('#')">
{{message.split('^')[0]}}
<ul>
<li ng-repeat="msg in message.split('^').splice(1)">
{{msg}}
</li>
</ul>
</li>
</ul>
I think this is what are you looking for
<li ng-repeat="message in messages">
{{message.text}}
<ul>
<li ng-if="message.sub" ng-repeat="msg in message.sub">
{{msg}}
</li>
</ul>
</li>
$scope.myFilter = function(input){
if(typeof input == 'string'){
var result = [];
var items = input.split('#');
for(var i=0;i<items.length;i++){
var item = items[i];
if(item.indexOf('^') == -1){//item doesn't have '^' means parent message
result.push({text:item});
}else{
//here my logic goes wrong to split child messages
var subItems = item.split('^');
//now If I push this subItems directly into result, not a good idea
result.push({sub: subItems.splice(1, subItems.length), text: subItems[0]});
}
}
return result;
}
return [];
};
https://jsfiddle.net/va94nqvd/12/

how to bind $index from ng-repeat to controller

In Angular I wanted to bind $index to controller. How can I send $index value in to my controller. Here is my html code.
<body>
<div class="container">
<div class="row row-content" ng-controller="demoController as demoCtrl">
<ul>
<li ng-repeat="val in demoCtrl.list" >Hello {{$index}}</li>
</ul>
</div>
</div>
Here is my controller code
var app = angular.module('confusionApp',[])
app.controller('demoController', function(){
var list = [1,2,3,4,5];
this.list = list;
var array = ['abc','def','ghi','jkl','mno']
this.array = array
console.log(this.array[index]);
});
I need to use ng-modal in HTML and bind that value to some variable in my controller.
Based on the selection of index, it should check in array and respective array should have to print.
Can any of you please help
To get your current iteration position in your controller you have define a function.
So your html code like this.
<body>
<div class="container">
<div class="row row-content" ng-controller="demoController as demoCtrl">
<ul>
<li ng-repeat="val in demoCtrl.list" ng-click="dispArray($index)">Hello {{$index}}</li>
</ul>
</div>
</div>
And your controller code
var app = angular.module('confusionApp',[])
app.controller('demoController', function($scope){
$scope.dispArray = function(index){
// console.log(index);
// your code
}
});
Depending on what you're trying to accomplish you might be better served creating a custom iterator.
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
}
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
Angular is going to iterate over everything in the list when you use ngRepeat. If you're trying to track which list item a user clicks then you'll just add that in there using $index.
<li ng-repeat="val in demoCtrl.list" >
<span ng-click="demoCtrl.userClicked($index)"> Hello {{$index}}</span>
</li>
If you're just trying to print the data in each item, ng-repeat is already iterating everything for you.
<li ng-repeat="val in demoCtrl.list" >
<span ng-click="demoCtrl.userClicked($index)"> Hello {{val}}</span>
</li>
It all depends on what you're trying to do.
i dont konw what u want to do. if u want to use event. you can pass $index to your controller function like:
<li ng-repeat="val in demoCtrl.list" ng-click="getIndex($index)">Hello {{$index}}
$scope.getIndex = function($index) {
console.log($index)
}
hope to help u.

Angular JS - Nested JSON Driving a Two Level Navigation - How to Alter scope from directive?

I'm new to Angular but enjoying getting to know it! I'm having no problem populating data but I'm stuck on this navigational problem.
I have a sidebar divided into two navigation s. Both are reading from nested JSON of 'areas' and 'reports'. The idea is that the currently selected/clicked item in the left hand list will populate the elements in the right hand list, according to its children in the JSON.
I've got the initial population working fine. It reads the 'areas' and assigns the active area, which displays the reports. I then have a directive which applies css classes on the click event, but how can I get it to also alter the active area?
I'm currently doing scope.activeArea = scope.areas[1];, but this does not work. The index is just there for development; that will be replaced by the index of the clicked element.
My HTML:
<div id="reports-subnav" ng-controller="reportAreasController">
<ul class="subnav-areas" >
<li ng-repeat="x in areas">
<a class="subnav-area-button" href="#" my-cool-directive>
<i class="{{ x.iconClass }}"></i>
</a>
</li>
</ul>
<ul id="nav-subsection" class="subnav-reports">
<li ng-repeat="r in activeArea.reports" id="{{ r.action }}" role="presentation"><span class="{{ r.iconClass }}"></span> {{ r.name }}</li>
</ul>
</div>
And Javascript:
var app = angular.module("focalWeb", []);
app.controller("reportAreasController", ['$scope','$http', function($scope, $http) {
$http.get('<?php echo SITE_ADDR; ?>/data/reportAreas.json').success (function(data){
$scope.areas = data.areas;
$scope.activeArea = $scope.areas[0];
});
}] );
app.directive("myCoolDirective", function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
$(elem).click(function() {
//var target = $(elem).next(".panel-collapse");
//target.hasClass("collapse") ? target.collapse("show") : target.collapse("hide");
$('.subnav-areas li').removeClass('active');
$(this).parent().addClass('active');
scope.activeArea = scope.areas[1];
});
}
}
});
Thanks!
Edit
Thanks to charlietfl's guidance my code now looks like this:
HTML
<div id="reports-subnav" ng-controller="reportAreasController">
<ul class="subnav-areas" >
<li class="subnav-area" ng-repeat="x in areas">
<a class="subnav-area-button" href="#" ng-click="showReports($event, x.id)">
<i class="{{ x.iconClass }}"></i>
</a>
</li>
</ul>
<ul id="nav-subsection" class="subnav-reports">
<li ng-repeat="r in activeArea.reports" id="{{ r.action }}" role="presentation"><span class="{{ r.iconClass }}"></span> {{ r.name }}</li>
</ul>
</div>
JS
var app = angular.module("focalWeb", []);
app.controller("reportAreasController", ['$scope','$http', function($scope, $http) {
$http.get('<?php echo SITE_ADDR; ?>/data/reportAreas.json').success (function(data){
$scope.areas = data.areas;
$scope.activeArea = $scope.areas[0];
$scope.showReports = function($event, index) {
$scope.activeArea = $scope.areas[index -1];
var theLi = angular.element($event.currentTarget).parent();
theLi.parent().find('li').removeClass('active');
theLi.addClass('active');
}
});
}] );
Edit 2
HTML
<div id="reports-subnav" ng-controller="reportAreasController">
<ul class="subnav-areas" >
<li class="subnav-area" ng-repeat="x in areas" ng-class="{active: x==activeArea}">
<a class="subnav-area-button" href="#" ng-click="showReports($event, x)">
<i class="{{ x.iconClass }}"></i>
</a>
</li>
</ul>
<ul id="nav-subsection" class="subnav-reports">
<li ng-repeat="r in activeArea.reports" id="{{ r.action }}" role="presentation"><span class="{{ r.iconClass }}"></span> {{ r.name }}</li>
</ul>
</div>
JS
var app = angular.module("focalWeb", []);
app.controller("reportAreasController", ['$scope','$http', function($scope, $http) {
$http.get('<?php echo SITE_ADDR; ?>/data/reportAreas.json').success (function(data){
$scope.areas = data.areas;
$scope.activeArea = $scope.areas[0];
$scope.showReports = function($event, item) {
$scope.activeArea = item;
}
});
}] );
Thanks again charlietfl!

Angular.js - ng-repeat not working

Angular.js is pretty new to me. I have learned before the $scope method for the controller and now trying the "this" method. For some reason I can not get my ng-repeat to work. here's my code:
HTML:
<ul class="nav nav-pills">
<li><a ng-click="myCtrl.tab=1" href>one</a></li>
<li><a ng-click="myCtrl.tab=2" href>two</a></li>
<li><a ng-click="myCtrl.tab=3" href>three</a></li>
</ul>
<div ng-controller="imageContr as imageCtrl">
<div ng-show="myCtrl.tab === 1" class="tab">
<h3>ONE title</h3>
<p>hello</p>
<li ng-repeat="item in gallery">
<img alt="imagealt" ng-src="{{item.photo}}">
</li>
</div>
<div ng-show="myCtrl.tab === 2" class="tab">
<h3>TWO title</h3>
<p>how are</p>
</div>
<div ng-show="myCtrl.tab === 3" class="tab">
<h3>THREE title</h3>
<p>you?</p>
</div>
</div>
</section>
</body>
</html>
APP.JS:
(function() {
var app = angular.module('myApp', []);
app.controller('myController', function() {
this.tab = 1;
});
app.controller('imageContr', function() {
this.gallery = images;
var images = {
photo: 'image1.jpg'
};
});
})();
Declare images before you assign it, and make it an array
var images = [{ photo: 'image1.jpg' }];
this.gallery = images;
Then in your view:
<li ng-repeat="item in imageCtrl.gallery">
<img alt="imagealt" ng-src="{{item.photo}}">
</li>
You are storing the gallery on this of the controller (which is basically the $scope), hence you need to access it in the template via imageContr as well:
<li ng-repeat="item in imageCtrl.gallery">
<img alt="imagealt" ng-src="{{item.photo}}">
</li>
You could also consider making gallery an array, so can easily iterate over it. Or you need to use the "iterate over object properties"-syntax:
<div ng-repeat="(key, value) in myObj"> ... </div>

order item in ngrepeat follow property of item

How can I order the post to top-down structure (the post which has highest likes number will be first and the last is the post which lowest likes number). To do that I set oderBy:likes.length but it is not work. Please help, thanks!
function MyController($scope) {
$scope.posts = [{"title": "AAtitle",
"content":"AAcontent",
"likes":["person1", "person2", "person3"]
},
{"title": "BBtitle",
"content":"BBcontent",
"likes":["person10"]
},
{"title": "CCtitle",
"content":"CCcontent",
"likes":["person10","person11", "person100", "person1", "person2"]
}
]
}
<div ng-controller = "MyController">
<ul>
<li ng-repeat = "post in posts | oderBy: likes.length">
<div> {{post.title}} </div>
<div> {{post.content}} </div>
<div> {{post.likes.length}} </div>
</li>
</ul>
</div>
You could write a function to do the sorting in the controller before displaying in view. Or preferable, you could write a custom filter
Filter:
angular.module('YourAppName', [])
.filter('orderByLikes', function() {
function compare(a, b) {
return b.likes.length - a.likes.length;
}
return function (input) {
return input.sort(compare);
}
});
View:
<div ng-controller = "MyController">
<ul>
<li ng-repeat = "post in posts | orderByLikes:posts">
<div> {{post.title}} </div>
<div> {{post.content}} </div>
<div> {{post.likes.length}} </div>
</li>
</ul>
</div>
Here is a working Fiddle

Resources