Angularjs directive input binding with bootstrap popover - angularjs

So I am trying to make a directive that is encapsulated inside a Bootstrap popover. Almost everything is working - I have event listeners to know when buttons get clicked, links, everything is hunkey-dory - except for the fact that even though I am using ng-model to bind to the input in the innermost directive, nothing is getting updated. It's almost as if it's doing a one-way binding instead of a two-way. FieldValue on the model is always null (its initial state), and is never altered (even though it should be equal to whatever was put in the input field). I have looked through a ton of answers and nothing seems to address the issue.
Here is my code:
Page HTML
<table class="table table-bordered table-striped">
<tbody>
<tr ng-repeat="field in model.CustomFields">
<td>{{field.Title}}</td>
<td>
<a popover field-description="field.CustomField.Description" model-value="field.FieldValue" class="trigger">
<text-custom-field text-model="field"></text-custom-field>{{field.FieldValue || 'Empty'}}
</a>
</td>
</tr>
</tbody>
</table>
Popover Directive
(function() {
'use strict';
angular
.module('app.directives')
.directive('popover', popover);
popover.$inject = ['$compile'];
function popover($compile) {
var directive = {
link: link,
restrict: 'A',
scope: {
fieldDescription: '=',
modelValue: '='
}
};
return directive;
function link(scope, element, attrs) {
$(element).popover({
html: true,
placement: 'top',
title: scope.fieldDescription,
content: function () {
return $(this).parent().find('.js-popover-content').html() + '<button ng-click="submit()" type="submit" class="btn btn-primary btn-sm" style="margin-right: 5px;"><i class="glyphicon glyphicon-ok"></i></button><button type="button" class="btn btn-default btn-sm edit-cancel" ng-click="closePop()" style="margin-right: 5px;"><i class="glyphicon glyphicon-remove"></i></button>';
}
});
scope.closePop = function () {
console.log('close');
$('.trigger').popover('hide');
};
scope.submit = function () {
console.log('submit');
scope.$parent.$broadcast('submit');
};
scope.$parent.submitData = function (value) {
scope.modelValue = value;
console.log('submitted data');
scope.closePop();
};
$(element).on('click', function (e) {
$('.trigger').not(this).popover('hide');
$compile($('.popover.in').contents())(scope);
});
}
}
})();
Inner Directive
(function() {
'use strict';
angular
.module('app.directives')
.directive('textCustomField', textCustomField);
function textCustomField() {
var directive = {
templateUrl: 'url/templates/TextCustomField.html',
restrict: 'E',
transclude: true,
link: link,
scope: {
textModel: '='
}
};
return directive;
function link(scope, element, attrs) {
scope.$parent.$on('submit', function (event) {
console.log('heard submit event');
var value = scope.textModel.FieldValue;
console.log(value);
scope.$parent.submitData(value);
});
}
}
})();
Directive HTML
<div class="js-popover-content hide">
<div class="form-group" style="display: inline-flex;">
<input type="text" class="form-control" ng-model="textModel.FieldValue" style="width: {{textModel.CustomField.DisplaySize}}; margin-right: 5px;">
</div>
</div>
Does anyone have any ideas why this isn't working?

Related

how to pass the values of set of input boxes of key value pair to the parent scope in angular js

I have created a directive which contains a map of key value pairs. In edit mode i used to populate the values of entries using ng-repeat, Also a user can add more key value pairs when he press a add button
Below is the code
angular.module('key_value_component',
[])
.directive('keyValuePairs', function ($compile, $templateCache) {
return {
templateUrl: 'key_value.tpl.html',
restrict: 'E',
replace: true,
scope: {
entries: "=ngModel"
},
controller: function ($scope) {
$scope.create_key_value = function () {
$scope.$broadcast('ready-to-render');
}
}
};
})
.directive("keyValueInit", function ($compile, $templateCache) {
return {
templateUrl: 'key_value_init.tpl.html',
restrict: 'E',
replace: true,
link: function (scope, element, attributes) {
scope.$on('ready-to-render', function (event) {
list_select = $compile($templateCache.get("key_value_input.tpl.html"))(scope);
element.parents('.key_value_element').append(list_select);
})
}
}
}).directive("keyValue", function ($compile, $templateCache) {
return {
templateUrl: 'key_value_input.tpl.html',
restrict: 'E',
replace: true,
}});
templates :
key_value_tpl.html
<div class="container-fluid">
<div class="key_value_header">
<button class="btn btn-success btn-add pull-right" type="button" ng-click="create_key_value()">
<span class="glyphicon glyphicon-plus"></span>
</button>
<div class="col-sm-4"><strong>key</strong></div>
<div class="col-sm-4"><strong>value</strong></div>
</div>
<div class="key_value_body">
<key-value ng-repeat="(key,value) in entries"></key-value>
<div class="key_value_element">
<key-value-init></key-value-init>
</div>
</div>
key_value_input.tpl.html
<div class="form-group key_value">
<div class="col-sm-4">
<input class="form-control key_value_key" type="text" ng-value="key" ng-readonly="key"/>
</div>
<div class="col-sm-4">
<input class="form-control key_value_value" type="text" ng-value="value" ng-readonly="value"/>
</div>
<input-remove-button></input-remove-button>
key_value_init.tpl.html
<div></div>
If someone add a new key value pair I want to append its value in entries. A submit event is happening outside the directive in the form. the directive is part of that form.

Variable value not passing in a controller using directive with ng-class

I am referencing the value of the variable in a controller in an ng-class template but its not working.
here is the html directive template URl :
<div class="tooltip-anchor">
<div class=" tooltip-content ehub-body" ng-class="{ 'tooltip__content--disabled': tooltipContentValue}" ng-transclude>Tooltip content</div>
</div>
Here is where i am using the directive in the index page
<div style="text-align:center;">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me
</div>
And here is the directive:
in this directive i am creating a variable and setting it to false and also trying to use it in an ng-class attribute
(function (window) {
'use strict';
angular
.module('ehub.component.tooltip', [])
.controller('ehubTooltipCtrl', ['$scope', function ($scope) {
$scope.tooltipContentValue = false;
}])
.directive('ehubTooltip', ehubTooltip);
function ehubTooltip() {
var directive = {
controller: "ehubTooltipCtrl",
link: link,
transclude: true,
templateUrl: 'ehub-tooltip.html',
restrict: 'E'
};
return directive;
function link(scope, element, attrs) {
scope.keyupevt = function () {
if (event.keyCode === 27) {
$scope.tooltipContentValue = true;
}
}
}
}
})();
Try this working jsfiddle.
angular.module('ExampleApp', ['ngMessages'])
.controller('ExampleController', function($scope) {
})
.directive('ehubTooltip', function() {
var directive = {
link: link,
transclude: true,
template: '<div class="tooltip-anchor"><div class=" tooltip-content ehub-body" ng-class="{ \'tooltip__content--disabled\': tooltipContentValue}" ng-transclude>Tooltip content</div></div>',
restrict: 'E'
};
function link(scope, element, attrs) {
scope.tooltipContentValue = false;
scope.keyupevt = function() {
if (event.keyCode === 27) {
scope.tooltipContentValue = true;
}
}
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<div style="text-align:center;">
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here</a>
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me</a>
</div>
</div>
</div>

AngularJS custom directive - map isolate scope to new child scope

I created a custom directive for bootstrap alerts. My alerts display fine (hard code and data bind). Based on the alert type, I want display a unique header into my alert message based on the returned scope values (success, info, warning, danger). Currently I'm passing the type into <h1> but I don't want those values, they need to be custom.
<!-- data binding example -->
<trux-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</trux-alert>
<!-- hard coded example -->
<trux-alert close="close" type="warning">This is an important warning message!</trux-alert>
Inside my directive, the scope is isolated using scope: '#' (one-way)
.directive('truxAlert', function() {
return {
scope: {
type: '#',
close: '&'
},
replace: true,
transclude: true,
template:
'<div class="alert" '+
'ng-class="[\'alert-\' + (type || \'warning\'), closeable ? \'alert-dismissible\' : null]" '+
'role="alert">'+
'<button ng-show="closeable" '+
'type="button" class="close" '+
'ng-click="close({$event: $event})" '+
'data-dismiss="alert" aria-label="Close">'+
'<span aria-hidden="true">×</span>'+
'<span class="sr-only">Close</span>'+
'</button>'+
'<h1>{{type}}</h1>'+
'<div ng-transclude></div>'+
'</div>',
link: function (scope, element, attrs) {}
}
});
This would be easier if all my values were pulled through data binding, but I need to allow for manual hard coded option. I know with one-way isolated scopes '#' I can't change these values though DOM manipulation. I can't use '=' or '&' for two-way because the values are strings.
How do I solve for this problem?
My recommendation is have one attribute for the control the open/closed state of the directive's alert and another attribute for the dismiss handler function.
angular.module("myApp").directive('truxAlert', function() {
return {
scope: {
type: '#',
dismissHandler: '&',
title: '#',
open: '='
},
replace: true,
transclude: true,
template:
'<div class="alert" ng-show="open" '+
'ng-class="[\'alert-\' + type]" '+
'role="type">'+
'<button type="button" class="close" '+
'ng-click="truxClose($event)" '+
'data-dismiss="alert" aria-label="Close">'+
'<span aria-hidden="true">×</span>'+
'<span class="sr-only">Close</span>'+
'</button>'+
'<h1>{{title+" "+type}}</h1>'+
'<div ng-transclude></div>'+
'</div>',
link: function (scope, element, attrs) {
console.log("truxAlert linking");
if (!scope.type) { scope.type="warning" }
scope.truxClose = function(event) {
console.log("truxClose "+event);
if (attrs.dismissHandler) {
scope.dismissHandler({$event: event});
return;
}
scope.open = false;
};
}
};
});
The linking function of the directive determines if the dismiss-handler attribute exists and either invokes the dismiss handler or directly closes the alert.
The DEMO PLNKR show the directive being used both with an ng-repeat directive and in a standalone manner.
Maybe, i don't understand you question.
You want to do so jsfiddle?
<form name="ExampleForm" id="ExampleForm">
<span simple="{{val}}">{{val}} - value from data-binding </span>
<br>
<span simple="valueTwo">valueTwo - hard code value</span>
</form>
And js controller
.controller('ExampleController', function($scope, $rootScope, $alert) {
$scope.val = "valueOne";})
And js directive
.directive('simple', function() {
return {
restrinct: 'A',
scope: {
simple: "#"
},
link: function(scope) {
console.log(scope.simple, typeof(scope.simple));
}
}})
UPDATED
angular.module('ExampleApp', ['use', 'ngMessages'])
.controller('ExampleOneController', function($scope) {
$scope.val = "valueOne";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.controller('ExampleTwoController', function($scope) {
$scope.val = "valueTwo";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.controller('ExampleThreeController', function($scope) {
$scope.val = "valueThree";
$scope.$on('pass.from.directive', function(event, value) {
$scope.valFromDirective = value;
});
})
.directive('simple', function($interval) {
return {
restrinct: 'A',
scope: {
simple: "#"
},
link: function(scope) {
var i = 0;
$interval(function() {
i++;
scope.$emit('pass.from.directive', scope.simple + i);
}, 1000);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-messages.min.js"></script>
<script src="https://cdn.rawgit.com/Stepan-Kasyanenko/use-form-error/master/src/use-form-error.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleOneController">
<h3>
ExampleOneController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
<div ng-controller="ExampleTwoController">
<h3>
ExampleTwoController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
<div ng-controller="ExampleThreeController">
<h3>
ExampleThreeController
</h3>
<form name="ExampleForm" id="ExampleForm">
<div simple="{{val}}">{{val}} - value from scope </div>
<div>{{valFromDirective}} - value from directive </div>
</form>
</div>
</div>

Reusable modal window directive. How can I pass directive-attributes-values to controller

I want to make a reusable modal-window-directive (i found a pair modal-window examples, but that's not what i want, or they do not work properly).
It should look something like this:
(1) title-, body-text and function-name (for the function, which is binded to the action-button in the template) are defined directly within directive
<my-modal
modal-title="Modal Window Title"
modal-body="This is my Modal Window Body"
click-main-html-action-button="myControllerActionFunction()">
</my-modal>
(2) The HTML-template for modal window must pick up the corresponding values
<div class="panel-default">
<div class="panel-heading">
{{title}}
</div>
<div class="panel-body">
<div class="row">
<div class="form-group col-sm-12">**{{body}}**</div>
</div>
<div class="col-sm-12 text-center">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-ng-click="templatestartaction(); $event.stopPropagation()">Start action</button>
</div>
</div>
</div>
(3) Button-function for modal window must be defined In the controller
app.controller('controller', function($scope) {
scope.title = "";
scope.body = "";
$scope.myControllerActionFunction = function () {
console.log('myControllerActionFunction', title);
};
})
The question is: how can I read the attrs values from (1)
(modal-title = "Modal Window Title", etc.) and the function name
(click-main-html-button action = "myControllerActionFunction ()") and
pass it to the model and function definition in the controller
(3), so that it is displayed in the html-template (2)?
I can read the attributes from (1), but I do not know how i pass it in controller.
(4)
app.directive('myModal', function () {
return {
transclude: true,
restrict: 'E',
//template: '<div ng-transclude></div>',
templateUrl: "directives/modal/view/modalWindowTemplate.html"
scope: {
templatestartaction: '&clickMainHtmlActionButton'
},
link: function (scope, element, attrs) {
scope.title = attrs.modalTitle;
scope.body = attrs.modalBody;
}
};
});
You need to define title and body in the controller like
$scope.modalProps = {
title: 'My modal title',
body: 'My modal body'
};
Use this in the html
<my-modal
modal-title="modalProps.title"
modal-body="modalProps.body"
click-main-html-action-button="myControllerActionFunction()">
</my-modal>
And define it for the directive
app.directive('myModal', function () {
return {
transclude: true,
restrict: 'E',
//template: '<div ng-transclude></div>',
templateUrl: "directives/modal/view/modalWindowTemplate.html"
scope: {
templatestartaction: '&clickMainHtmlActionButton'
title: '=modalTitle',
body: '=modalBody'
},
...

Angular placing functions

I'm using the angular-google-maps library in my project. I have used a directive to load a custom google maps menu. The goal is to obviously reuse the directive. In the menu are a couple of buttons which when clicked should all carry out a function. I'm still trying to get my head around on how to do that, so here is my problem:
I would like to pan the map to its original position when the button "Home" is clicked. Normally that is just done with ng-click and the function is placed within the scope of the controller. With the directive I'm confused. Where should I place the "home()" function? Directive? Directive controller? Controller? I hope this makes any sense?!?!
HTML:
<div class="map_canvas">
<google-map center="map.center" zoom="map.zoom" draggable="true">
<marker ng-repeat="m in map.markers" coords="m" icon="m.icon" click="onMarkerClicked(m)">
<marker-label content="m.name" anchor="50 0" class="marker-labels"/>
<window ng-cloak coords="map.center" isIconVisibleOnClick="false" options="map.infowindows.options">
<p>This is an info window at {{ m.latitude | number:4 }}, {{ m.longitude | number:4 }}!</p>
<p class="muted">My marker will stay open when the window is popped up!</p>
</window>
</marker>
<map-custom-control position="google.maps.ControlPosition.TOP_CENTER" control-template="../templates/gmaps/main_menu.html" control-click=""></map-custom-control>
</google-map>
</div>
Template:
<div class="gmaps-menu">
<div class="gmaps-row">
<button type="button" class="btn btn-default"><img class="glyphicon-custom" src="../img/icons/glyphicons/glyphicons_020_home.png" ng-click="home()"></button>
<button type="button" class="btn btn-default"><img class="glyphicon-custom" src="../img/icons/glyphicons/glyphicons_349_fullscreen.png"></button>
<button type="button" class="btn btn-default"><img class="glyphicon-custom" src="../img/icons/glyphicons/glyphicons_096_vector_path_polygon.png"></button>
<button type="button" class="btn btn-default"><img class="glyphicon-custom" src="../img/icons/glyphicons/glyphicons_030_pencil.png"></button>
</div>
</div>
Directive:
AppDirectives.directive('mapCustomControl', ['$log', '$timeout', '$http', '$templateCache', 'google', 'GMapsLib' ,function ($log, $timeout, $http, $templateCache, google,GMapsLib) {
return {
restrict: 'E',
replace: true,
require: '^googleMap',
link: function(scope,element,attr,mapCtrl){
if (!angular.isDefined(attr.controlTemplate)) {
$log.error('map-custom-control: could not find a valid control-template property!');
return;
}
var templateUrl = attr.controlTemplate;
var position = google.maps.ControlPosition.TOP_CENTER;
if (angular.isDefined(attr.position)) {
var EVAL_IS_OK_WE_CONTROL_THE_INPUT = eval;
position = EVAL_IS_OK_WE_CONTROL_THE_INPUT(attr.position);
}
$timeout(function() {
var map = mapCtrl.getMap();
var controlDiv = document.createElement('div');
controlDiv.style.padding = '5px';
controlDiv.style.width = 'auto';
controlDiv.marginLeft = 'auto';
controlDiv.marginRight = 'auto';
$http.get(templateUrl, {cache: $templateCache})
.success(function(html) {
controlDiv.innerHTML = html;
})
.then(function (/*response*/) {
map.controls[position].push(controlDiv);
if (angular.isDefined(attr.controlClick)) {
google.maps.event.addDomListener(controlDiv, 'click', function() {
scope.$apply(attr.controlClick);
});
}
}
);
});
}
};
}]);
You can pass the scope function that has to be executed on the controller:
HTML
<div ng-app="app" ng-controller="sampleCtrl">
<maps-custom-control click-handler="alertMe()"></maps-custom-control>
</div>
JS
var app = angular.module('app', []);
app.directive('mapsCustomControl', function() {
return {
restrict: 'EA',
replace: true,
scope: {
clickHandler: '&'
},
template: '<div style="width: 100px; height:100px; background-color: red;" ng-click="clickHandler()"></div>'
};
});
app.controller('sampleCtrl', function ($scope) {
$scope.alertMe = function () {
window.alert('Refresh gMaps control');
};
});
Since we pass the alertMe function, this is the function that will get executed, I hope this makes sense?
Fiddle
A small remark on your code, it would be better if you get the template as follows:
app.directive('..', function() {
return {
template: '<div ng-include="getTemplate()"></div>',
link: function(scope, element, attr) {
scope.getTemplate = function() {
return this.attr.controlTemplate;
}
}
};
});
This way you don't need to do any strange ajax calls. Just add all the mark-up in your template and include it. don't make it necessary hard :-)

Resources