directive only works inside another directive - angularjs

i made a directive for filtering data. It uses scope: true and no transclusion. Somehow it only works inside an ng-switch. Maybe also other directives but i havent tried.
My html looks like this:
<my-filter source="{{foodArray}}" filter="reaction,category"> // source and filter are properties on the directive scope
<div>
<div class="span3">
<table class="table table-bordered">
<tr data-ng-repeat="item in filters.reaction"> // filters property of directive scope
<td><input type="checkbox" data-ng-model="item.isChecked" data-ng-change="filterCtrl.filterList()"></td>
<td>{{ item.value }}</td>
</tr>
</table>
</div>
</div>
<table>
<tbody>
<tr data-ng-repeat="food in filteredData"> // filteredData object on the directive scope
// interating through filteredData
</tr>
</tbody>
</table>
</my-filter>
here is my directive and its controller:
angular.module('myModule', [])
.directive('myFilter', function() {
return {
restrict: 'E',
scope: true,
controller: ["$scope", function ($scope) {
var filterCtrl = this;
$scope.filters = {};
filterCtrl.inputArray = [];
filterCtrl.filterList = function() {
/* some code where tmp array is created */
$scope.filteredData = tmp;
}
}],
controllerAs: 'filterCtrl',
link: function(scope, element, attrs, filterCtrl) {
filterCtrl.inputArray = angular.fromJson(attrs.source);
scope.filteredData = filterCtrl.inputArray;
// ...
}
}
});
FilteredData and filters are property of the directives scope. Now when i remove the ng switch around it the data is empty. Also the scope.source property can be an array or an object. When i remove the ng switch and give it an object as source it actually throws a syntax error: SyntaxError: Unexpected end of input
at Object.parse (native)
at Object.fromJson
Which it doesnt throw when i use an array.
Not sure what to make of this. If anybody had this problem before i would love to hear from you.

Related

How to use angularjs directive to append HTML content to thead in table?

I want to append <tr><button></button></tr> to the <thead>in the below html.
<table>
<thead>
<tr>
Header 1
</tr>
</thead>
</table>
Is it possible to achieve the idea above using angular directives?
in your linking function of your costum directive you can use
angular.element(document).find("thead").append("<tr><button></button></tr>")
your directive could look like this:
angular.module('app', [])
.directive('myDirective', ['$document', function($document) {
return {
link: function(scope, element, attr) {
angular.element(document).find("thead").append("<tr><button></button></tr>")
}
};
}]);
carefull i havent tested this code but this is the way to manipulate DOM in custom directives ,i will make a plunker when i can

how to use filter and orderby with isolated directive?

I make a custom directive with isolated scope and is get data also in that directive but now my filter and orderby not working here is my directive:
<div my-data remoteurl='url' filter='test'>
</div>
Controller:
(function() {
'use strict';
var myApp = angular.module('myApp', [])
.controller('myAppCtrl', ['$scope', '$http', function($scope, $http) {
$scope.url = 'https://www.reddit.com/r/worldnews/new.json';
$scope.filter= 'test';
$scope.orderBy= 'sortExpression';
}])
.directive('myData', ['$http', function($http) {
return {
restrict: 'A',
scope: {
remoteurl: '=',
filter: '=',
orderBy: '='
// orderBy:'sortExpression':'order' ;
},
templateUrl: 'DataTable.html',
link: function(scope, element, attr) {
$http.get(scope.remoteurl)
.success(function(response) {
scope.names = response.data.children;
});
}
};
}]);
})();
DataTable.html
<ul>
<li >
<table width="80%" id="dataTable" align="center" name="table1">
<tr>
<td><strong>Title</strong></td>
<td><strong>Link</strong></td>
<td><strong>Score</strong></td>
</tr>
<tr ng-repeat="x in names |filter:test|orderBy:sortExpression:order ">
<td id="title"><a ng-href="{{ x.data.url}}">{{x.data.title}}</a></td>
<td ><a ng-href="{{ x.data.url}}">Link</a></td>
<td>{{x.data.score}}</td>
</tr>
</table>
</li>
i am passing all parameters but only url is working filter and orderby is not working can anyone correct me?
It has nothing to do with the isolated scope. In general you are passing the variables in a correct way - from the controller into the directive via the =-notation.
You forgot to pass the orderBy to the directive, as well as understanding how filters and orderBy generally work. Please take a look into the documentation for filter and orderBy and look at the examples as well.
Corrected directive code, now with the controller's orderBy scope variable passed into the directive.
<div my-data remoteurl='url' filter='test' order-by="orderBy">
</div>
As I have already commented, your table rows code should look like this:
<tr ng-repeat="x in names | filter:filter | orderBy:orderBy">
In the controller I have set up the following filter and orderBy strings.
$scope.filter= 'obama';
$scope.orderBy= '-data.score'; //the "-" stands for reverse ordering
When you define a filter like this filter:test the filter is based on a scope variable called test, e.g. $scope.test. In your scope, the filter is inside the scope variable $scope.filter, so correct call of your filter is filter:filter.
I have set up a working plnkr here: http://plnkr.co/edit/FGZVaPrvbUcdvNRnKMk4?p=preview

How can I prevent ngClick to go through parent directive when nesting custom directives inside each other

This question follows on a previous question I had about getting these directives to work:
Previous question about dynamically generating a grid
My Html looks like this:
<div ng-grid ng-collection="entries" ng-collection-headings="headings" ng-button-click="theAction(inp)">
<div ng-checkbox-column></div>
</div>
It generates Html that looks like this:
<div ng-grid="" ng-collection="entries" ng-collection-headings="headings" ng-button-click="theAction(inp)" class="ng-isolate-scope">
<table class="table table-bordered">
<thead>
//Truncated for brevity
</thead>
<tbody>
<tr id="item0" ng-repeat="item in ngCollection" class="ng-scope">
<td ng-checkbox-column="">
<label>
<input type="checkbox" ng-model="item.checked" ng-click="tempButtonClicked()" class="ng-pristine ng-untouched ng-valid"> From the checkbox directive.
</label>
</td>
</tr>
//Truncated for brevity
</tbody>
</table>
</div>
The problem I am having is that the ng-click events of the ng-checkbox-column directive cannot be assigned from the myCtrl controller outside of the ng-grid directive. This is because I created an isolate scope for the ng-grid directive and the ng-checkbox-column directive is sharing the scope of the ng-grid directive.
Is there a way around this?
What options do I have to get this to work?
Is my only option to use events?
The reason I created an isolate scope for the ng-grid directive is so that it can be used more than once in the same controller. Therefore I cannot share the scope of the myCtrl controller.
Here is a plunker showing the problem:
Plunker with working dynamic grid, but limited ng-click
Looking at the plunker, you can click on the buttons to see where the click happens in relation to the "scopes".
I would like the checkboxes to fire their own events and update that status without having to go through the ng-grid directive.
Currently I am handling the ng-click with this:
ng-click='tempButtonClicked()'
and in the ng-checkbox-column controller:
$scope.tempButtonClicked = function () {
var val = "From the checkbox directive";
$scope.buttonClicked(val);
};
where $scope.buttonClicked(val); is a reference to a function defined in the controller of the ng-grid directive:
$scope.buttonClicked = function (inp) {
if (typeof inp != 'undefined')
inp = inp + ", through the grid directive.";
else
inp = "From the grid directive.";
$scope.ngButtonClick({ inp: inp });
};
which in turn, binds it to a function specified through an isolate scope variable defined in the ng-grid directive as:
scope: {
ngButtonClick: "&"
},
I hope what I'm explaining makes sense and someone can shed some more light on the subject.
What I would like to have:
Is a way to bind a function to the ng-checkbox-column directive so that I can call a function in the myCtrl controller when it is clicked.
The reason for this is that I may have multiple column templates in a grid and perform different actions depending on which column is clicked.
With the current way it works, I'll have to define x-amount of functions in the ng-grid directive to use with the templates, which seems like a sloppy way of doing it.
I would LOVE to be able to do this:
<div ng-checkbox-column ng-click-action="someDefinedFunctionInMyCtrl()"></div>
which generates this:
<td ng-checkbox-column="">
<label>
<input type="checkbox" ng-model="item.checked" ng-click="someDefinedFunctionInMyCtrl()"> From the checkbox directive.
</label>
</td>
Got it working using an expression as an attribute and ng-transclude:
http://plnkr.co/edit/3XmsE3d44v8O0AxOoUeF?p=preview
JS:
.directive("ngGrid", function () {
return {
[...]
transclude: true, //added transclude
template: function (element, attrs) {
var children = element.html();
children = children.trim().replace(/div/g, "td");
var htmlText = [
"<input type='button' ng-click='buttonClicked()'",
" value='From the grid directive' />",
"<table class='table table-bordered'>",
"<thead><tr>",
"<th ng-repeat='heading in ngCollectionHeadings'>{{heading}}</th>",
"</tr></thead>",
//added ng-transclude
"<tbody><tr id='item{{$index}}'",
" ng-repeat='item in ngCollection' ng-transclude>",
children,
"</tr></tbody>",
"</table>"
].join('');
return htmlText;
},
[...]
};
})
.directive("ngCheckboxColumn", function () {
return {
restrict: "A",
scope: {
//expression as an attribute
myClick: '&'
},
[...]
controller: function ($scope, $element) {
$scope.tempButtonClicked = function () {
var val = "From the checkbox directive";
//call the expression (val will be available in the expression)
$scope.myClick({val: val});
};
}
};
})
HTML:
<div ng-controller="myCtrl">
<div ng-grid ng-collection="entries"
ng-collection-headings="headings" ng-button-click="theAction(inp)">
<!-- added the my-click expression (use val here) -->
<div ng-checkbox-column my-click="ctrl.theAction(val)"></div>
</div>
<p id="btnClickVal">{{actionVal}}</p>
<input type="button" ng-click="theAction(fromParent)"
value="From the parent controller" />
</div>

Controller is not a function, got undefined

I'm trying to create a directive on a controller. The directive will create a table of items returned from djangorestframework.
controller.js:
var expensesApp = angular.module('expensesApp', []);
expensesApp.controller('ShoppingListController', ['$scope', '$http',
function($scope, $http){
$http(
{
method: 'GET',
url: '/items/?format=json'
})
.success(function(data, status, headers, config) {
$scope.items = data;
})
.error(function(data, status, headers, config) {
});
}]);
directive.js
angular.module('expensesApp', [])
.directive('shoppingList', function () {
return {
restrict: 'A',
templateUrl: 'http://localhost:8000/static/angular/partials/shopping-list.html',
controller: 'ShoppingListController'
};
})
The partial being pulled in by the directive:
shopping-list.html
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>SHOP</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td>[[ item.id ]]</td>
<td>[[ item.name ]]</td>
<td>[[ item.shop ]]</td>
</tr>
</tbody>
</table>
and the main html page where I define the app and the controller.
items.html
...
<div class="container" ng-app="expensesApp">
<div class="row">
<div class="col-md-6" ng-controller="ShoppingListController">
<div shopping-list></div>
</div>
</div>
</div>
...
The headers of the table in the partial are being pulled into the page, but it's not executing the $http and fetching the items that should make up the content of the table. I get ShoppingListController not a function, got undefined
Everything works if I don't split the table out into a directive. All items are returned and I don't see the error in the console.
Anyone have any idea what I'm doing wrong?
You are redefining module when you create a directive. It should be:
angular.module('expensesApp')
.directive('shoppingList', function () {
return {
restrict: 'A',
templateUrl: 'http://localhost:8000/static/angular/partials/shopping-list.html',
controller: 'ShoppingListController'
};
});
If you pass an array as the second argument to module method angular.module('expensesApp', []), Angular creates a new module without ShoppingListController controller in it. You should use getter syntax angular.module('expensesApp') to retrieve previously created module.

angular directive for tables: bind transcluded elements with ng-bind-html

I'm trying to write a generic table directive like this:
<h-table rows="customers">
<h-column field="Id">
<a ng-click="editCustomer(row.Id)">{{row.Id}}</a>
</h-column>
<h-column field="Name">
</h-column>
</h-table>
That will generate the following html:
<table>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr>
<td>
<a ng-click="editCustomer(1)">1</a>
</td>
<td>
Alexandre
</td>
</tr>
...
</table>
My h-table template is something like:
<script type="text/ng-template" id="hTableTemplate.html">
<div>
<div ng-transclude id="limbo" style="display: none"></div>
<table>
<tr>
<th ng-repeat="col in cols">{{col.field}}<th>
</tr>
<tr ng-repeat="row in rows">
<td ng-repeat="col in cols">
// Here I need to put the content of h-column directive
// if it exists, or just bind the value for the column
<span ng-bind-html="getContentOrValueFor(row, col)" />
</td>
</tr>
<table>
</div>
</script>
So I need to create two directives: h-table and h-column. The h-table directive uses a directive controller, that will be used by both directives.
The h-column directive will use this controller to add cols to the table and get value of a row/col.
So far, this is my directive's controller:
.controller("hTable", function ($scope, $element, $attrs, $compile) {
$scope.cols = [];
this.addCol = function (col) {
$scope.cols.push(col);
};
$scope.getContentOrValueFor = function (row, col) {
// MY PROBLEM IS HERE! I will explain below ...
return col.content && col.content.html() ? col.content.html() : row[col.field];
};
})
My h-column directive receives h-table's controller.
It uses transclude to get it content and save this content inside col object, to bind it after:
.directive("hColumn", function () {
return {
restrict: "E",
require: "^hTable",
transclude: true,
scope: {
field: "#",
},
link: function(scope, element, attrs, hTableController, transclude) {
var col = {};
col.field = scope.field;
col.content = transclude(); // <-- Here I save h-column content to bind after
hTableController.addCol(col);
...
}
};
})
And finally :) my h-table directive:
.directive("hTable", function () {
return {
restrict: "E",
scope : {
rows: "="
},
controller: "hTable",
require: "hTable",
replace: true,
transclude: true,
templateUrl: "hTableTemplate.html",
link: function(scope, element, attrs, hTableController) {
...
}
};
})
I need to put h-column's content inside the td tag. So, I call getContentOrValueFor function to get this content that was inside h-column's directive.
If there is no content, so I bind with the value for the field.
It works normally if the h-column's content is something like {{1+2+3}} (it will show me 6, that's ok).
But if this content is an html tag like:
test
I get the error "html.indexOf is not a function"
How can I achieve this??
Thats caused by not including ngSanatize i think. See : https://docs.angularjs.org/api/ng/directive/ngBindHtml

Resources