ng-click with parameter not working inside dynamic template - javascript

I'm using the AngularUI bootstrap library in my project. In particular, I'm using the accordion and a custom template to display the accordion groups etc. On the template I have an ng-click event which seems to be working only when I don't have the parameter on it. Inside my directive I have a scope variable that produces a unique identifier which I have included as a parameter on the ng-click event. What am I missing to get this working? I'm using Angular 1.0.8.
thanks in advance
!-- template
<a callback="accordion_group_opened($index)" ng-click="callSubmit(currentRow)">
!-- scope variable incremented each time the directive is called
countRow = generateUnique.getNextIdStartingAtOne();
scope.currentRow = countRow;
Edit:
Added the compilation still not getting the value from the scope into my ng-click param. Any ideas on a work around?
compile:function (scope, element, attrs) {
var countRow = generateUnique.getNextIdStartingAtOne();
scope.currentRow = countRow;
return function(scope, element, attr) {
$timeout(function() {
var fn = $parse(attr["ngClick"]);
element[0].on("click", function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
})
};
}

Related

Calling an angular directive via append method

I'm trying to call an angular directive with html text I am appending in my controller like so,
var loadReviews=function(){
var theDiv=$("#rlist")
for(var i=0; i<vm.reviewlistByUpvote.length; i++){
var review=vm.reviewlistByUpvote[i];
var html='<a star-directive ng-model="review.overall" data-size="xs" data-disabled="true"> </a>';
theDiv.append(html)
};
};
And my directive looks as follows,
angular.module('App')
.directive('starDirective', function() {
return {
restrict: 'A',
// templateUrl: 'views/star.html',
link: function(scope, element, attrs, ngModel) {
$(element).rating(scope.$eval(attrs.starRating));
// get value from ng-model
ngModel.$render = function() {
$(element).rating('update', ngModel.$viewValue || '');
}
$(element).on('rating.change', function(event, value, caption) {
ngModel.$setViewValue(value);
});
}
};
});
However, the directive won't compile. If I load the star directive within my html, it works fine, but through this approach, nothing gets loaded. I've looked into $compile but it did not fix the issue, but I may have applied it incorrectly.
I would avoid adding manually the directive into the html, I would recommend let angular being in charge of adding html content.
Now there are cases where you will need to append html content (like in modals), in this case you need to compile the html using $compile service before appending the html and then assing a scope (it can be a new one or the same one)
Here is a cool example from Ben Lesh of how to do that:
http://www.benlesh.com/2013/08/angular-compile-how-it-works-how-to-use.html

Angular.js call jquery function in Directive

In my app i am trying to call $('#accountTable').dataTable(); this function in my controller. But I think it doesnt work like that in angular.js. Tried to call this function in my Directive but i did not work.
My Directive:
'use strict'
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
$('#accountTable').dataTable();
}
}
});
Angular uses JQuery under the hood if you have JQuery referenced. If you don't then it falls back on a slimmer version of JQuery called JQuery Lite. The elem argument to the link function is already a JQuery wrapped object representing the element your directive is attached to. Just call the plugin from there and it should work fine. It is best to avoid the classic JQuery selectors to navigate the DOM and instead lean on Angular to provide the elements you need.
Make sure you have JQuery referenced before Angular in your script references.
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
elem.dataTable();
}
};
});
Angular needs to know about changes when they happen. If you assign any events and need to update scope variables, you'll need to make sure that Angular knows about those changes by wrapping them in scope.$apply. For example:
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
elem.on('order.dt', function (e) {
scope.something = 'someValue';
}).dataTable();
}
};
});
The above code will set the something property on scope, but because the event was triggered outside of an Angular digest cycle, any UI bound to the something variable may not appear to update. Angular needs to be told of the change. You can make sure the change happens during a digest cycle like this:
app.directive('dataTableDirective', function () {
return {
restrict: "A",
link: function (scope, elem, attrs) {
elem.on('order.dt', function (e) {
scope.$apply(function () {
scope.something = 'someValue';
});
}).dataTable();
}
};
});
Then in your markup:
<table data-data-table-directive>
<!-- table contents -->
</table>
#supr pointed this out in the comments. Note that the attribute is data-data-table-directive not data-table-directive. There is an HTML convention that you can begin arbitrary attributes with data- and Angular respects that and omits it. For example, you can put ng-click on an element or you can put data-ng-click on an element and they would both work the same. It also supports x-ng-click as another convention.
This is super relevant to you because it just so happens that your directive name begins with the word "data", so you'll need to double up on the data- in the beginning. Hopefully that makes sense.

AngularJS: Switch model value does not affect on UI

I've created a directive and initiated some models in link: method, then bind a click event on <a> tag of template. Where scope models switches it's values to each-other.
When I see model values in console, seems it's working fine. But does not reflect on UI.
Here's my Fiddle: http://jsfiddle.net/gp32g7sr/7/
Need to $apply on a scope to run digest cycle. Changing scope variable from an events doesn't run angular digest cycle we need to manually run it.
Link Fn
link: function (scope, element, attrs) {
scope.activeContinent = 'Asia';
scope.altContinent = 'America';
element.find('a').on('click', function (e) {
e.preventDefault();
var x = scope.altContinent;
scope.altContinent = scope.activeContinent;
scope.activeContinent = x;
document.querySelector('#log')
.innerHTML = scope.activeContinent + ' ' + scope.altContinent;
//$compile(document.querySelector('#log'))(scope);
scope.$apply();
});
}
Working Fiddle

Directive at angularjs and custom method/html

I have this code:
<body ng-controller="testController">
<div test-directive transform="transform()">
</div>
<script type="text/ng-template" id="testDirective.html">
<div>
<p>
{{transform()}}
</p>
</div>
</script>
<script>
angular.module("Test", [])
.directive("testDirective", function() {
return {
templateUrl: "testDirective.html",
scope: {
transform: "&"
},
link: function(scope) {
}
};
})
.controller("testController", function($scope) {
$scope.transform = function() {
return "<a ng-click='somethingInController()'>Do Something</a>";
};
$scope.somethingInController = function() {
alert("Good!");
};
});
</script>
</body>
So basically what I want to accomplish is to create a directive with a method that will be called from the controller. And that method will do something with the values passed (in this example it does not receives nothing, but in the real code it does).
Up to that point is working. However, the next thing I want to do is create an element that will call a method in the controller. The directive does not knows what kind of element will be (can be anything) nor what method will be. Is there any way to do it?
Fiddle Example:
http://jsfiddle.net/abrahamsustaita/C57Ft/0/ - Version 0
http://jsfiddle.net/abrahamsustaita/C57Ft/1/ - Version 1
FIDDLE EXAMPLE WORKING
http://jsfiddle.net/abrahamsustaita/C57Ft/2/ - Version 2
The version 2 is now working (I'm not sure if this is the way to go, but it works...). However, I cannot execute the method in the parent controller.
Yes. However there is a few problems with your code. I will start by answering your question.
<test-directive transform='mycustommethod'></test-directive>
// transform in the directive scope will point to mycustommethod
angular.module('app').directive('testDirective', function() {
return {
restrict: 'E',
scope: {
transform: '&'
}
}
});
The problem is that printing the html will be escaped and you will get < instead of < (etc.). You can use ng-bind-html instead but the returned html will not be bound. You will need to inject the html manually (you can use jquery for this) in your link method and use var compiled = $compile(html)(scope) to bind the result. Then call ele.after(compiled) or ele.replace(compiled) to add it to your page.
I finally get to get it working.
The solution is combined. First of all, I needed to add another directive to parse the element I wanted:
.directive("tableAppendElement", function ($compile) {
return {
restrict: "E",
replace: true,
link: function(scope, element, attrs) {
var el = angular.element("<span />");
el.append(attrs.element);
$compile(el)(scope);
element.append(el);
}
}
})
This will receive the element/text that will be appended and then will registered it to the scope.
However, the problem still exists. How to access the scope of the controller? Since my directive will be used by a lot of controllers, and will depend on the model of the controller, then I just set scope: false. And with that, every method in the controller is now accessible from the directive :D
See the fiddle working here. This also helped me because now, there is no need to pass the transform method, so the controller can be the one handling that as well.

Why does ngModel.$setViewValue have no effect?

I have a directive like below. It's supposed to load a file from an <input type=file> and set it to an ng-model provided. It's also got some custom validation only incidental to my question. It's also on plunkr.
What's wrong is that the ngModel never gets set at all. It's always undefined. Why?
app.directive('fileInput', function () {
var link = function (scope, element, attrs, ngModel) {
var VALIDTYPES = ['text/csv', 'text/directory', 'text/vcard'];
var updateModel = function () {
var file = element[0].files[0];
if (file) {
scope.$apply(function () {
if (VALIDTYPES.indexOf(file.type) >= 0) {
ngModel.$setValidity("mimetype", true);
ngModel.$setViewValue(file);
} else {
ngModel.$setValidity("mimetype", false);
alert("Sorry, can only accept VCF and CSV files.");
}
})
}
};
element.bind('change', updateModel);
};
return {
restrict: 'A',
require: 'ngModel',
template: "<input type='file'>",
replace: true,
link: link,
scope: {},
}
});
This is fixed as of 1.2.0 due to this change:
Fixes issue with isolate scope leaking all over the place into other directives on the same element.
Isolate scope is now available only to the isolate directive that requested it and its template.
Before 1.2.0 if any directive on an element requested an isolate scope then all the directives on that element shared that scope.
In your case this caused the input directive to use the isolate scope you were requesting for your directive instead of the parent scope that the html <p>Filename is: {{ file.name }}</p> is on. Thus file was undefined because file was on a child scope
You can see that in action, pre 1.2.0, by copying the $viewValue up to the parent scope using this one line below your $setViewValue:
ngModel.$setViewValue(file);
scope.$parent.file = ngModel.$viewValue;
You'll see in this updated plunker that fixes this problem in pre-1.2.0 code.
The best solution though is to move to 1.2.0. With 1.2.0 your isolate scope will only affect your directive and not the 'input' directive so everything works as expected in this plunker

Categories

Resources