How to bind value to angularjs directive? - javascript

I am working on a directive to create a custom dropdown. I have managed to bring the directive code to get it replaced.
Below is the directive tag in the html
<dropdown placeholder="Country.." list="count" selected="item" property="name"></dropdown>
Below is the html template that replaces the directive tag
<div class="dropdown-container" ng-class="{ show: listVisible }">
<div class="dropdown-list">
<div>
<div ng-repeat="item in list" ng-click="select(item)" ng-class="{ selected: isSelected(item) }">
<span>{{property !== undefined ? item[property] : item}}</span>
<i class="fa fa-check"></i>
</div>
</div>
</div>
Below is the angularjs directive
App.directive("dropdown", function ($rootScope) {
return {
restrict: "E",
templateUrl: "/app/dropdownTemplate.html",
scope: {
placeholder: "#",
list: "=",
selected: "=",
property: "#"
},
link: function (scope) {
scope.listVisible = false;
scope.isPlaceholder = true;
scope.select = function (item) {
scope.isPlaceholder = false;
scope.selected = item;
};
scope.isSelected = function (item) {
return item[scope.property] === scope.selected[scope.property];
};
scope.show = function () {
scope.listVisible = true;
};
$rootScope.$on("documentClicked", function (inner, target) {
console.log($(target[0]).is(".dropdown-display.clicked") || $(target[0]).parents(".dropdown-display.clicked").length > 0);
if (!$(target[0]).is(".dropdown-display.clicked") && !$(target[0]).parents(".dropdown-display.clicked").length > 0)
scope.$apply(function () {
scope.listVisible = false;
});
});
scope.$watch("selected", function (value) {
scope.isPlaceholder = scope.selected[scope.property] === undefined;
scope.display = scope.selected[scope.property];
});
}
}
});
I am trying to get the list of countries from another controller whose function is as follows,
addressService.getCountries().success(function (response) {
angular.copy(response, $scope.list);
}
How do I bind the values from the controller to my directive when the page loads?
EDIT: What do I do as the directive loads before the function addressService.getCountries() gets called?

you can make a factory that will be useful throughout project to make to make a variable accessible whenever and wherever, in any controller or in any directive you want to use that variable you can easily access
this is how we can make factory,
(function() {
"use strict";
angular.module('dataModule',[])
.factory('datafactory',function(){
return {
credentials : {},
};
});
})();
After making this factory you can inject this module in your module and this factory in your controller
whenever you want you can use this like
addressService.getCountries().success(function (response) {
datafactory.country = response
}
in your controller
$scope.myvar =datafactory.country
in future you use this factory for any such variable that will be accessible everywhere for example you want to store list of state globally
datafactory.state =["abc","def","xyz"]

Related

AngularJS Directive Wont Call Link After Compile and Insertion into DOM

For some reason the data on the myScope object isn't being passed and rendered. The directive inserts correctly via the append (static html from the template appears) but it's as if the digest hasn't ran to do the loop. I'd expect the link function to be called, which it isn't.
I've tried running $apply within a $timeout after insertion.
.directive("slHyperlinkSelector", function () {
return {
templateUrl: "slHyperlinkSelector.html",
restrict: "E",
scope: {
data: "=",
},
link: function (scope) {
console.error('Is called');
}
};
});
$scope.$on('replaceView', function (event, builder) {
var element = $element.find(".sl-account-canvas").children();
var appendEl = builder(function () {
$scope.maintain = false;
});
$scope.maintain = true;
element.parent().append(appendEl);
$timeout(function () {
$scope.$apply();
});
});
$rootScope.$broadcast('replaceView', function (closer) {
var myScope = scope.$new(true);
var destroy = function() {
closer();
myScope.$destroy();
};
myScope.data = {
links: scope.formerNinos
.map(function (formerNino) {
return {
textContent: formerNino.nino,
onClick: function () { alert(formerNino.nino); destroy(); }
};
})
};
return $compile('<sl-hyperlink-selector data="data"></sl-hyperlink-selector>')(myScope);
});
(Template file below)
<h2>Former National Insurance Numbers</h2>
<ul class="sl-primary-card">
<li ng-repeat="link in data.links">
<h4><a href ng-click="link.onClick()">{{ link.textContent }}</a>
</h4>
</li>
</ul>
I expect the directive to display several links.

AngularJS can't call controller method inside directive

I'm working on a hybrid Ionic1 app and I am not able to call a controller method from inside a directive.
The problem is that inside the directive I'm able to pass controller's variables (such as variable apps) but I am not able to call removeCredentials() inside card-app directive.
I've tried so many ways to make it work but none of them solved the problem. I've also tried using the & to bind the controller method inside the directive but clicking on the X icon in the directive template triggers nothing.
Right now the code looks like this:
Controller AppsCtrl in apps.js
angular.module('testprofile')
.controller('AppsCtrl', function($scope, $state, $log, $ionicHistory, $filter, $ionicPopup, UserData, CredentialStoreService, ionicMaterialMotion, ionicMaterialInk, Auth, Effects, CredentialStoreService, Globals, Validations) {
$scope.onlyMyApps = true;
$scope.showNoItems = false;
var translate = $filter('translate');
$scope.apps = [];
$scope.mapAppsFromCredentials = function(credentials) {
var map = credentials.map(function(cred) {
/* app: "App 1"
appId: 7
profile: "Default Profile"
profileId: 0
*/
var app = UserData.getLocalApp(cred.app);
if (!app) {
$log.debug("apps.js::mapAppsFromCredentials - no local app matching credentials: "+JSON.stringify(cred));
return null;
}
var curHelper = UserData.getCurrentAppHelper(app);
return {
used: Boolean(cred.used),
id: app.id,
title: app.nameApp,
profile: cred.profile,
desc: curHelper ? curHelper.name : 'NO HELPER'
};
})
return map;
}
$scope.refresh = function() {
$log.debug("apps.js::AppsCtrl - REQUEST CREDENTIAL LIST...");
CredentialStoreService.getCredentialList(
//ON SUCCESS
function(response) {
$log.debug("apps.js::AppsCtrl - ...CREDENTIAL LIST RESPONSE: " + JSON.stringify(response));
$scope.apps = $scope.mapAppsFromCredentials(response);
Effects.init();
$scope.showNoItems = true;
for (var i = 0; i < $scope.apps.length; i++) {
if ($scope.apps[i].used) {
$scope.showNoItems = false;
break;
}
}
},
//ON ERROR
function() {
$log.debug("apps.js::AppsCtrl - ...CREDENTIAL LIST ERROR");
}
)
}
$scope.$on("$ionicView.enter", function () {
$log.debug("apps.js::$ionicView.enter - ENTER");
$scope.refresh();
});
//REMOVE CREDENTIALS
$scope.removeCredentials = function() {
$log.debug("app-details.js::removeCredential CONFIRMATION...");
$ionicPopup.confirm({
title: translate('Remove Credentials'),
template: translate('Are you sure you want to delete current credentials') + ' ' + app.nameApp + '?',
cancelText: translate('No'), // String (default: 'Cancel'). The text of the Cancel button.
//cancelType: '', // String (default: 'button-default'). The type of the Cancel button.
okText: translate('Yes'), // String (default: 'OK'). The text of the OK button.
okType: 'button-energized-900' // String (default: 'button-positive'). The type of the OK button.
}).then(function(res) {
if(res) {
$log.debug("app-details.js::removeCredential CONFIRMED!");
Auth.askPIN(true)
.then( function() {
CredentialStoreService
.removeCredential(
//authenticationProfileName
UserData.getAuthProfile(app).name,
//appProfileName
app.name,
// ON SUCCESS
onRemovedCredential,
// ON ERROR
Globals.onError);
});
} else {
$log.debug("app-details.js::removeCredential REJECTED.");
}
});
};
var onRemovedCredential = function() {
$log.debug("app-details.js::removeCredential CREDENTIALS REMOVED!");
Effects.showToast(translate('CREDENTIALS REMOVED')+'.');
$ionicHistory.nextViewOptions({
disableBack: true
});
$state.go('menu.apps', {}, {reload:true});
};
})
Directives in inclusions.js
angular.module('testprofile')
.directive('cardProfile', function() {
return {
scope: {
data:"=cardSource"
},
restrict: 'E',
templateUrl: 'app/templates/card-profile.html'
};
})
.directive('cardApp', function() {
return {
scope: {
data:"=cardSource",
},
restrict: 'E',
templateUrl: 'app/templates/card-app.html',
controller: 'AppsCtrl',
controllerAs: "ac",
bindToController: true,
link: function(scope, element, attrs, ctrl) {
scope.removeCredentials = function() {
return scope.ac.removeCredentials();
};
}
};
})
.directive('pinRequired', function(Auth) {
return {
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
attrs.authorized = false;
element.on('click', function(e){
// console.log("\t\t + attrs.authorized:"+attrs.authorized);
if (attrs.authorized) {
if (attrs.href) location.href = attrs.href;
return true;
} else {
e.preventDefault();
e.stopPropagation();
Auth.askPIN(true)
.then(function() {
// console.log("inclusions.js::pinRequired - askPin - AUTHORIZED");
//THE PIN IS CORRECT!
attrs.authorized = true;
element.triggerHandler('click');
attrs.authorized = false;
});
}
});
}
};
})
;
apps.html
<ion-view view-title="Apps">
<ion-content padding="true" class="has-header bg-stable">
<h4 class="list-title"><i class="material-icons"></i> {{ 'App List' | translate }}
<!--label class="toggle toggle-calm pull-right toggle-small">
<span class="toggle-label" translate>My Apps</span>
<input type="checkbox" ng-model="onlyMyApps">
<div class="track">
<div class="handle"></div>
</div>
</label-->
</h4>
<ion-list class="">
<card-app ng-repeat="app in apps | filter:(onlyMyApps || '') && {used: true}"
card-source="app"
class="{{app.used ? 'used' : 'unused'}}"></card-app>
<div class="item padding" ng-show="showNoItems"><p class="text-center">{{ 'No saved credentials' | translate}}</span>.</p>
</div>
</ion-list>
</ion-content>
</ion-view>
card-app.html
<ion-item
menu-close=""
class="card item-thumbnail-left item-icon-right app-item">
<!--<img ng-src="{{data.thumb}}">-->
<span style="line-height:70px;vertical-align:middle" class="item-image avatar-initials h2">{{data.title|initials}}</span>
<div class="item">
<h2 class="inline-block">
<span ng-bind="data.title"></span>
</h2>
<span class="icon-right material-icons" ng-click='removeCredentials()'></span>
<div>
<span>{{'Active helper' | translate}}</span>
<h3>{{ data.desc | translate }}</h3>
</div>
</div>
</ion-item>
Solution: at the end the click event was not triggered because I had to add pointer-events: auto; and cursor: pointer; in my css
If your directive needs to call just a single function from a parent controller, just pass it as parameter (like you tried).
First ensure that controller scope is available for directive. For example create simple method and call it before directive:
// to ensure scope is ok
<button ng-click="alertMethod()"></button>
<card-app ng-repeat='' card-source='' class='' my-method="alertMethod()"></card-app>
// directive
//
scope: {
myMethod: '&'
},
template: "<button ng-click='myMethod()'>Click</button>"
That should work, but if your controller scope is not available maybe you need to use named controllers:
https://docs.angularjs.org/api/ng/directive/ngController
Using controller as makes it obvious which controller you are accessing in the template when multiple controllers apply to an element.
Than in your code:
// controller
this.alertMethod = function() {
alert('Hello');
}
// view
<div ng-app="myApp" ng-controller="testCtrl as ctrl">
<card-app ng-repeat='' card-source='' class='' my-method="ctrl.alertMethod()"></card-app>

Call function in Directive when Parent Scope Variable Changes

I need to call a function in my directive when the value of variable in the parent controller changes. I tried adding a watch (I'm obviously doing it wrong) because nothing happens when the value changes. Here is the directive:
angular.module('ssq.shared').directive('checkboxPicklist', function() {
return {
restrict: 'E',
templateUrl: '/Scripts/app/Shared/directives/checkboxPicklist.html',
replace: true,
scope: {
itemId: '=',
list: '=',
nameProp: '=',
title: '#',
searchPlaceholder: '#',
callbackFn: '&',
callMore: '&',
clear: '='
},
link: function (scope, element, attrs) {
scope.query = '';
var parent = scope.$parent;
var clear = parent.clear;
scope.$watch(clear, function () {
if (clear == true) {
this.clearAll();
}
})
var child = element.find('.dropdown-menu');
child.on({
'click': function (e) {
e.stopPropagation();
}
});
var selectedItemFn = function (item) {
return item.selected;
};
scope.getSelectedCount = function () {
return _.filter(scope.list, selectedItemFn).length;
};
scope.loadMore = function () {
scope.callMore();
};
scope.allSelected = function(list) {
var newValue = !scope.allNeedsMet(list);
_.each(list, function(item) {
item.selected = newValue;
scope.callbackFn({ object: item });
});
};
scope.allNeedsMet = function(list) {
var needsMet = _.reduce(list, function(memo, item) {
return memo + (item.selected ? 1 : 0);
}, 0);
if (!list) {
return (needsMet === 0);
}
return (needsMet === list.length);
};
function clearAll() {
_.each(list, function (item) {
item.selected = false;
})
}
}
};
});
Here is where I am trying to watch the variable:
var parent = scope.$parent;
var clear = parent.clear;
scope.$watch(clear, function () {
if (clear == true) {
this.clearAll();
}
})
Here is the function in my parent controller that changes the value of "clear"
$scope.clearFilters = function (clear) {
$scope.clear = true;
$scope.form.selected.services = [];
$scope.form.picked.areas = [];
$scope.form.certified.verifications = [];
$scope.form.subscribed.subscriptions = [];
$scope.form.OperatorBusinessUnitID = null;
$scope.form.OperatorBusinessUnitID = null;
};
I tried setting an attribute called "clearFilter" and assigning the variable to it, but the watch still doesn't trigger:
scope.$watch(attrs.clearFilter, function (value) {
if (value == true) {
this.clearAll();
}
});
<checkbox-picklist data-item-id="'servicesPicklist'"
data-search-placeholder="Search Services"
data-list="services"
data-title="Service(s)"
data-name-prop="'vchDescription'"
data-callback-fn="addService(object)"
call-more="loadMoreServices()"
clear-filter="clear">
</checkbox-picklist>
I'm not really sure if I am calling the function correctly. scope.$parent above does get the initial value of the variable from the parent scope, but once it changes, it never updates.
EDIT:What I have discovered is the normal scope.$watch('clear', function...) is not working it seems because the directive is in "ssq.shared" module which is injected in my my Main Module "myModule" (see below), so even though the page the directive is on uses my 'GeneralSearchCtrl', I cannot get the watch to work on the variable located in 'GeneralSearchCtrl'. If I use scope.$parent.clear I can see the value of the variable, but I cannot seem to set a watch on it.
My module injection code:
var app = angular.module('myModule', ['ui.bootstrap', 'checklist-model', 'ssq.shared', 'ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.pagination', 'ui.grid.selection', 'ui.grid.exporter', 'ui.grid.autoResize', 'ui.router', 'cgBusy', 'ui.mask', 'ngFileUpload', 'ngSanitize']);
The page where the directive lives uses:
<div ng-app="myModule" ng-controller="GeneralSearchCtrl">
I am unable to get a watch on the variable located in GeneralSearchCtrl.
Any assistance is greatly appreciated!!!!
Add a watch for the $scope value and call the function,
scope.$watch('clear', function(newValue, oldValue) {
if (newValue) {
this.clearAll();
}
});
scope.$watch(clear, function () {
if (clear == true) {
this.clearAll();
}
})
This.clearAll() doesn't exist in the scope of your $watch function. Simply calling clearAll() should work better.
The signature of the watch function is not correct.
scope.$watch('clear', function (new, old) {}
As it turns out, the problem was that the directive had scope:{...} in its definition which stopped the "normal" scope.$watch('clear', function...) from working. I had to add clear: '=' to the scope list like so:
replace: true,
scope: {
itemId: '=',
list: '=',
nameProp: '=',
title: '#',
searchPlaceholder: '#',
callbackFn: '&',
callMore: '&',
clear: '='
},
Then clear="clear" to the directive like so:
<checkbox-picklist data-item-id="'servicesPicklist'"
data-search-placeholder="Search Services"
data-list="services"
data-title="Service(s)"
data-name-prop="'vchDescription'"
data-callback-fn="addService(object)"
call-more="loadMoreServices()"
clear="clear">
</checkbox-picklist>
Then in the directive I had to add the watch like this for it work:
scope.$watch('$parent.clear', function (newValue, oldValue) {
if (newValue == true) {
clearAll();
alert('it works!');
}
})
I really hope this helps someone else as this was difficult for me to figure out. Happy coding!

Angularjs: How to update parent scope in directive without using isolated scope when the attribute is passed in within ngRepeat

I have a simple angularjs directive that uses JQuery to convert a template to a draggable dialog
var myApp = angular.module("myApp", []);
myApp.controller('myCtrl', function ($scope) {
$scope.tasks = [{
name: 'learn angular',
show: false
}, {
name: 'build an angular app',
show: false
}];
$scope.showBox = function (taskname) {
for (var i = 0; i < $scope.tasks.length; i++) {
if ($scope.tasks[i].name === taskname) {
$scope.tasks[i].show = !$scope.tasks[i].show;
}
}
}
});
myApp.directive("draggableDialog", function () {
return {
template: 'task: {{task.name}}',
link: function (scope, element, attrs) {
element.dialog({
title : "My Dialog",
autoOpen: false
});
element.bind("dialogclose", function () {
if (!scope.$$phase) {
scope.$apply(function () {
scope[attrs.draggableDialog] = false; //here is the problem
});
}
});
scope.$watch(attrs.draggableDialog, function (v) {
if (v) {
element.dialog("open");
} else {
element.dialog("close");
}
});
}
}
});
I am using this directive in a ngRepeat
<div>
<h2>Draggable Dialog</h2>
<div ng-controller="myCtrl">
<ul class="unstyled">
<li ng-repeat="task in tasks">
<button ng-click="showBox(task.name)">show {{task.name}}</button>{{task.show}}
<div draggable-dialog="task.show">test</div>
</li>
</ul>
</div>
</div>
Refer to this fiddle: http://jsfiddle.net/tianhai/BEtPk/#base
When the user manually close the dialog, I can detect the event and I want to set $scope.task[i].show in myCtrl to false. How can I do it? I am not able to use isolated scope two way binding as I am using this directive together with another directive also taking in $scope.task.
You have attrs.draggableDialog set to "task.show" so when you do
scope[attrs.draggableDialog] = false you end up with a element attached to scope that you could access with scope['task.show'] which is different than scope['task']['show'] or scope.task.show
To generically set a parent variable to false you need to eval a string containing the assignment. For you it would look like this:
scope.$eval(attrs.draggableDialog + ' = false;');
Hope this helped

how to use services in angularjs to share data across multiple controllers?

I want to share data across multiple controllers in angularjs without using $rootScope. I am learning to use services for the same. What is wrong with the following code?
This is my controllers.js :
function Ctrl1(shareData) {
shareData.setValue("my_data");
}
function Ctrl2($scope, shareData) {
$scope.value = shareData.getvalue();
}
This is my services.js :
angular.module('connectionsServices').
factory('shareData', function() {
var shareVar = 'undefined';
return {
getValue: function() {
return shareVar;
},
setValue: function(value) {
shareVar = value;
}
}
});
But it is not working.
You can't bind service variable to controller variable, your code is changing shareVar but you are unable to see it, you can update the view like this:
HTML:
<div ng-app="testApp">
<div ng-controller="Ctrl2">{{ value }}
<br />
<div ng-controller="Ctrl1">
<button type="button" ng-click="change(); update();">Change me!</button>
</div>
</div>
</div>
JS:
angular.module('testApp', []).factory('shareData', function ($window) {
var shareVar = 'undefined';
return {
getValue: function () {
return shareVar;
},
setValue: function (value) {
shareVar = value;
}
};
});
function Ctrl1($scope, $window, shareData) {
$scope.change = function () {
shareData.setValue("my_data");
};
}
function Ctrl2($scope, shareData) {
$scope.value = shareData.getValue();
$scope.update = function () {
$scope.value = shareData.getValue();
};
}
Services provide an easy way for us to share data and functionality
throughout our app. The services we create are singletons that can be
injected into controllers and other services, making them the ideal
place for writing reusable code.
Follow its link https://thinkster.io/a-better-way-to-learn-angularjs/services

Categories

Resources