jQlite .find() traversing not working - javascript

I'm using an angular directive and I am not having any luck with the jQlite .find() method:
DIRECTIVE
function cardsList () {
return {
restrict: 'A',
controller: 'CardsController',
templateUrl: 'app/directives/cards-list/cards-list.html',
link: function ($scope, $element, attr, CardsController) {
var cardLink = $element.find('a');
console.log(cardLink);
});
}
}
}
contextCards.directive('cardsList', cardsList);
An empty [] gets logged on the console.
TEMPLATE
<li data-ng-repeat="card in cards" class="cards--item">
<a class="cards--link" data-ng-href="#/{{ card.slug }}">{{ card.title }}</a>
</li>
VIEW
<ul class="col-xs-12 cards--list" cards-list></ul>
All I want to do is traverse to the <a> elements. According to the docs, .find() only works on tag names which is exactly what I'm trying to do.
EDIT: I want to add a class to the <a></a> if the card the link represents is selected (like .current-card)

From your answer it's not clear how the selected card is specified in the model, so I am assuming that the card object (the object of each iteration of ng-repeat) holds this flag, for example: card.isSelected.
Then, you could use ng-class to specify which CSS class to add based on this value:
<li ng-repeat="card in cards" class="cards--item">
<a class="cards--link"
ng-class="{'current-card': card.isSelected}"
ng-href="#/{{ card.slug }}">{{ card.title }}</a>
</li>
Addendum:
The answer to your original question about why .find("a") returns empty, it is because ngRepeat directive transcludes its content (which means that Angular takes the elements out of DOM during compilation), and places it at a later stage than your link function.

Related

Angular Directive placed on ng-repeat not removed from ng-repeat

I have an AngularJS directive meant to interact with notifications pushed from a server. The problem I am having is that currently the method I am using is to have an ng-repeat which keeps track of all the notifications during the session, and then displaying them as they are added to the array. What I want to happen, is that an alert comes, it's added to the DOM, it stays for a couple seconds, and removes itself from the DOM. At this point the issue is that the element will hide, but it will not remove itself from the DOM. Being that the element is absolutely positioned, after the fadeout is prevents me from accessing item that it is in front of. I assumed that element.remove() and the element.$destroy() would do the trick, but it seems like the element is not being from the ng-repeat or possibly that the $scope of the directive is not being deleted. Any help would be greatly appreciated.
angular.module('bmuApp').directive('messageNoti', function($timeout){
return {
scope: {
alert: '=messageNoti'
},
replace: true,
restrict: 'EA',
templateUrl: 'partials/authenticated/homepage/alerts.html',
link: function(scope, element, attrs) {
$timeout(function(){
element.addClass('fadeOut');
$timeout(function(){
element.remove();
}, 500);
}, 5000);
element.on('$destroy', function () {
scope.$destroy();
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!------------>
<!-- Alerts -->
<!------------>
<div class="alert-container" ng-cloak>
<a class="alert animated fadeInUp" ng-repeat="alert in alerts" ng-href="messages/{{ alert.user.thread_id }}" message-noti="alert">
</a>
</div>
<!-----Template for Alert------>
<div class="row">
<span class="close">
<i class="glyphicon glyphicon-remove"></i>
</span>
<div class="col-xs-12 text-left">
<span class="profile-image" style="background-image:url('https://www.blackmarketu.com/{{ ::alert.user['profile-picture'] }}');"></span>
<h5> {{ ::alert.user["user-firstname"] }} {{ ::alert.user["user-lastname"] }} </h5>
<p>{{ ::alert.message }}</p>
</div>
</div>
You don't need to interact with the DOM directly to remove it. Just splice (remove) that array member from the array.
So your logic is a bit wrong, because you have an array of alerts and you are not removing them, you just want to remove the DOM element.
If you don't want to change your code too much and add a parent directive to handle this, you can pass both "alert" (current array member) and "alerts" (entire array of alerts) to your directive and then in your timeout callback instead of removing the DOM element, you splice that array member from the array.
Remove replace: true. Since the ng-repeat directive creates inherited scopes and your directive creates isolate scope, they won't play well together on the same element.
From the Docs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)
specify what the template should replace. Defaults to false.
true - the template will replace the directive's element.
false - the template will replace the contents of the directive's element.
-- AngularJS Comprehensive Directive API
From GitHub:
Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".
-- AngularJS Issue #7636

ng-transclude works differently for element and attribute directives [duplicate]

I want to create a wrapper directive that would serve as the frame for a notification widget in a list. In that frame I want to transclude later some specific content based on a property from the notif object. Currently I hard coded a div element.
I have the following in index.cshtml:
<div ng-controller="notificationCenterCtrl">
<ul>
<li ng-repeat="notif in allNotifications">
<notification-base notification="notif" remove="removeNotification(notif)">
<div style="background-color: fuchsia">bla bla</div>
</notification-base>
</li>
</ul>
</div>
This is the directive spec:
ns.directive("notificationBase", function() {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: '/Scripts/notification-base.html',
controller: 'notificationBaseCtrl',
scope: {
notification: '=',
removeNotif: '&remove'
}
};
});
Why does the following work, that is it displays the transcluded div in fuchsia background?
<div>
My notification title: {{notification.name}}
<div ng-transclude id="notificationContent">
</div>
<a ng-click="remove()">Remove</a>
</div>
...but if i use it like an element the fuchsia div no longer shows up.
<div>
My notification title: {{notification.name}}
<div id="notificationContent">
<ng-transclude></ng-transclude>
</div>
<a ng-click="remove()">Remove</a>
</div>
What is the difference if I use ng-transclude as an attribute or an element. (I use Firefox).
Only recently (since this pull request) ngTransclude directive supports usage as element.
You are probably using angular with version < 1.3 where ngTransclude used as element is not supported - the primary reason for this being IE8 support.

$http.get on HoverOver to populate PopOver text

I'm trying to implement a feature using Angular and Boostrap where the user can get a popOver on an item in the list, and have it perform an angular factory $http.get function to retrieve data and populate the popover text.
I'm not sure this is the best approach, but I have a ng-repeat like so:
<ul>
<li ng-repeat="product in products">
<model-popover ng-attr-id="{{product.Id}}"></model-popover>
</li>
</ul>
And my best guess is to use an angular directive, taking in the id number as a scope attribute,and performing a factory call from the directive. I've read up on the controller/link functions within the directive, but not sure the proper implementation
app.directive('modelPopover', ['Factory', function (Factory) {
return {
restrict: 'E',
replace: true,
scope: { id: "=" },
controller: function($scope){
var prod = Factory.getProductDetail(id);
},
template: '<a popover-placement="bottom" popover="{{prod}}">{{prod}}</a> '
};
}]);
I know the directive is incorrect, but i'm hoping there's enough information to help me out. Thanks in advance!
You do not need special directive for this, due to value-binding u can just change scope variable and popover will change also.
So u simply:
<button popover="{{var}}" popover-trigger="mouseenter" class="btn btn-default" ng-mouseover="changeVar()">Mouseenter</button>
And in changeVar you can change $scope.var any way you want.
Here is example plunk ($http call emulated using $timeout):
http://plnkr.co/edit/gnm1BtnHzNLnvO62Ar2i?p=preview
This var prod = Factory.getProductDetail(id);
has to be changed to $scope.prod = Factory.getProductDetail(id) if you want to use the mustaches

AngularJS : How can I transclude an element into a template that uses ng-repeat?

I've got a carousel directive which includes some chunking to map the passed in array of items into an array of arrays of elements structure which then generates markup similar to the pseudo code below:
<array of arrays>
<array of items>
<item>
<item>
</array of items>
<array of items>
<item>
<item>
</array of items>
</array of arrays>
The angular template for this looks like this:
<div class="carousel__content" ng-style="carousel.containerWidth">
<ul class="carousel__chunk" ng-repeat="chunk in carousel.chunks" ng-style="carousel.chunkWidth">
<li class="carousel__item" ng-repeat="item in chunk" ng-style="carousel.itemWidth">
<div class="carousel__item-content">
[element should be transcluded into this spot.]
</div>
</li>
</ul>
</div>
Given my view code:
<!-- The <a> tag should appear inside the 'carousel.html' template's ng-repeat list. -->
<carousel items="items" config="config">
{{ item.name }}
</carousel>
I would want the transcluded element to bind to the item object of the deepest ng-repeat
A full Plunker with a reduced test case is available here: http://plnkr.co/edit/kYItcXV0k9KvnpiYx1iG
The problem is that I cannot put a ng-transclude attribute inside the ng-repeat and that (as the carousel.js directive file in the Plunkr shows) I can't seem to inject the to-be-transcluded markup manually into that spot either using the transclude() function in the compile step of the directive.
Any ideas would be much appreciated.
Set a variable with a reference to the transclude function in the link function of your existing directive:
post: function($scope, $element, attrs) {
$scope.transclude = transclude;
...
Then, create a new directive to use in place of ng-transclude on the element you wish for the transcluded content to appear inside of:
.directive('innerTransclude', function(){
return function(scope, elem, attrs){
scope.transclude(scope, function(clone){
elem.append(clone);
});
}
})
This directive simply appends the clone to the element, while avoiding the issue with trying to use transclusion inside of an element which uses transclusion itself. Don't forget to add it to your template:
<div class="carousel__item-content" inner-transclude></div>
Demo

AngularJS - passing an interpolated html fragment to a directive attribute

I have a controller that receives a json list of items and repeats them in my view. I am using a variation of the bootstrap popover and would like to insert the order ID within the sub template defined my $scope.popover and have it parsed for variables. I read that the square brackets was the way to go but it does not seem to work for me.
function ManageOrderCtrl($scope, $http) {
$scope.subtemplate = '<input class="hidden" value="[order._id]">';
$http.get('/api/orders').
success(function(data, status, headers, config) {
$scope.orders = data.orders;
});
};
}
Template (is actually a directive - a variation of the bootstrap popover):
<li ng-repeat="order in orders">
{{ order._id }}
<li>
You can try something like
<li ng-repeat="order in orders">
{{ order._id }}
<li>
Instead of defining the template in controller. You can also create templates using ngInclude and include them in the html.

Categories

Resources