AngularJS Access Global Variable Under Directive - angularjs

I have a global variable like following.
var Lightbox = {};
............
Lightbox.openModal = function (newImages, newIndex, modalParams) {
...............
}
I would like to access this variable under directives like the following way.
app.directive('productBuyers', ['Product', function(Product) {
return {
restrict : 'E',
scope : {},
template : '<div>' +
'<p class="product-buyers-f bold" ng-show="photos.length">Others:</p>' +
'<
'<div class="product-buyer square" ng-click="openLightboxModal($index)" ng-repeat="photo in photos | limitTo:3" ng-style="{\'background-image\':\'url(\' + photo.image + \')\'}"></div>' +
'<div class="clear"></div>' +
'</div>' +
'</div>',
link : function($scope, element, attrs) {
$scope.photos = [];
function getImages() {
}
$scope.openLightboxModal = function (index) {
Lightbox.openModal($scope.photos, index);
};
getImages();
}
}
}]);
I have tried by passing "$window" to the directive parameter and also using scope but it's not working. It's showing undefined "Lightbox".

Try window.Lightbox and also window.Lightbox.openModal = function(...)

Related

AngularJs : Custom Directive Scope is Undefined

I want to put the value from the other controller to my custom directive , but the value in the watch function in the directive is coming undefined .Here is my code :
directive code :
function associates($templateCache,$compile) {
var directive = {};
directive.restrict = 'E';
directive.controller = 'associatesTypeaheadController';
directive.controllerAs = 'ctrlTA';
directive.template = $templateCache.get('typeahead.template.html');
directive.scope= {
"options": '='
};
directive.link=link;
return directive;
}
function link(scope, element, attrs) {
scope.$watch('options.reload', function(data){
console.log(data); //Still undefined
});
}
Controller :
employeeDetails.opts = {
"reload": false,
"refresh": false
};
employeeDetails.reload = function () {
employeeDetails.opts.reload = true;
console.log('there');
return employeeDetails.opts.reload;
};
employeeDetails.reload();
After lot's of struggle, i found the solution :
Directive To share between two controller:
directive.restrict = 'E';
directive.controller = 'associatesTypeaheadController';
directive.controllerAs = 'ctrlTA';
directive.template = $templateCache.get('typeahead.template.html');
directive.compile = function (element, attributes) {
var linkfunction = function ($scope) {
if ($scope.employeeDetails && $scope.employeeDetails.hint) {
$scope.hint = $scope.employeeDetails.hint;
}
if ($scope.ctrl && $scope.ctrl.hint) {
$scope.hint = $scope.ctrl.hint;
}
};
return linkfunction;
};
return directive;
}
Controller A:
employeeDetails.scope = $scope;
employeeDetails.hint = "Reports To";
Controller A HTML Scope :
<associates></associates>
Directive HTML :
<md-autocomplete
flex id="associate-typehead"
md-input-name='autocompleteField'
md-input-minlength='2' md-input-maxlength='50'
md-no-cache='ctrlTA.noCache'
md-selected-item='ctrlTA.selectedItem'
md-search-text='ctrlTA.searchText'
ng-model-options='{debounce : 1000}'
md-floating-label={{hint}}
md-items='item in ctrlTA.querySearch(ctrlTA.searchText)'
md-item-text='item.username' >
<md-item-template>
<span md-highlight-text='ctrlTA.searchText'>{{item.lastName + ', ' + item.firstName + ' (' + item.username + ')'}}</span>
</md-item-template>
</md-autocomplete>

isolate scope communication from directive to directive?

I am new to AngularJS and got confused with directive concept.
I am updating scope.markers in my second directive via $scope.delete function but changes are not reflecting on first directives,as I am using two way data binding isolate scope, so it should reflect. any solution will be a great help.
I have my first directive as:
app.directive('damageDiagram', function () {
return {
retrict: 'AE',
scope: {
imgsrc: '=', markers: '=', pointsrc: '=', dtype:'='
},
templateUrl: 'app/components/inspections/damage-diagram.html',
link: function (scope, element, attrs) {
}
}
});
and second directive as:
app.directive('damageMarker', function ($mdDialog,inspectionService,$timeout) {
return {
restrict: 'AE',
require: '?^damageDiagram',
scope: {
marker: '=',
pointsrc: '=',
dtype:'=',
markers: '='
},
template: '<img ng-src="{{pointsrc}}" />',
link: function (scope, elem, attr) {
elem.on("click",function(e){
showDialog();
function showDialog($event) {
var parentEl = angular.element(document.body);
$mdDialog.show ({
parent: parentEl,
targetEvent: $event,
template:
'<form name="clientForm" novalidate >'+
'<md-dialog aria-label="List dialog">' +
' <md-dialog-content>'+
'<md-input-container>'+
'<md-select ng-model="dtypeValue" class="dialog-close" placeholder="Select dtype">'+
'<md-option ng-repeat="opt in dtype">{{opt}}</md-option>'+
'</md-select>'+
'</md-input-container>'+
'<md-input-container class="md-block">'+
'<label>Comment</label>'+
'<input required name="name" ng-model="comment" class="dialog-close">'+
'<div ng-messages="clientForm.name.$error">'+
'<div ng-message="required">This is required.</div>'+
'</div>'+
'</md-input-container>'+
' </md-dialog-content>' +
' <div class="md-actions" layout="row" layout-align="end center">' +
' <md-button ng-click="closeDialog()" class="md-primary">' +
' Close' +
' </md-button>' +
'<md-button ng-disabled="clientForm.$invalid" ng-click = "save()" class="md-primary">'+
'Save'+
'</md-button>'+
'<md-button ng-disabled="clientForm.$invalid" ng-click = "delete()" class="md-primary">'+
'Delete'+
'</md-button>'+
' </div>' +
'</md-dialog>'+
'</form>',
controller: DialogController
});
function DialogController($scope, $mdDialog) {
$scope.dtypeValue = scope.dtype[scope.marker.dtype.toUpperCase()];
$scope.dtype = scope.dtype;
$scope.comment = scope.marker.comment;
$scope.marker = scope.marker;
$scope.closeDialog = function() {
$mdDialog.hide();
}
$scope.save = function(){
console.log($scope.marker.id);
console.log($scope.dtypeValue);
console.log($scope.comment);
var dataSend = {};
dataSend.id = $scope.marker.id;
dataSend.comment = $scope.comment;
for(var key in $scope.dtype) {
if($scope.dtype[key] == $scope.dtypeValue) {
dataSend.dtype = key;
}
}
inspectionService.updateDiagram(dataSend).then(function(response){
console.debug("response ; "+response);
$mdDialog.hide();
scope.marker.id = response.id;
scope.marker.comment = response.comment;
scope.marker.dtype = response.dtype;
});
}
$scope.delete = function(){
var dataSend = {};
dataSend.id = $scope.marker.id;
var param = {};
param.inspection=$scope.marker.inspection;
inspectionService.deleteDiagramMarker(dataSend).then(function(response){
inspectionService.getDiagram(param).then(function(response){
$timeout(function() {
scope.$apply(function(){
scope.markers = response.results;
})
},2000);
console.debug("response ; "+response);
$mdDialog.hide();
});
});
}
}
}
});
console.log(scope.marker.top, scope.marker.left, elem);
}
}
});
My html code for damage-diagram directive is as follows:
<damage-diagram imgsrc="imgsrc" pointsrc="pointsrc" dtype="dtype"
markers="inspection.damage_diagram">
</damage-diagram>
and my html code for damage-directive is as follow:
<div style="position:relative">
<img id="23467129" ng-src="{{imgsrc}}" style="position:relative" width="100%" />
<div ng-repeat="marker in markers"
marker="marker"
markers="markers"
dtype="dtype"
pointsrc="pointsrc"
damage-marker>
</div>
</div>
controller is as follows:
app.controller('InspectionDetailCtrl', ['$scope', 'inspectionService', '$stateParams', 'Restangular','$rootScope',
function ($scope, inspectionService, $stateParams, Restangular, $rootScope) {
$scope.updateDamageImage = {};
$scope.insp_id = $stateParams.inspId;
$scope.damageImagesShow = false;
$scope.comments = [];
$scope.types = [];
$scope.selectTypeDelete = false;
$scope.commentDelete = false;
$scope.selectTypeDeleteBefore = true;
$scope.commentDeleteBefore = true;
init($scope.insp_id);
console.log("Fetching details for scope", $scope.insp_id);
function init(insp_id)
{
inspectionService.inspections.customGET(insp_id, {type: 'full'})
.then(function (data) {
$scope.inspection = data;
}, function (err) {
$scope.inspection = null;
});
}
$scope.pointsrc="app/components/inspections/pointer.png";
$scope.dtype = {
'S': 'Scratch (minor)',
'DS': 'Deep Scratch',
'D': 'Dents',
'WD': 'Wheel Damage',
'CW': 'Cracked Window',
'FT': 'Flat Tire',
'BL': 'Broken (lights)'
};
}]);
First of all two way binding in directive doesn't works like that any change reflected in main controller can be seen in directive but not other way any change in directive won't be reflected in main controller.
But there is a solution you can create an object in main controller
var x={};
x.value='to be passed in directive'
then you use same variable in directive since only once instance of object is created so any change in any directive will be reflected everywhere.

Is it okay to have $compile inside the link part of a directive?

I am using AngularJS v1.4.1 and I have the following directive. It was I thought working but now for some reason when my page loads the directive gets called twice. I checked everything I could but I cannot see anything different from before to now except the directive no longer works. Specifically what happens is it appears to be getting called twice.
app.directive('pagedownAdmin', ['$compile','$timeout', function ($compile, $timeout) {
var nextId = 0;
var converter = Markdown.getSanitizingConverter();
converter.hooks.chain("preBlockGamut", function (text, rbg) {
return text.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm, function (whole, inner) {
return "<blockquote>" + rbg(inner) + "</blockquote>\n";
});
});
return {
require: 'ngModel',
replace: true,
scope: {
modal: '=modal'
},
template: '<div class="pagedown-bootstrap-editor"></div>',
link: function (scope, iElement, attrs: any, ngModel) {
var editorUniqueId;
if (attrs.id == null) {
editorUniqueId = nextId++;
} else {
editorUniqueId = attrs.id;
}
scope.showPagedownButtons = function () {
document.getElementById("wmd-button-bar-" + editorUniqueId).style.display = 'block';
};
var newElement = $compile(
'<div>' +
'<div class="wmd-panel">' +
'<div data-ng-hide="modal.wmdPreview == true" id="wmd-button-bar-' + editorUniqueId + '" style="display:none;"></div>' +
'<textarea ng-click="showPagedownButtons()" data-ng-hide="modal.wmdPreview == true" class="wmd-input" id="wmd-input-' + editorUniqueId + '">' +
'</textarea>' +
'</div>' +
'<div data-ng-show="modal.wmdPreview == true" id="wmd-preview-' + editorUniqueId + '" class="pagedownPreview wmd-panel wmd-preview"></div>' +
'</div>')(scope);
// iElement.html(newElement);
iElement.append(newElement);
var hide = function () {
document.getElementById("wmd-button-bar-" + editorUniqueId).style.display = 'none';
}
var editor = new Markdown.Editor(converter, "-" + editorUniqueId, {
handler: hide
});
// var $wmdInput = iElement.find('#wmd-input-' + editorUniqueId);
var $wmdInput = angular.element(document.getElementById("wmd-input-" + editorUniqueId));
var init = false;
editor.hooks.chain("onPreviewRefresh", function () {
var val = $wmdInput.val();
if (init && val !== ngModel.$modelValue) {
$timeout(function () {
scope.$apply(function () {
ngModel.$setViewValue(val);
ngModel.$render();
});
});
}
});
ngModel.$formatters.push(function (value) {
init = true;
$wmdInput.val(value);
editor.refreshPreview();
return value;
});
editor.run();
}
}
}]);
Here is the code that calls the directive:
<textarea data-pagedown-admin
data-modal="cos"
id="contentText"
name="contentText"
ng-minlength="5"
ng-model="cos.content.text"
ng-required="true"></textarea>
When I debug the directive by putting a breakpoint on "var editorUniqueId" then I see it goes there twice.
Does anyone have any ideas what might be happening?
I have done this in the past without issue:
var newElement = '<div>' +
// ... more HTML ...
'</div>';
iElement.append( $compile(newElement)(scope) );

Angularjs How to push the new value for the scope object once it is used in the directives

I want to push the new values for my main scope object through the directives.
please advise me how to do that.
In my sample I am using $scope.saveNew function for this but it is not working.
it throw the error "Error: $scope.users is undefined"
myApp.directive("userName", function() {
var editorTemplate = '<td class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'Add New' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="newValue.name">' +
'<input ng-model="newValue.phone">' +
'Save' +
' or ' +
'cancel.' +
'</div>' +
'</td>';
return {
restrict: "A",
template: function(element, attrs) {
if(attrs.type=='add'){
return editorTemplate;
}
else{
return editorTemplate1;
}
},
scope: {
value: "=userName"
},
controller: function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
$scope.saveNew = function() {
$scope.users.push({name:$scope.newValue.name,phone:$scope.newValue.phone});
$scope.disableEditor();
};
}
};
});
That's because $scope.users was not defined.
Before pushing objects into it, you should initialize it outside saveNew function:
$scope.users = [];
You are creating an isolated scope through scope: { .. }. This means that your directive's scope wont' prototipically inherit from the parent scope. There are two fixes to this situation:
First solution:
If you think that your directive should be isolated, pass users to your directive as you do for userName.
Second solution:
If you wish to access the parent's scope users variable, set scope to true. However, be aware that every instance of your directive will share the same users object/array.

generating a variable-length dropdown based on input value in an AngularJS directive

I want to provide a page selection directive that generates "Page [ 1 ] of x". The number of pages in the dropdown is dependent upon values passed into the directive, so it can't be part of a static template. I'm having a difficult time figuring out how/where to generate the <select><option>...</select>.
I have tried, unsuccessfully, to do it via:
an $observe (and $watch) in link
a function added to $scope in controller, which returns $compile(markup)($scope) (This gives the error Error: [$parse:isecdom] Referencing DOM nodes in Angular expressions is disallowed!)
a sub-directive for the <select> element (The link $observer never seemed to get the recordCount updates, regardless of inherited or shared scope.)
ng-repeat in the template
Here's my mangled code, as it currently stands.
HTML
<x-pager
record-count="{{recordCount}}"
page-size="pageSize"
page-number="pageNumber"
set-page="selectPage(page)"
></x-pager>
JS
module.directive("pager", ["$compile",
function ($compile)
{
return {
template: "<div class='pager' ng-show='recordCount > pageSize'>\
{{recordCount}} results\
<button>« Prev</button>\
page <select>\
<option>#</option>\
</select> of {{calcPages()}}\
<button>Next »</button>\
</div>",
replace: true,
restrict: "E",
scope: {
recordCount: "#",
pageSize: "=",
pageNumber: "=",
setPage: "&"
},
link: function (scope, element, attrs)
{
/*
* We can't build the page selection dropdown until
* we know how many records we have. Register an
* observer to do this when recordCount changes.
*/
attrs.$observe("recordCount", function (recCnt)
{
var html;
var pages;
var i;
if (angular.isDefined(recCnt)) {
html = "<select>\n";
pages = Math.ceil(scope.recordCount / scope.pageSize);
for (i=1; i<=pages; i++) {
html += " <option value='" + i + "'>" + i + "</option>\n";
}
html += "</select>";
console.log("generatePageSelect html", html);
html = $compile(html)(scope);
// add the template content
// angular.element("select.page-selector").html(html);
// template: page <select class='page-selector'></select> of {{calcPages()}}\
}
});
},
controller: function ($scope)
{
$scope.calcPages = function ()
{
return Math.ceil($scope.recordCount / $scope.pageSize);
};
function genPagesArray ()
{
var pages = $scope.calcPages();
var i;
var pagesArray = [];
for (i=0; i<pages; i++) {
pagesArray.push(i);
}
return pagesArray;
}
$scope.pagesArray = genPagesArray();
console.log("$scope.pagesArray", $scope.pagesArray);
// template: page {{generatePageSelect()}} of {{calcPages()}}\
$scope.generatePageSelect = function ()
{
var html = "<select>\n";
var pages = $scope.calcPages();
var i;
for (i=1; i<=pages; i++) {
html += " <option value='" + i + "'>" + i + "</option>\n";
}
html += "</select>";
return $compile(html)($scope);
};
}
};
}
]);
To expand on my comment from earlier, here's a directive that does (most of) what you want it to do.
angular.module('Test', []).controller('TestCtrl', function($scope) {
$scope.pageSize = 10;
$scope.pageNumber = 1;
$scope.recordCount = 30;
}).directive("pager", function () {
return {
template: '<div class="pager" ng-show="recordCount > pageSize">\
{{recordCount}} results\
<button ng-click="pageNumber = pageNumber - 1" ng-disabled="pageNumber <= 1">« Prev</button>\
page <select ng-model="pageNumber" ng-options="i for i in pages"></select> of {{totalPages}}\
<button ng-click="pageNumber = pageNumber + 1" ng-disabled="pageNumber >= totalPages">Next »</button>\
</div>',
replace: true,
restrict: "E",
scope: {
recordCount: "#",
pageSize: "=",
pageNumber: "=",
setPage: "&"
},
link: function (scope, element, attrs) {
attrs.$observe("recordCount", function (count) {
if (angular.isDefined(count)) {
scope.recordCount = parseInt(count);
var i;
scope.totalPages = Math.ceil(scope.recordCount / scope.pageSize);
scope.pages = [];
for (i=1; i<=scope.totalPages; i++) {
scope.pages.push(i);
}
}
});
}
}
});
Plunkr here.

Resources