How do you execute directive code after rendering? - javascript

I'm trying to build a directive that runs after a nested ng-repeat has completed rendering. Here's what I've tried (fiddle):
The HTML:
<div ng-app="MyApp" ng-controller="MyCtrl">
<ul my-directive>
<li ng-repeat="animal in animals">{{animal}}</li>
</ul>
</div>
And the JavaScript:
angular.module("MyApp", [])
.directive("myDirective", function () {
return function(scope, element, attrs) {
alert(element.find("li").length); // 0
};
});
function MyCtrl($scope) {
$scope.animals = ["Dog", "Cat", "Elephant"];
}
The linking function in my custom directive runs before all of the <li> elements have been rendered (and 0 is alerted). How do I run code after the ng-repeat has completed rendering?

You could move the directive inside ng-repeat, http://jsfiddle.net/ZTMex/3/
<div ng-app="MyApp" ng-controller="MyCtrl">
<ul>
<li ng-repeat="animal in animals" my-directive>{{animal}}</li>
</ul>
</div>
Or have a look at https://stackoverflow.com/a/13472605/1986890 which is related to your question.

Related

ng-bind-html renders data as HTML but ignores children elements

I'm working on this function that generates HTML based on user input and I can render it as proper HTML inside a list item (instead of a string version) using ng-bind-as-html.
The only problem is that it doesn't render the span tag that is also a child element in the li tag. I'm an Angular n00b and could use any insight ya got.
The code below allows me to have my functions output as proper HTML, which is nice, but I need that span tag to show up as it allows my user to copy the contents of the text.
Basically, I can either rewrite without ng-bind-html and have the span render appropriately, or I can have my HTML output render and not get the span tag. I'm stuck with one or the other and not both... and I want both. Classic.
Thanks for your help!
<li
class="entry"
ng-repeat="entry in output track by $index"
ng-mouseenter="onEnter()"
ng-bind-html="entry">
{{entry}}
<span
class="copy"
ng-click="copyData(entry)"
ng-mouseenter="onEnter()">
{{message}}
</span>
</li>
You can create a custom directive to achieve this with transclude and ng-transclude as below.
Documentation for ng-transclude
var app = angular.module('app', []);
app.controller('TestController', ['$scope', function($scope) {
$scope.message = 'You are welcome!';
$scope.output = ['<h1>Hello user</h1>'];
}]);
app.directive('bindHtml', ['$sce', function($sce) {
return {
restrict: 'A',
transclude: true,
template: '<span><span ng-bind-html="html"></span><span ng-transclude></span></span>',
link: function (scope, element, attrs) {
scope.html = $sce.trustAsHtml(scope.$eval(attrs.bindHtml));
}
};
}]);
angular.bootstrap(document, ['app']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="TestController">
<li
class="entry"
ng-repeat="entry in output track by $index"
ng-mouseenter="onEnter()"
bind-html="entry">
<span
class="copy"
ng-click="copyData(entry)"
ng-mouseenter="onEnter()">
{{message}}
</span>
</li>
</div>

Ng-click changing key,value pair

I have a ng-repeat like this:
<div ng-app="myApp" ng-controller="Ctrl">
{{ctrlTest}}<hr/>
<div ng-repeat="elements in filter">
<div>
<li ng-repeat="(key,value) in filter.producers" ng-show="value">
{{key}}<a ng-click="filter.producers.key=false"> X</a>
</li>
</div>
{{filter.producers}}
</div>
angular.module('myApp', [])
.controller('Ctrl', function($scope) {
$scope.ctrlTest = "Filters";
$scope.filter = {"producers": {"Ford":true,"Honda":true,"Ferrari":true}}
});
I am trying to make a ng-click that would set each label to false when clicking in a link, but I haven't achieved to do it properly as the key values are not fixed (they should be treated as variables).
So far I have tried it his way.
http://jsfiddle.net/Joe82/wjz8270z/5/
Thanks in advance!
Ps: I cannot change the json structure.
You just need to access the element of object by its key, to ensure that there references would not get lost & binding will work
<li ng-repeat="(key,value) in filter.producers" ng-show="value">
{{key}}<a ng-click="filter.producers[key]=false"> X</a>
</li>
Forked Fiddle
You also call a function and set value false
HTML
<li ng-repeat="(key,value) in filter.producers" ng-show="value">{{key}} {{value}}<a ng-click="setValue(key)"> X</a>
JS
$scope.setValue = function(key){
$scope.filter.producers[key.toString()] = false;
}
see this link http://jsfiddle.net/wjz8270z/8/

Angularjs - repeater appending elements in different parents in view

I know that if I use the directive ng-repeat, like below, I get every element inside and including the div to repeat on the DOM.
<div class="col col-3" ng-repeat="movie in popular" >
<figure>
<img ng-src="{{movie.backdropURL}}" alt="{{movie.code}}">
<div class="overlay"></div>
<figcaption>{{movie.code}}</figcaption>
<!-- <span class="extra-info">{{movie.extra}}</span> -->
<span class="price">{{movie.price}}</span>
</figure>
</div>
However now I want to have some parent elements that won't repeat but will use the same scope object for their children, that will then repeat.
So, I would like to do a repeater that would append the properties of the scope into their parent, something like this:
<ul class="parent1">
<li><img src={{myScope[0].imgUrl}}></li>
<li><img src={{myScope[1].imgUrl}}></li>
<li><img src={{myScope[2].imgUrl}}></li>
</ul>
<div class="parent2">
<span>{{myScope[0].description}}</span>
<span>{{myScope[1].description}}</span>
<span>{{myScope[2].description}}</span>
</div>
I would like to know if it is possible to reuse a native angular directive (I would prefer not to run the same repeater every time for every parent) where it could append the element to the parent. If not, do you have any suggestion for a solution. I've looked up some links for custom directives I haven't succeeded in applying them. So if you have a 'beginners' custom directive tutorial that could help me go on the right direction, it would be highly appreciated.
I don't know if I understand exactly what you are asking for.
Anyway, the ngRepeat directive is placed in the DOM under a particular parent, so you cannot run it once and append the leaves under different parents. The only way to do that is to create a custom directive that runs a loop internally and sets the leaf under the parent of your choice.
That is:
angular
.module('mymodule')
.directive('mydirective', mydirective);
function mydirective(){
var directive = {
restrict: 'A'
, link: link
}
return directive;
function link($scope, elem, attrs) {
for(var i=0;i<$scope.myScope.length;++i){
var el1 = angular.element('<li><img src='+$scope.myScope[i].imgUrl+'></li>'),
el2 = angular.element('<span>'+$scope.myScope[i].description+'</span>');
elem.find('.parent1').append(el1);
elem.find('.parent2').append(el2);
}
}
}
Please let me know if I misunderstood your goal.
Check this:
HTML:
<div ng-app="myApp" ng-controller="myCtrl">
<ul class="parent1">
<li ng-repeat="item in myScope">
<img ng-src={{item.imgUrl}}>
</li>
</ul>
<div class="parent2">
<p ng-repeat="item in myScope"><span>{{item.description}}</span></p>
</div>
</div>
Controller:
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope) {
$scope.myScope = [
{imgUrl:"someUrl1", description: "this is first url"},
{imgUrl:"someUrl2", description: "this is second url"}
]
}]);
Acceptable :) ?

how to pass data from controller to directive using $emit

I had strucked in passing value from controller to directive
I have two arrays in my controller
$scope.displayPeople.push(data.userName);
$scope.displayOrg.push(data.orgname);
i need to pass these data from controller to directive
my directive
<div>
<div class="multitext-wrap blue-border">
<ul inputfocus>
<!--<li class="tag" ng-repeat="list in selecteditemsdisplay track by $index" ng-class="{selected: $index==selectedIndex}" >-->
<!--<span class="tag-label">{{list}}</span><span class="tag-cross pointer" ng-click="Delete($index,selecteditemslist[$index],list,searchid)">x</span>-->
<!--</li>-->
<li class="tag" ng-repeat="list in displayItems track by $index" ng-class="{selected: $index==selectedIndex}" >
<span class="tag-label">{{list}}</span><span class="tag-cross pointer" ng-click="Delete($index,selecteditemslist[$index],list,searchid)">x</span>
</li>
<li class="">
<input type="text" ng-model="searchModel" ng-keydown="selected=false" ng-keyup="searchItem(searchModel,searchobj)"/>
</li>
</ul>
</div>
<div class="typeahead" ng-hide="!searchModel.length || selected">
<div class="typeahead" ng-repeat="item in searchData | filter:searchModel | limitTo:8" ng-click="handleSelection(item,searchobj,$index,searchid)" style="cursor:pointer" ng-class="{active:isCurrent($index)}" ng-mouseenter="setCurrent($index)">
<div class="bs-example">
<div class="list-group list-group-item active">
{{item.displayConfig[0].propertyValue}} {{item.displayConfig[1].propertyValue}}
</div>
</div>
</div>
</div>
</div>
I was using $emit to send
in controller
$rootScope.$emit("displayEvent", {displayItems: $scope.displayPeople});
$rootScope.$emit("displayEvent", {displayItems: $scope.displayOrg});
in directive
$rootScope.$on('displayEvent', function (event, args) {
$scope.displayOrgs = args.displayItems;
console.clear();
console.info($scope.displayOrgs);
});
by doing this i getting duplicates in place of org (both people and org wher coming )
how can i solve this problem please hepl me thanks in advance
By declaring 'scope: false' you´re able to access the controller´s scope in your directive. 'false' means 'do not create an isolated scope, inherit the controllers'.
.directive('myDirective', function() {
return {
scope: false,
link: function($scope){
//Do stuff with $scope.displayOrgs
//Do stuff with $scope.displayPeople
}
};
});
This option will create an isolated scope and inherits the selected variables. This is a cleaner way of doing it.
.directive('myDirective', function() {
return {
scope:{
displayPeople:'=',
displayOrg :'=',
},
link: function($scope){
//Do stuff with $scope.displayOrgs
//Do stuff with $scope.displayPeople
}
};
});
using $emit for communication between controller and directive is not a preferable.
you need to use "=" scope of directive to allow two-way communication between controller and directive like:
directive
angular.module('YourModuleName').directive('yourDirectiveName',function () {
return{
restrict:'E',
scope:{
displayPeople:'=',
displayOrg :'=',
},
link: function postLink(scope, element, attrs) {
}
}
});
template respective to controller
<yourDirectiveName displayPeople="displayPeople" displayOrg ="displayOrg "></yourDirectiveName >

Angular ngchange doesn't fire for <pre contenteditable="true">

I'm trying to get a tag to fire the ng-change event but to no avail. The purpose is to watch changes to post.content. The code is pretty straightforward and parts of it are omitted for brevity, but it's basically a ng-template that is repeated like so:
<li ng-repeat="post in posts" data-id="[[post.id]]" data-rank="[[post.sortrank]]" class="post">
<ng-include src="post.template"></ng-include>
</li>
The repeated template:
<script type="text/ng-template" id="postText.html">
<pre ng-blur="savePost($event, post)" ng-change="changePost()" ng-model="post.content" class="postText" contenteditable="true">[[post.content]]</pre>
</script>
The JS:
$scope.savePost = function($event, post) { //This fires correctly
console.log($event);
console.log(post);
};
$scope.changePost = function() { //This doesn't fire at all
console.log("changed!");
};
Can this be because of the contenteditable attribute? Because I can get the ngChange examples working on the Angular tutorial site.
You can use akatov's angular-contenteditable.js.
From the docs... JS:
angular.module('myapp', ['contenteditable'])
.controller('Ctrl', ['$scope', function($scope) {
$scope.model="<i>interesting</i> stuff"
}])
HTML:
<div ng-controller="Ctrl">
<span contenteditable="true"
ng-model="model"
strip-br="true"
select-non-editable="true">
</span>
</div>
Plunker

Categories

Resources