uib-timepicker broken when $compile - angularjs

I want to use a 'uib-timepicker' inside a popover, so as I read, I need to $compile the html to bind it to angular scope:
app.directive('bsPopover', function($compile) {
return function(scope, element, attrs) {
element.find('#formula-nro').popover({
html : true,
title : '<span>Title</span>',
content : function() {
return $compile($('#formula_form_nro').html())(scope);
// return $('#formula_form_nro').html();
}
});
};
});
If I compile the code, the component brokes, but when I don't, it's shows and behaves correctly, but it is not binded to angular scope.
This is the HTML code of the component:
<div id="formula_form_nro" class="padding-block hide">
.....
<uib-timepicker ng-model="time" minute-step="15" readonly-input="true"> </uib-timepicker>
.....
</div>
The following is the popover button:
<button id="formula-nro" type="button" class="btn btn-default" data-container="body" data-toggle="popover" data-placement="top">f(x)</button>
The button is inside a ng-repeat:
<div ng-repeat="item in items" bs-popover>
It looks like a bug, but I don't know if am I doing well. Maybe there is a workaround for this ..
thanks,

Related

Angular watch directive only loads after refresh page

I am trying to make a read more directive.
Currently my directive on works once I refresh the page. I feel like I'm missing a concept here, do I need to initialize the directive into the controller? Is my watch wrong? Currently there is nothing in the controller related to this problem.
HTML:
<div data-ng-switch-when="false">
<div data-ng-bind-html="::sanitizeWidgetContent(widget.content)"
data-expand-height
data-ng-class="{true: 'widget-text-collapsed'}[elementHeight>500]"
class="widget-content">
</div>
<div class="fadeout-bottom" data-ng-show="elementHeight>500">
<button class="btn btn-default btn-fadeout" data-ng-click="elementHeight=false">Read More</button>
</div>
</div>
JS:
angular.module('myApp.directives')
.directive('expandHeight', function () {
return function (scope, el, attrs) {
scope.$watch(attrs.expandHeight, function(){
scope.elementHeight = el[0].offsetHeight;
});
}
});

Can't create a popover with custom template

First I tried using angular-ui
<span popover-template="removePopover.html" popover-title="Remove?" class="glyphicon glyphicon-remove cursor-select"></span>
here the template is not included and no errors are provided in console. As I undestood from previous questions this capability is still in development (using v0.13.0).
Then I tried using bootstrap's popover
<span delete-popover row-index={{$index}} data-placement="left" class="glyphicon glyphicon-remove cursor-select"></span>
This is included to popover
<div id="removePopover" style="display: none">
<button id="remove" type="button" ng-click="removeElement()" class="btn btn-danger">Remove</button>
<button type="button" ng-click="cancelElement()" class="btn btn-warning">Cancel</button>
</div>
This is the managing directive
app.directive('deletePopover', function(){
return{
link: function(scope, element, attrs) {
$(element).popover({
html : true,
container : element,
content: function() {
return $('#removePopover').html();
}
});
scope.removeElement = function(){
console.log("remove"); //does not get here
}
scope.cancelElement = function(){
console.log("cancel"); //does not get here
}
}
};
});
In case of bootstrap's popover the scope is messed up. cancelElement() call does not arrive in directive neither the parent controller.
If anyone could help me get atleast on of these working it would be great.

How to pass the value to the local scope of the directive?

I'm learning directives, it's cool thing but sometimes a little complicated. Please can somebody explain this:
I have custom directive with template of little form and it own local scope, and want to change the list of items form the main controller.
Please see it:
By clicking on change button I open a custom directive with input form template
<body ng-controller="testCtrl">
<h1>Hello Plunker!</h1>
<ul>
<li ng-repeat="item in list">
<div> {{item}} </div>
<button ng-click="edit()">Change</button>
<change ng-if='editable'></change>
</li>
</ul>
</body>
"Change" is the custom directive with the input form inside the other Html file
.directive('change', function(){
return {
restrict: "E",
replace: true,
scope: {
show: '='
},
templateUrl: "other.html"
}
})
Also there is another directive inside "change" directive. It's a button which I want to use inside "change" directive and inside my main controller. I can see my item list only from scope.$parent.item, but how to pass it in the function of my button directive?
How can I implement this?
.directive('save', function(){
return {
restrict: "E",
replace: true,
template: ' <button class="btn btn-sm btn-warning" ng-click="saving(item)">SAVE</button>',
link: function(scope,element,attr){
scope.saving = function(item){
console.log(item);
console.log(scope.$parent.item)
}
}
}
})
Please see the example: Plnkr
P.S. Sorry for my explanation, I hope that everything is clear
Simply pass in the item to each of your directives that need access to it. For example:
<li ng-repeat="item in list">
//snip
<save item="item"></save>
//snip
</li>
And then define your directive to bind the attribute to the scope:
.directive('save', function(){
return {
//snip
scope: {
item: '=' //two-way binding to 'scope.item'
},
//snip
link: function(scope, element, attr){
scope.saving = function() {
console.log(scope.item);
}
};
});
In angularjs, you have the $emit event.
Dispatches an event name upwards through the scope hierarchy notifying the registered $rootScope.Scope listeners.
$rootScope.Scope
HTML
<body ng-controller="testCtrl">
<h1>Hello Plunker!</h1>
<ul>
<li ng-repeat="item in list">
<div> {{item}} - <input type="text" ng-model="item">
<button ng-click="edit()">Change</button>
</div>
<div>
<change ng-if='editable'></change>
</div>
</li>
</ul>
</body>
Directive
directive('save', function(){
return {
restrict: "E",
replace: true,
template: ' <button class="btn btn-sm btn-warning" ng-click="saving(item, $parent.$index)">SAVE</button>',
link: function(scope,element,attr, controller){
scope.saving = function(item, index){
//Build our object with the index of $scope.list which is updated & the item value
var obj = {
index: index,
item: item
};
//Emit a 'change' event, and we pass our object data
scope.$emit('change', obj)
}
}
}
})
In the "change" directive, we use $emit to pass event, and to notify our $rootScope.Scope.
In the "change" directive template, you can see that we pass the $parent.$index and not the $index, in order to get the current item of the list.
Controller
controller('testCtrl', function($scope){
$scope.list = [1,2,3,4,5,6,7,8,9];
//Listen for 'change' event
$scope.$on('change', function(event,value){
//Set to the list value.index our value.item
$scope.list[value.index] = value.item;
});
$scope.editable = false;
$scope.edit = function(){
$scope.editable = !$scope.editable;
}
})

How to get the form data when the form is in a directive in Angular?

I have this this template:
<div class="modal" id="popupModal" tabindex="-1" role="dialog" aria-labelledby="createBuildingLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="createBuildingLabel">{{ title }}</h4>
</div>
<form data-ng-submit="submit()">
<div class="modal-body" data-ng-transclude>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-ng-click="visible = false">Annuleren</button>
<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Maken</button>
</div>
</form>
</div>
</div>
</div>
and here's the directive:
app.directive("modalPopup", [function () {
return {
restrict: 'E',
templateUrl: 'Utils/ModalPopup',
scope: {
title: '#',
onSubmit: '&',
visible: '='
},
transclude: true,
link: function (scope, element, attributes) {
var container = $("#popupModal");
scope.submit = function (newGroup) {
scope.onSubmit(newGroup);
}
scope.hide = function () {
container.modal('hide');
}
scope.show = function () {
container.modal('show');
}
scope.$watch('visible', function (newVal, oldVal) {
if (newVal === true) {
scope.show();
}
else {
scope.hide();
}
})
}
}
}]);
As you can see I have declared my form tag inside the directive and I also use transclude to determine how my form is going to look like. For now I have this:
<modal-popup title="Nieuwe groep aanmaken" data-on-submit="createGroup()" visible="showAddGroupForm">
<div class="row">
<div class="col-md-3">Kopieren van:</div>
<div class="col-md-8">
<select class="form-control" data-ng-model="newGroup.Year">
<option value="">Nieuw jaar</option>
<option data-ng-repeat="year in years" value="{{year.Id}}">{{year.Name}}</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-3">Naam</div>
<div class="col-md-8">
<input type="text" class="form-control" data-ng-model="newGroup.Name" />
</div>
</div>
</modal-popup>
When the submit button is pressed, I want the data to be available in my controller.
I ques the data isn't available because of the isolated scope, however I'm not sure. What do I need to do to get the data back from the directive into my controller?
PS: I know about angular-ui and angularstrap, but I'm doing this to learn about Angular.
EDIT:
Here's a Fiddle
I think the cause is a misunderstanding about how scopes work (especially with transclusion).
Let's start with this code (from the fiddle):
<div ng-controller="MyCtrl">
<my-popup on-submit="formSubmitted(model)">
<input type="text" ng-model="model.Name"/>
</my-popup>
</div>
Since <my-popup> transcludes its content, the scope above is that of MyCtrl, even in the content of the directive. By content I mean the <input>, NOT the directive template, i.e. the <div><form ... code.
Therefore it is implied that model (as used in ng-model="model.Name") is a property of the scope of MyCtrl, as is formSubmitted(). Since both are members of the same scope, you do not need to pass the model as argument; you could just do:
(in the template:)
<my-popup on-submit="formSubmitted()"><!-- no `model` argument -->
(the controller:)
function MyCtrl($scope) {
// I like declaring $scope members explicitly,
// though it can work without it (charlietfl comments)
$scope.model = {};
$scope.submittedValue = null;
$scope.formSubmitted = function() {
// another bug was here; `model` is always a member of the `$scope`
// object, not a local var
$scope.submittedValue = $scope.model.Name;
}
}
Another bug is in the directive code:
link: function(scope, element, attributes){
scope.submit = function(){
scope.onSubmit({model: model});
}
}
The variable model (not the name model:) is undefined! It is a property of the parent scope, so you would have a chance if the scope was not isolated. With the isolated scope of the directive, it may work with an awful workaround that I am not even considering to write :)
Luckily, you do not need the directive to know about things happening in the external scope. The directive has one function, to display the form and the submit button and invoke a callback when the submit button is clicked. So the following is not only enough for this example, but also conceptually correct (the directive does not care what is happenning outside it):
link: function(scope, element, attributes){
scope.submit = function(){
scope.onSubmit();
}
}
See the fiddle: http://jsfiddle.net/PRnYg/
By the way: You are using Angular v1.0.1. This is WAAAAY too old, seriously consider upgrading!!! If you do upgrade, add the closing </div> to the template: <div ng-transclude></div>.

How to access a dom element in a Angular directive

I am trying to attach a keyup event to a directive in my Angular project. Here is the directive:
angular.module('clinicalApp').directive('chatContainer', function() {
return {
scope: {
encounter: '=',
count: '='
}
templateUrl: 'views/chat.container.html',
link: function(scope, elem, attrs) {
scope.count = 500;
}
};
});
And here is the html from the template:
<div class="span4 chat-container">
<div class="chat-body">
<form accept-charset="UTF-8" action="#" method="POST">
<div class="text-area-container">
<textarea id="chatBox" class="chat-box" rows="2"></textarea>
</div>
<div class="button-container btn-group btn-group-chat">
<input id="comment" class="btn btn-primary btn-small btn-comment disabled" value="Comment" ng-click="addMessage()"/>
</div>
</form>
</div>
</div>
</div>
I want to access the chatbox in my link function and attach the keyup event to it. I know I can get it with jQuery, but that cannot be the Angular way. What is the proper way to grab that element from the dom?
You can easily do it with Angular' element' find() method:
var chatbox = elem.find("textarea"); // Finding
chatbox.bind("keyup",function(){ // Binding
console.log("KEYUP!")
})
Live example: http://jsfiddle.net/cherniv/S7XdK/
You can use element.find(yourSelector) as previously mentioned, but it is better to use ngKeyUp, similar to how you would use ngClick:
https://docs.angularjs.org/api/ng/directive/ngKeyup

Resources