Can't create a popover with custom template - angularjs

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.

Related

$event.stopPropagation doesn't play nice with Bootstrap data-toggle

I have a list of elements, each containing a ng-click. Nested inside each element is a div that should toggle a Bootstrap modal.
I have added the $event.stopPropagation to the nested div because i don't want the ng-click event from the parent element to fire, but this causes the modal to not begin displayed.
<div id="segment{{segment.Id}}" class="message" ng-class="{activeSegment: segment.Selected}" ng-click="!segmentIsLoaded || selectSegment(segment)">
<div>
<div class="pull-left">
<span>ID: {{segment.Id}}</span>
<span>{{segment.Name}} </span>
<i class="fa fa-info-circle fa-lg" data-toggle="tooltip" data-original-title="{{segment.Description}}" tooltip></i><br />
<small>{{formatJsonDate(segment.Updated)}}</small>
</div>
<div class="btn btn-danger pull-right" data-toggle="modal" data-target="#deleteSegmentModal" ng-click="$event.stopPropagation();">
<span><i class="fa fa-trash-o" aria-hidden="true"></i></span>
</div>
</div>
</div>
Any known work-arounds for this?
I should perhaps mention that the stopPropagation() works as far as preventing the click event from firing. My issue is with the bootstrap modal not being activated through data-toggle attribute
Have you tried using a directive
<a ng-click='expression' stop-event='click'>
.directive('stopEvent', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
if(attr && attr.stopEvent)
element.bind(attr.stopEvent, function (e) {
e.stopPropagation();
});
}
};
});
check SO link How can I make an AngularJS directive to stopPropagation?

uib-timepicker broken when $compile

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,

AngularJS - $emit fire before $on

I am working with AngularJS framework. I wrote a few directives with and without their own controller and they sometimes include each other.
For every course, I use my own directive to print its informations
<li ng-repeat="course in courses" on-courses-loaded="fetch_subscribed">
<course-info course="course"></course-info>
</li>
The 'on-courses-loaded' attribute is looking for the last element, I wrote it to do things when ng-repeat is over : a few data are prepared and signal is emitted for every function register as $rootScope.$on('courses_available', function() {}) (scope < actual scope)
angular.module('app').directive('onCoursesLoaded', [
'$rootScope', function ($rootScope) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true)
{
element.ready(function() {
// Prepare things, when done return promise and then ->
$rootScope.$broadcast('courses_available');
});
}
}
}
}]);
In course info directive, there is a call to another directive
<subscribe code="course.code" triggered="true"></subscribe>
With this code
<div ng-show="available">
<button type="button" class="btn btn-success" ng-hide="subscribed" ng-click="subscribe()">
<span class="glyphicon glyphicon-ok"></span> Subscribe
</button>
<button type="button" class="btn btn-danger" ng-show="subscribed" ng-click="unsubscribe()">
<span class="glyphicon glyphicon-remove"></span> Unsubscribe
</button>
</div>
And THIS link function
if (scope.triggered === true)
{
console.log('wait for emit');
$rootScope.$on('courses_available', function() {
internals.getSubscribed();
});
}
else
{
console.log('do not wait for emit');
scope.$watch('code', function(value) {
internals.getSubscribed();
});
}
The biggest issue is that signal is emitted before subscribe directive has set its $rootScope.$on('courses_available', ..)
Here you can find HTML pseudo code expanded
<li class="list-group-item" ng-repeat="course in courses | filter: search" on-courses-loaded="fetch_subscribed">
<!--<course-info course="course"></course-info> IS NEXT DIV-->
<div>
<!--<subscribe code="course.code" triggered="true"></subscribe> IS NEXT DIV-->
<div ng-show="available">
<button type="button" class="btn btn-success" ng-hide="subscribed" ng-click="subscribe()">
<span class="glyphicon glyphicon-ok"></span> Subscribe
</button>
<button type="button" class="btn btn-danger" ng-show="subscribed" ng-click="unsubscribe()">
<span class="glyphicon glyphicon-remove"></span> Unsubscribe
</button>
</div>
</div>
I need to prepare data and emit signal after ng-repeat
While 1., every content from ng-repeat must subscribe to $rootScope.$on() and before emit.
If you have any tips, thank you all.
You can detect finish event of ng-repeat as below
if (scope.$last){
window.alert("im the last!");
}
I have done something like this , so I will post my code here maybe it can help you. First I created custom directive
'use strict';
angular.module('myApp')
.directive('onFinishRender',['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('tilesCreated');
});
}
}
}
}]);
Then used this directive with ng-repeat
<div on-finish-render="" ng-repeat="tile in tiles" ></div>
Then I listen for this event inside another directive
scope.$on('tilesCreated', function() {
// Do something here
});
P.S. Notice that in here I do all things on the same scope. But it depends on you if you want you can broadcast events on $rootScope. I just wanted to give you base idea of how to do this. I hope it will help.

Compiling Another Directive inside a Directive not working

I'm developing a blog website and currently working on a post creator. The post creator will eventually allow a user to add HTML elements to their post and edit them when needed, before submitting the post.
So far, a user can add a Header or Rich Text to their post. I'm working on rendering these components based on content information retrieved from the backend, as such:
<div ng-repeat="component in components track by $index">
<editable type="component.type" model="component.content"></editable>
</div>
The editable directive looks like the following:
(function() {
'use strict';
angular.module('blog')
.directive('editable', directive);
directive.$inject = ['$compile'];
function directive($compile) {
return {
restrict: 'E',
templateUrl: 'components/editable/editable.html',
scope: {
type: '=',
model: '='
},
link: function(scope, element) {
scope.editing = false;
scope.currentModel = scope.model;
var viewTemplate, editTemplate;
switch(scope.type) {
case 'header':
viewTemplate = '<h2 ng-show="!editing">{{currentModel}}</h2>';
editTemplate = '<input ng-show="editing" type="text" class="form-control" ng-model="model">';
compileTemplate(viewTemplate, editTemplate);
break;
case 'richtext':
viewTemplate = '<div ng-show="!editing">{{currentModel}}</div>';
editTemplate = '<summernote ng-show="editing" ng-model="model" height="300"></summernote>';
compileTemplate(viewTemplate, editTemplate);
break;
default:
break;
}
function compileTemplate(viewTemplate, editTemplate) {
var viewTemplateCompiled, editTemplateCompiled;
viewTemplateCompiled = $compile(angular.element(viewTemplate))(scope);
editTemplateCompiled = $compile(angular.element(editTemplate))(scope);
element.find('view').replaceWith(viewTemplateCompiled);
element.find('edit').replaceWith(editTemplateCompiled);
}
scope.toggleEditMode = function(saveChanges) {
scope.editing = !scope.editing;
if (saveChanges) {
scope.currentModel = scope.model;
}
}
}
}
}
}());
The template looks like the following:
<div class="row">
<div class="col-md-8">
<view></view>
<edit></edit>
</div>
<div class="col-md-4">
<span class="pull-right">
<button ng-show="editing" class="btn btn-success" ng-click="toggleEditMode(true)"><span class="glyphicon glyphicon-ok"></span></button>
<button ng-show="editing" class="btn btn-danger" ng-click="toggleEditMode(false)"><span class="glyphicon glyphicon-remove"></span></button>
<button ng-show="!editing" class="btn btn-warning" ng-click="toggleEditMode(true)"><span class="glyphicon glyphicon-edit"></span></button>
</span>
</div>
</div>
Here's how the website looks like in "view" mode, with the HTML for summernote inspected:
When the "edit" button is clicked (yellow one on the right with the icon), The second line should appear in a summernote editor, but the editor never shows up:
I've noticed that the summernote editor is never inserted after compiling (you'll see that it's not there in the developer tools/element inspector). Perhaps this is the issue? If so, is there a way to fix it?
P.S.: I can create a Plunkr on request.
function compileTemplate(viewTemplate, editTemplate) {
var viewTemplateCompiled = angular.element(viewTemplate),
editTemplateCompiled = angular.element(editTemplate);
element.find('view').replaceWith(viewTemplateCompiled);
element.find('edit').replaceWith(editTemplateCompiled);
$compile(viewTemplateCompiled)(scope);
$compile(editTemplateCompiled)(scope);
}

Why does this only work the first time the dialog is displayed?

Using ui-bootstrap with this attribute attached to the ok/save button on the dialog.
The first time my dialog is created, it focuses on the button just as expected.
Every subsequent time it has no effect.
.directive('autoFocus', function($timeout) {
return {
restrict: 'AC',
link: function(_scope, _element) {
$timeout(function(){
_element[0].focus();
}, 0);
}
};
});
The modal template looks like this (This comes from Michael Conroy's angular-dialog-service):
<div class="modal" ng-enter="" id="errorModal" role="dialog" aria-Labelledby="errorModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header dialog-header-error">
<button type="button" class="close" ng-click="close()">×
</button>
<h4 class="modal-title text-danger"><span class="glyphicon glyphicon-warning-sign"></span> Error</h4>
</div>
<div class="modal-body text-danger" ng-bind-html="msg"></div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" autoFocus ng-click="close()">Close</button>
</div>
</div>
</div>
</div>
The first time the focus moves to the close button no problem. After that the focus stays where it was.
I'm trying to deal with an enter key keypress that is launching this error dialog repeatedly, and I really need the focus to move away from the from underneath the dialog.
Turns out that autofocus is a really bad choice for a directive. I renamed it takefocus and now it works every time without any change. Why does autofocus not work? Beats me. There are override directives for and other tags that are in angular and work, but overriding autofocus with a directive does not.
It happens because the directive autoFocus is compiled once when the element is added to stage and the link function isn't called again, if you have a variable on parent scope responsible for displaying modal like $scope.opened you can use $watcher on said variable, i.e. if ith change from false to true set focus
.directive('autoFocus', function($timeout, $watch) {
return {
restrict: 'AC',
link: function(_scope, _element) {
$watch('_scope.opened', function (newValue) {
if(newValue){
$timeout(function(){
_element[0].focus();
}, 0);
}
}
}
};
});

Resources