Using ng-click inside np-repeat - angularjs

I'm trying to implement a shortlisting functionality in a case where I'm using ng-click inside ng-repeat.
While the $index is being displayed correctly outside the ng-click, $index is only the index of the last object in the JSON array in all the cases where the html is generated using ng-repeat.
I was expecting the respective venue or venue.id to be passed as an argument to shortlistThis(). I'm guessing that this could be an issue related to event binding.
Can someone help me understand what's going wrong here and probably a better way to do this if possible.
I did try checking this Bind ng-model name and ng-click inside ng-repeat in angularjs The fiddle here works just fine but, mine doesn't.
Update: I've found something interesting here. When the shortlist button is placed just under the ng-repeat, it work just as intended. But when, nested inside a div with a ng-class, it isn't working as intended. Can anyone explain and suggest a fix for this ?
//locationsapp.js var locationsApp = angular.module('locationsApp', []);
var venues = [{
id: "Flknm",
name: "ship",
}, {
id: "lelp",
name: "boat",
}, {
id: "myp",
name: "yacht",
}, ];
var shortlistedVenues = [];
var locationsApp = angular.module('locationsApp', ['ui.bootstrap']);
locationsApp.controller("getvenues", function($scope) {
$scope.venues = venues;
$scope.shortlistThis = function(venue) {
console.log(venue);
if (shortlistedVenues.indexOf(venue) == -1) {
shortlistedVenues.push(venue);
//alert('If');
} else {
console.log('already shortlisted')
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="locations" ng-app="locationsApp">
<div ng-controller="getvenues">
<div ng-repeat="venue in venues" ng-mouseover="" class="venue">
<div ng-controller="manageInfo">
<div class="row-fluid">
<div class="control" ng-click="showInfo()">
</div>
<div class="maps" class="info">
<div ng-class="class">
<div class="close-info" ng-click="hideInfo()">
<i class="fa fa-times">
</i>
</div>
<div class="row full-venue-contents" ng-app="ui.bootstrap.demo">
<div class="">
<div class="row-fluid myclass">
<div class="button-holder">
<div class="row-fluid">
<div class="col-md-4 col-xs-4 text-center btn-grid">
<button type="button" class="btn btn-default btn-lg btn-sup" ng-click="shortlistThis(venue)">
Shortlist{{$index}}
</button>
</div>
</div>
</div>
</div>
</div>
<!-- End of Full Info Container -->
</div>
</div>
</div>
</div>
<div class="compare">
<button type="button" class="btn btn-default btn-lg btn-sup">
Compare ( {{shortlistedVenues.length}} )
</button>
</div>
</div>
</div>
</div>
</div>

The problem is with your button div which was position: fixed, it is actually showing the 1st button but clicking on it firing last button click, I'd suggest you should suggest you render only the shortlist button which has been selected by you. For that you can use ng-if and render those button which index is the same as that of selected $index
HTML
<div ng-if="selected==$index" class="button-holder">
<div class="row-fluid">
<div class="col-md-4 col-xs-4 text-center btn-grid">
<button type="button" class="btn btn-default btn-lg btn-sup"
ng-click="shortlistThis($parent.$index)">Shortlist{{$index}}</button>
</div>
</div>
</div>
& then put ng-click="selected='$index'" on ng-repeat div like
<div ng-repeat="venue in venues" class="venue" ng-click="selected=$index">
Working Fiddle
Hope this could help you, Thanks.

Related

select nth element from element array jquery

I'm working on my angular e2e test project. I've the following html generated as part of my ng-repeat with out any id, I would like to choose the second element that is with heading Topic - xyz and also then click the button which is a child of it's sibling. How can I do that.
<div class="row ng-scope" ng-repeat="post in posts">
<div class="col-md-7">
<h4 class="ng-binding">Topic - ABC</h4>
<div class="text-right">
<button class="btn btn-none btn-sm" ng-click="posts.newPost()">
Create Post
</button>
</div>
</div>
</div>
<div class="row ng-scope" ng-repeat="post in posts">
<div class="col-md-7">
<h4 class="ng-binding">Topic - XYZ</h4>
<div class="text-right">
<button class="btn btn-none btn-sm" ng-click="posts.newPost()">
Create Post
</button>
</div>
</div>
</div>
<div class="row ng-scope" ng-repeat="post in posts">
<div class="col-md-7">
<h4 class="ng-binding">Topic - EFG</h4>
<div class="text-right">
<button class="btn btn-none btn-sm" ng-click="posts.newPost()">
Create Post
</button>
</div>
</div>
</div>
This is what I've tried to so far which is not working
var button = $$(by.repeater('post in posts')).get(1).$(by.css('[ng-click="posts.newPost()"]'))
button.click(); // click is not showing up
$$(by.repeater('post in posts')) and $(by.css('[ng-click="posts.newPost()"]')) - these are not correct syntax of using the by.repeater() or by.css() locator. $$ is a shortcut for the element.all(by.css()) and should not be used for the "repeater" locator. If you use $(), there is no need to wrap your selector into by.css():
var button = element.all(by.repeater('post in posts')).get(1).$('[ng-click*=newPost]');
button.click();
If you want to filter the repeater element by the topic name, you can use .filter():
var button = element.all(by.repeater('post in posts')).filter(function (post) {
return post.$("h4").getText().then(function (postTitle) {
return postTitle === "Topic - XYZ";
});
}).get(1).$('[ng-click*=newPost]');
button.click();
Also see if using the by.buttonText locator would work as well (a little bit cleaner):
var post = element.all(by.repeater('post in posts')).get(1);
var button = post.element(by.buttonText("Create Post"));
button.click();

Angular add class to body from inside view

I have an app where, when a thumbnail is clicked a modal will open and play the video connected to the thumbnail. What I need to do is add a class to the body tag when this modal is open so I can stop the body content scrolling behind the modal.
I am using an ng-click to activate an ng-show and ng-if. The problem is all of this is inside the scope of an ng-repeat which in turn is inside the view. I am new to Angular and any help would be appreciated.
<div class="videos" ng-controller="modalCtrl">
<ul>
<li class="clear-fix" ng-repeat="video in filtered = videos | filter:search | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
<!-- THIS ACTIVATES THE MODAL WHEN CLICKED -->
<div ng-click="showModal = !showModal" class="item-thumbnail float-left pointer" back-img="{{video.thumbnail}}">
</div>
<div class="item-details-container float-left">
<div class="item-header">
<div ng-click="showModal = !showModal" class="item-title pointer">
<h3 class="underline">{{video.title}}</h3>
</div>
<div class="item-author pointer">
<span>by: <a class="underline" ui-sref="{{video.author_link}}">{{video.author}}</a></span>
</div>
</div>
<div class="item-description">
<p>{{video.description.trunc(150)}}</p>
</div>
</div>
<!-- THIS IS THE MODAL BACKGROUND COVER -->
<div ng-show="showModal" class="modal-cover"></div>
<!-- THIS IS THE MODAL -->
<div ng-show="showModal" class="modal-container">
<div ng-if="showModal" class="video-modal clear-fix">
<button class="btn-close" ng-click="$parent.showModal = false">
<span></span>
<span></span>
</button>
<div class="video-container float-left">
<youtube-video video-url="video.url"></youtube-video>
</div>
<div class="video-info-container float-right">
<div class="video-info">
<div class="item-header">
<div class="item-title">
<h3>{{video.title}}</h3>
</div>
<div class="item-author pointer">
<span>by: <a class="underline" ui-sref="{{video.author_link}}">{{video.author}}</a></span>
</div>
</div>
<div class="item-description">
<p>{{video.description}}</p>
</div>
</div>
<div class="voting-container clear-fix">
<div class="votingButton btn-solid pointer" ng-click="upVote(video); showDetails = !showDetails">
<i class="fa fa-thumbs-o-up" aria-hidden="true"></i>
</div>
<div class="bugButton btn-solid pointer">
<i class="fa fa-flag-o" aria-hidden="true"></i>
</div>
<div ng-show="showDetails = showDetails"><span>Thank you for voting.</span></div>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
Here's what you need to do :
You can use ng-class in your body tag like this
<body ng-class="{'is-modal-open': freeze}">
And you can write ng-click on your thumbnail like this
<div class="thumbnail" ng-click="is-modal-open=true"></div>
You could just make a div in your template like this:
<div class="fullscreen-modal">
//content here
</div>
And in your controller you could do this:
var elementResult = element[0].getElementsByClassName('fullscreen-modal');
angular.element(document.body).append(element);
Now your element is a direct child of <body>.
OR
Define the modal div outside of ng-repeat.
Then define a scope function
$scope.openModal() = function(contentOrVideoId)
{
//put logic here
$scope.showModal = true;
$scope.modalContent = contentOrVideoId;
}
And just call this function inside ng-repeat with ng-click="openModal('IdOrContent')"
You are looking for ngClass directive.
So if you would add this to the body element you want to disable scrolling on, it would give you what you are looking for:
<div ng-class="{'is-modal-open': showModal}">
In short, this will add the class is-modal-open when showModal validates to true
Just to clarify (Thanks #Ronnie), you need to make sure the <div> that contains what you call body is inside the scope of your controller (somewhere inside the div that has ng-controller). And you shouldn't put it directly on the <body> tag
[edit]
I don't like using $rootScope, but you could try this:
<body ng-class="{'is-modal-open': showModal}">
// Your div
<div ng-click="showHideModal()" class="item-title pointer">
// your controller
app.controller('modalCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
$scope.showHideModal = function() {
$rootScope.showModal = !$rootScope.showModal;
};
}]);

x-editable nested editable-select not submitting

I have a editable-select nested inside other, when I submit the nested editable-select changes, it does not call the onaftersave assigned function ('vm.addOperation()'), it also shows the outter editable-select edit form.
I want it just to show the nested edit form and the function call to work.
My html code:
<div editable-select="vm.selectedUser"
e-ng-options="user.objectId as user.displayName for user in vm.users"
onshow="vm.getUsers()"
onaftersave="vm.addUser()">
<div class="container" ng-repeat="u in entity.authorizedUsers">
<div class="row">
<div class="col-xs-2">
{{u.id}}
</div>
<div class="col-xs-4">
<div editable-select="vm.selectedOperation"
e-ng-options="operation.id as operation.name for operation in vm.operations"
onshow="vm.getOperations()"
onaftersave="vm.addOperation()">
<div class="container" ng-repeat="op in u.authorizedOperations">
<div class="row">
<div class="col-xs-3">
{{op.name}}
</div>
<div class="col-xs-push-2">
<button class="btn btn-xs btn-danger"
ng-click="vm.removeOperation(entity.id, u.id, op.id)">
<i class="fa fa-trash-o"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-push-4">
<button class="btn btn-xs btn-warning pull-left"
ng-click="vm.removeuser(entity.id, u.id)">
<i class="fa fa-trash-o"></i>
</button>
</div>
</div>
</div>
</div>
I've manage to fix the problem with the following code. It's an ugly workaround tho, please if someone has a more elegant solution I'd very much appreciate it.
in html:
<div editable-select="vm.selectedOperation"
e-form ="nestedForm"
onshow="vm.getOperations()"
ng-click="nestedForm.$show(); vm.xEditableNestedFormFix(nestedForm); $event.stopPropagation()"
e-ng-options="operation.id as operation.name for operation in vm.operations"
onaftersave="vm.addOperation()">
in js:
vm.xEditableNestedFormFix = function (form) {
var editorElement = form.$editables[0].controlsEl[0].firstChild;
editorElement.onclick = function (event) {
event.stopPropagation();
}
var submitButton = form.$editables[0].buttonsEl[0].firstChild
submitButton.onclick = function (event) {
form.$submit();
event.stopPropagation();
}
var cancelButton = form.$editables[0].buttonsEl[0].lastChild
cancelButton.onclick = function (event) {
event.stopPropagation();
}
}

ng-click directive not passing desired parameter

I have the following piece of angular code: (it's a modal-popover menu which uses the following js library: http://scruffles.github.io/BootstrapModalPopover/).
<div ng-repeat="purchasedItem in purchased" class="row item_summary">
<!-- edited for brevity -->
<div id="{{purchasedItem.code}}-popover" data-product-id="{{purchasedItem.code}}" class="popover" style="display: none;">
<div class="arrow"></div>
<h3 class="popover-title" style="display: none;"></h3>
<div class="popover-content">
<div class='popover-menu-left'>
<button class="btn btn-sm btn-danger" ng-click='purchasedItems.removePurchasedItem(purchasedItem)'>Verwijderen</button><br>
</div>
<div class='popover-menu-right-template'>
<div class='number-content' style="display: none;">
<div ng-controller="numpadCtrl as numpad" class="numpad">
<!-- edited for brevity -->
<input type="button" class="btn btn-sm" value="enter" data-item-id='{{purchasedItem.code}}' ng-click="numpad.flushNumber(purchasedItem.code)"></input>
</div>
</div>
</div>
<div class='popover-menu-right-content'>
</div>
</div>
</div>
</div>
the problem lies with this particular piece of code:
<input type="button" class="btn btn-sm" value="enter" data-item-id='{{purchasedItem.code}}' ng-click="numpad.flushNumber(purchasedItem.code)"></input>
The data-item-id attribute was added just to see if I could get retrieve the code - this works just fine! But when I set a breakpoint in the flushNumber function, the parameter passed is undefined.
The code for the numpad controller is the following (it doesn't really do anything yet)
sportOaseControllers.controller('numpadCtrl', ['$scope',
function($scope) {
var parent = this;
parent.currentEntry = "";
parent.addNumber = function (number) {
parent.currentEntry = parent.currentEntry.concat(number);
console.log(parent.currentEntry);
};
parent.cancel = function() {
console.log('cancelled!');
parent.currentEntry = "";
};
parent.flushNumber = function(code) {
console.log('flushing!' + code);
}
}]);
The only thing which might be a clue is that the $scope injected into the numpad controller is null - my knowledge of angular is too minimal to do any real debugging however. Can somebody point me in the right direction?
This is a working JSFiddle, what AngularJS version do you have?
controller as was introduce since 1.2 it's not available before that.
HMTL:
<div ng-app ng-controller="FirstCtrl">
{{purchasedItem}}
<div ng-controller="numpadCtrl as numpad" class="numpad">
<!-- edited for brevity -->
<input type="button" class="btn btn-sm" value="enter" data-item-id='{{purchasedItem.code}}' ng-click="numpad.flushNumber(purchasedItem.code)"></input>
</div>
</div>
JS:
function FirstCtrl($scope) {
$scope.purchasedItem = {
code: 'hello'
};
}
function numpadCtrl($scope) {
this.flushNumber = function (code) {
console.log(code);
}
}

How do I set the ui.bootstrap collapse feature to collapsed on page load?

I'm using ui.bootstrap and I'm trying to use the collapse function.
In my code what's happening is the default state is expanded, when the load weather button is clicked the div is collapsed and after the data has been loaded it expands.
What do I need to do to my code to get it to have a collapsed state on page load and only expand/contract when the button has been clicked?
Here's my code:
<div ng-controller="weatherCtrl">
<div>
<button ng-model="hotel" class="btn btn-default"
ng-click="loadWeather(hotel); isCollapsed = !isCollapsed">load weather</button>
<hr>
<div collapse="isCollapsed">
<div class="well well-lg">Some content</div>
</div>
</div>
</div>
.controller('weatherCtrl', function ($scope, weather) {
$scope.loadWeather = function (hotel) {
console.log('loadWeather')
console.log('hotel')
console.log(hotel)
weather.get({}, function (data) {
console.log('data')
console.log(data)
$scope.isCollapsed = false;
})
}
})
I got it working by using ng-init on a parent element:
<div ng-controller="weatherCtrl" ng-init="isCollapsed = !isCollapsed">
<div>
<button ng-model="hotel" class="btn btn-default"
ng-click="loadWeather(hotel)">load weather</button>
<hr>
<div collapse="isCollapsed">
<div class="well well-lg">Some content</div>
</div>
</div>
</div>

Resources