im very new to AngularJS and now I wrote my first directive.
The directive represents a table row and the markuplooks like the following:
<table>
<!--thead ...-->
<tbody>
<my-directive ng-repeat="(key, value) in object"></my-directive>
</tbody>
</table>
But the directive is renderd / printed out before the sorrounding table tags.
So my DOM looks like this:
<!-- multiple times -->
<my-directive></my-directive>
<table>...</table>
Heres my directive definition
return {
restrict: "E",
templateUrl: "/my-directive.html",
link: function(scope, element, attrs){
}
};
So why does this happen?
Other than tr, td, tbody, th elements never gonna work inside the table element. You should really use attribute directive so that it can support inside table to by doing restrict: "A", in directive.
<tbody my-directive ng-repeat="(key, value) in object">
</tbody>
If you want to have the other than table element to be there, then they could be placed inside th & td
Kind of going along with what Pankaj said, you can't call the directive from inside the table, its invalid HTML.
However, I can understand why you would want to use restrict: 'E'. Personally, I like using my directives as elements.
So, if you want to still use the directive as an HTML element The simplest fix to this would be to have your directive itself render the whole table. It could be a <table-for/> directive, or something like that.
This has been my workaround for this problem. Markup is markup at the end of the day, and you still have to play by the rules.
So I came to the following approach:
<tbody>
<tr my-directive ng-repeat="..."></tr>
</tbody>
And I restricted my directive to argument and it works. Thank you for your help.
Related
I'm creating a directive with the following template (simplified):
<table>
<tr>
<td>
<input type="text"/>
</td>
<td>
<input type="text"/>
</td>
</tr>
</table>
In the directive's link function I want to add listeners to the inputs, but I can't get the input elements. The directive looks like the following:
angular.module('app').directive('myDirective', function(){
return{
restrict: 'E',
templateUrl: '<path-to-above-html-file>',
link: function(scope, element, attr){
var inputs = element.find('input'); // Returning empty JQLite object
}
};
});
According to the Angular element documentation, the find() method should be able to find nested elements. But why does this not work then?
I've tried to print the element in the console and loop trough all child elements, and the inputs do exist.
Any help is appreciated!
If you want to access element,then use querySelector().But beware,querySelector only returns first child element.
If you want to get all elements use querySelctorAll() like below
element.querySelectorAll("input")
I've been looking all over the internet for something like this and I still can't find the answer.
I have a directive that is reused throughout my application, it allows the user to sort and search through lists of items. I have multiple kinds of items that can be passed in to the directive (html templates that I pass in) as well as multiple uses for those templates. I.e, sometimes I want a certain button on that template, but sometimes I want another. (This will make more sense in a minute).
Therefore I have created multiple directives with transclusion in order to achieve this. However, I'm having serious issues with scoping and I can't seem to figure out exactly how to pass the isolated scope to the child directive.
Below is my code:
Item List Directive
var app = angular.module('main');
app.directive('itemList', function(){
var linkFunction = function (scope, element, attributes, ctrl, transclude) {
//Things that modify the scope here. This scope is what I want to pass down to the child directives
//NOTE: I do not currently have a working transclude function which is why I didn't include it here because I have no idea what to do with it
scope.pagedItems = groupItemsToPages(items);
scope.currentPage = 0;
};
return {
restrict: 'E',
replace: 'true',
transclude: true,
templateUrl: 'partials/directives/item-list.html',
link: linkFunction,
scope: {
searchPlaceholder: "#",
items: "=",
control: "="
}
};
});
item-list.html
<div class="form-group">
<!-- I won't put all of the html here, just some to show you what i'm going for -->
<div class="search-field">
<input type="text" ng-model="query.value" placeholder="{{searchPlaceholder}}/>
</div>
<table class="table table-hover">
<tbody>
<tr ng-repeat="item in pagedItems[currentPage]">
<td ng-transclude></td>
</tr>
</tbody>
</table>
</div>
Here's the directive that simply returns the URL of whatever template is passed to it. This is so that I can add in an extra html through further nested transclusions.
item-template.js
var app = angular.module('main');
app.directive('itemTemplate', function() {
return {
restrict: 'AE',
replace: 'true',
transclude: true,
templateUrl: function(tElement, tAttrs){
return tAttrs.templateUrl;
}
};
});
Here's an example template (extremely simplified again, just to show you the layout)
profile-template.html
<div>
<p>item.name</p>
<p>item.description</p>
</div>
<div ng-transclude></div>
Here's an example of the HTML that calls this code
tab.html
<div class="tab">
<div class="available-items">
<item-list control="profileControl" search-placeholder="Search Profiles" items="profileControl.profiles">
<item-template template-url="partials/profile-template.html">
<button type="button" ng-click="launchProfile(item.id)">Launch Profile</button>
</item-template>
</item-list>
</div>
</div>
So now that you've seen the code. The issue I'm having is that my profile-template.html inclusion isn't getting the scope from the directive above it even though I've tried cloning the scope to it. All the examples I've seen require you to remove the template key from the directive and assume you're only returning the code you have in your transclusion. In my case, I have other html that I want to display in the template.
Any help would be appreciated
Instead of trying to pass the scope between your directives, you can make use of the $parent attribute and get access to higher scopes.
For instance, in your item-template controller you could gain access to the higher scope from item-list with a code like this:
if ($parent.searchPlaceholder === '') {
...
}
Say in my controller somewhere I have:
$scope.elements = [document.getElementById('a'), document.getElementById('b')];
and I have valid elements somewhere in the document with IDs of a and b.
I'd like to interpolate these elements directly in an HTML template, without writing JavaScript. I tried the following, and it did not work.
<div ng-repeat="e in elements">
{{ e }}
</div>
Is this possible?
More information about what I'm doing:
I have content (several custom directive elements which load up their own data via AJAX) that I want to disperse between several columns. The column of the content elements will change, and the number of columns will change.
<column-resizer options="columnResizerOptions">
<content1></content1>
<content2></content2>
...
</column-resizer>
The template for columnResizer currently looks like this:
<ng-transclude></ng-transclude>
<div ng-repeat="column in columns">
<div ng-repeat="element in column">
{{ element }}
</div>
</div>
columnResizerOptions is information about how to resize the columns and where to place the content in the columns. In the link function for the columnResizer, I use transclude to grab content1-contentn and place them in arrays corresponding to the column they should be in, which I ngRepeat through in my template (above).
Not sure why you wouldn't treat your whole app the "Angular way", but you could write a directive to do this:
angular.module('demoApp', [])
.directive('interpolateHtml', function() {
return {
restrict: 'A',
scope: {
htmlElement: '='
},
link: function postLink(scope, element) {
element.append(scope.htmlElement);
}
}
})
And use it in your HTML like this:
<div ng-repeat="e in elements">
<div interpolate-html html-element="e"></div>
</div>
Here's a working plnkr: http://plnkr.co/edit/5NvMA1x0C8TcwdLO2FNK?p=preview
It is possible.
$scope.elements = [ (document.getElementById('a') || {}).outerHTML ];
and
<div ng-repeat="e in elements">
<div ng-bind-html="e"></div>
</div>
You won't get data binding this way. You can use innerHTML or jqLite html() instead to get rid of extra wrappers.
get them into the DOM without using append, as it would be cleaner.
It wouldn't. A directive with nested directives or with DOM modifications in link is proper way in this case, you don't have to use data binding everywhere just because Angular promotes it.
So I am basically creating a table with nested ng-repeat.
<div ng-controller="PresetManageController">
<table>
<in-preset ng-repeat="preset in presetManage.presets"></in-preset>
</table>
</div>
And the template of directive in-preset is
<tr>
<td>{{preset.name}}</td>
<td>一些属性</td>
<td>
编辑
删除
</td>
</tr>
Directive declaration
module.directive('inPreset', function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'scripts/directive-templates/in-preset.html'
}
});
But when I open the page, I found that all trs jumped outside of the table.
If I change table to div, then it works fine. I have checked all html tags are perfectly closed. It is damn hard to debug this issue. Any clues?
The issue is because you are using ng-repeat on a custom directive (set to replace). There are a few weird behaviors with ng-repeat and replacing directives (see related post).
Try to leave the <tr> in your html and change your directive to be on an attribute instead.
html
<table>
<tr in-preset ng-repeat="preset in presets"></tr>
</table>
js
.directive('inPreset', function() {
return {
restrict: 'A',
template: '<td>{{ preset.name }}</td>',
};
})
See the Fiddler
I use sockte.io to get my data from the server, and depends of the data(item.text) I have to change the images.
I tried to acces to the DOM and modify the image using a Directive but no luck.
This my code:
HTML:
<table>
<tr ng-repeat="item in items" myDirective>
<td><img ng-src="{{myImages}}" ></td> <td>{{item.text}}</td>
</tr>
</table>
JS:
App.directive('myDirective', function(){
return {
link: function($scope, elm){
console.log('eee', elm);
elm[0]['children'][0]['children'][0].attr('src',"/url/to/the/image");
}
}
});
How can I fix that, or if there is a better way to do that.
UPDATE:
At a given time the images are not the same, I want to acces to the index of each image using their index.
UPDATE:
I added this example, how can I modify for example the image that have 'text 2'
You shouldn't need a directive for this. Use ng-src instead of the normal src attribute for images.