How to call directive template on click? - angularjs

I am trying to make a reusable modal dialog and I would like to load directive template on click directive itself..
function modalDialog() {
var directive = {
restrict: 'A',
link: linkFunc,
template: '<div class="modalBox--blur">' +
'<div class="modalBox">' +
'<h3>' {{title}} '</h3>' +
'<h4>' {{text}} '</h4>' +
'<button ng-click="answer(true)">Cancel</button>' +
'<button ng-click="answer(false)">Ok</button>' +
'</div>' +
'</div>',
scope: {
title: '=dialogTitle',
text: '=dialogTxt'
},
transclude: true
};
return directive;
function linkFunc($scope, element, attrs) {
element.on('click', function () {
$scope.newEl = element.parent();
$scope.newEl.append(...template Here...???);
});
}
}
This is how directive is set in the view:
<button
modal-dialog
dialog-title="modalBox.title"
dialog-txt="modalBox.subText"
type="button"
ng-click="deleteSth()"
class="button">
</button>
I can't figure out how to load template on element click :
element.on('click', function () {
$scope.newEl = element.parent();
$scope.newEl.append(template????);
});
Any tips?
Thank you in advance!

You can get the template with $templateCache
Like $templateCache.get('templateId.html')

Solution was compiling the template:
scope.modal = $compile(' <div class="modalBox--blur">' +
'<div class="modalBox">' +
'<h3>{{title}}</h3>' +
'<h4>{{text}}</h4>' +
'<button ng-click="dialogAnswer(true)">Annuleren</button>' +
'<button ng-click="dialogAnswer(false)">Ok</button>' +
'</div>' +
'</div>')(scope);
element.on('click', function () {
scope.newEl = element.parent();
scope.newEl.append(scope.modal);

Related

$watch doesn't triggered in directive

i have a directive:
function templateFn() {
return '<div class="card-wrapper">'
+ '<div class="cards-carousel">'
+ '<div class="cards-carousel-inner">'
+ '<div ng-repeat="item in cards">'
+ '<div class="item" ng-class="{\'active\': $index == $parent.ngModel.id, \'next\' : $index == $parent.ngModel.id + 1, \'prev\' : $index == $parent.ngModel.id - 1}">'
+ '<label><input id="{{item.eid}}" type="radio" ng-value="item" ng-model="$parent.ngModel">'
+ '<img alt="{{item.id}}" ng-src="{{item.url}}" /></label>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '</div>';
}
function linkFn(scope, element, attrs) {
scope.cards = JSON.parse(attrs.cards);
scope.$watchCollection('cards', function(newValue, oldValue) {
if (newValue !== oldValue) {
scope.cards = newValue;
}
}, true);
}
return {
restrict: 'E',
required: ['ngModel'],
scope: {
ngModel: '='
},
link: linkFn,
replace: 'true',
template: templateFn
};
and in controller i make rest request to take needed cards. Cards pushed like this: ctrl.cardList.push(card)
This is my view:
<ab-card-carousel cards="{{ctrl.cardList}}"
ng-model="ctrl.debitCardItem">
</ab-card-carousel>
and in the view, when i console.log({{ctrl.cardList}}) it renders good, and as i need, but changes not made in scope.$watch function.
Can anybody help me?
Remove the interpolation from the attribute:
<ab-card-carousel ̶c̶a̶r̶d̶s̶=̶"̶{̶{̶c̶t̶r̶l̶.̶c̶a̶r̶d̶L̶i̶s̶t̶}̶}̶"̶
cards="ctrl.cardList"
ng-model="ctrl.debitCardItem">
</ab-card-carousel>
Add one-way binding:
scope: {
ngModel: '=',
cards: "<",
},
Change the linkFn to:
function linkFn(scope, element, attrs) {
̶s̶c̶o̶p̶e̶.̶c̶a̶r̶d̶s̶ ̶=̶ ̶J̶S̶O̶N̶.̶p̶a̶r̶s̶e̶(̶a̶t̶t̶r̶s̶.̶c̶a̶r̶d̶s̶)̶;̶
scope.$watchCollection('cards', function(newValue, oldValue) {
if (newValue !== oldValue) {
scope.cards = newValue;
}
}, true);
}

Angular Directive: Button Spinner

I have created a directive, its behave like when you click on button it will show spinner or loader, its show user there is something in progress like API call or moving to another page, I am facing following issues:
If more than one directive used on page, its show spinner for both button instead clicked button, i want it should show only for clicked button
In some scenarios, clicked event bind three times, as there are three span used inside button to show spinner, there is some issue with event propagation
Here is directive code:
(function () {
'use strict';
angular
.module('app.base')
.directive('buttonSpinner', directiveFunction)
.controller('btnController', ControllerFunction);
// ----- directiveFunction -----
directiveFunction.$inject = [];
/* #ngInject */
function directiveFunction() {
var directive = {
restrict: 'E',
scope: {
label: "#",
available: "="
},
controller: 'btnController',
controllerAs: 'vm',
replace: true,
transclude: true,
template:
'<button ng-disabled="vm.isDisabled">'
+ '<span ng-hide="vm.isClicked">{{label}}</span>'
+ '<div class="spinner" ng-show="vm.isClicked">'
+ '<span class="bounce1"></span>'
+ '<span class="bounce2"></span>'
+ '<span class="bounce3"></span>'
+ '</div>' +
'</button>'
};
return directive;
}
// ----- ControllerFunction -----
ControllerFunction.$inject = [ '$scope' ];
/* #ngInject */
function ControllerFunction( $scope ) {
var vm = this;
vm.isClicked = false;
$scope.$on('APICALLED', function(event, data){
vm.isClicked = data.done;
if( data.elem ) {
angular.element(document.getElementById(data.elem))[0].disabled = true;
} else {
vm.isDisabled = data.disable;
}
});
}
})();
How to use:
In View:
<button-spinner class="btn btn-primary btn-block btn-lg" type="submit" ng-click="vm.notifyMe($event)" label="Notify Me" available="vm.notify.is" id="notifyMeBtn"></button-spinner>
In Controller:
to show spinner:
$rootScope.$broadcast('APICALLED', {'done': true, 'disable': true});
to hide spinner:
$rootScope.$broadcast('APICALLED', {'done': false, 'disable': false});
Your problem is related to your $broadcast's so its a pattern problem. $rootScope.$broadcast() isnt a good solution at all. e.g.) You need to destroy $rootScope.$broadcast.$on() bindings manually. Just parse a unique scope var into the directive like loading. This scope param could be handled by the controller itself for each loading procedure:
/* #ngInject */
function directiveFunction() {
var directive = {
restrict: 'E',
scope: {
label: "#",
available: "=",
loading: "="
},
controller: 'btnController',
controllerAs: 'vm',
replace: true,
transclude: true,
template:
'<button ng-disabled="vm.isDisabled">'
+ '<span ng-hide="vm.isClicked">{{label}}</span>'
+ '<div class="spinner" ng-show="vm.loading">'
+ '<span class="bounce1"></span>'
+ '<span class="bounce2"></span>'
+ '<span class="bounce3"></span>'
+ '</div>' +
'</button>'
};
return directive;
}
View
<button-spinner class="btn btn-primary btn-block btn-lg"
type="submit"
ng-click="vm.notifyMe($event)"
loading="vm.isLoading"
label="Notify Me"
available="vm.notify.is"
id="notifyMeBtn"></button-spinner>

How do I dynamically change class in Angular direcitves?

I'm trying to write a custom directive to replace similar buttons on my page. But when I move ng-class into directive's template, it's not working anymore. Is it wrong to include ng-class within custom directive? Should I use addClass and removeClass in link function instead?
html:
<dt-button ngclass="{'active-button': selectedRows.length >=1}" text="tablebuttons.delete" icon="v-delete" ng-click="deleteDialog()"></dt-button>
directive
.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
ngclass: '='
},
link: function(scope, ielem, iattrs) {
},
template:
'<button ng-class="{{ngclass}}">' +
'<span class="{{icon}}"></span>' +
'<p translate="{{text}}">' +
'</p>' +
'</button>'
}
})
try use this. change class to ng-class in your template.
you pass a model to directive for text in view while it is not 2 way data binding.
template:
'<button class="active-button" ng-class="{{ngclass}}">' +
'<span class="{{icon}}"></span>' +
'<p translate="{{text}}">' +
'</p>' +
'</button>'
// Code goes here
var app = angular
.module('MyApp', [])
.controller('Main', ['$scope',
function($scope) {
var vm = this;
vm.selectedRows = 4;
vm.deleteDialog = function() {
console.log(vm.selectedRows);
vm.selectedRows = 0;
}
}
])
.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
ngclass: '='
},
controller: "Main as ctrl",
link: function(scope, ielem, iattrs) {
},
template: '<button ng-class="ngclass" >' +
'<p>{{text}}</p>' +
'</button>'
}
});
.active-button {
background-color: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="main-content" ng-app="MyApp" ng-controller="Main as ctrl">
<div>
<dt-button ngclass="{'active-button':ctrl.selectedRows >=1}" ng-click="ctrl.deleteDialog()" text="delete"></dt-button>
</div>
</div>
I think nothing wrong with your approach to put ng-class at template of directive. I have tried to reproduce your code snippet at this plunk it is give the correct class name active-button which i defined at style.css with background color blue. But because i don't know much about expression selectedRows.length >=1 on your ngclass attribute, i make it just to true value which will always give active-button class to the element. When you change it to false, it will remove the active-button class.
My guess is seem something wrong with your expression selectedRows.length >=1. At following element declaration :
<dt-button ngclass="{'active-button': selectedRows.length >=1}" text="tablebuttons.delete" icon="v-delete" ng-click="deleteDialog()"></dt-button>
Maybe you can check by bind those expression return value to the element with double curly brace or any other way.
Small correction for your code, you may need to put semicolon ( ; ) at the end of return keyword inside .directive().
Try This
jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.selectedRows = [0];
$scope.tablebuttons = {delete:"Delete"};
$scope.deleteDialog = function(){
$scope.selectedRows = [];
}
});
jimApp.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
myClass: '#'
},
link: function(scope, ielem, iattrs) {
console.log(scope.myClass);
},
template:
'<button class="{{myClass}}">' +
'<span class="{{icon}}"></span>' +
'{{text}}' +
'</button>'
}
})
.active-button{
background:red;
}
.inactive-button{
background:#ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
<dt-button my-class="{{selectedRows.length?'active-button':'inactive-button'}}" text="{{tablebuttons.delete}}" icon="v-delete" ng-click="deleteDialog()"></dt-button>
</div>

ng-model two way binding not working with ui bootstrap modal

I have following code for working with ui bootstrap modal. I am having a input field whose value has to be captured on the controller. But the input field value is not getting reflected on the controller after an value is entered on the modal.
angular.module('myApp')
.controller('mainController', ['$scope', '$modal', function($scope, $modal) {
$scope.openModal = function() {
$modal.open({
templateUrl: 'modal.html',
controller: BoardController
});
};
}])
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h4 ng-bind="dialogTitle"></h4>' +
'</div>' +
'<div class="modal-body" ng-transclude></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ' +
'ng-click="cancel()">Close</button>' +
'<button type="button" class="btn btn-primary" ' +
'ng-click="ok()">Save</button>' +
'</div>' +
'</div>'
};
});
var BoardController = function ($scope, $modalInstance) {
$scope.dialogTitle = 'Create new item';
$scope.placeholder = 'Enter item name';
$scope.inputname = '';
$scope.ok = function () {
console.log($scope.inputname);
$modalInstance.dismiss('cancel');
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
In 'modal.html' i have the following code:
<modal-dialog>
<input type="text" class="form-control" id="enter-name"
ng-model="inputname" placeholder={{placeholder}}>
{{ inputname }}
</modal-dialog>
So, after entering some text into the inputfield when i click the save the following line under $scope.ok() prints blank.
console.log($scope.inputname);
I guess this has something to do with scopes or may be transclusion. But i am not able to figure out whats causing this. I couldnt find the updated value in developer console also.
The problem here is transclusion. ngTransclude directive creates one more scope, but it is a sibling scope. Using transclusion makes it very difficult to access your scope. In your case you could retrieve model value like this:
$scope.ok = function () {
console.log($scope.$$childHead.$$nextSibling.inputname);
$modalInstance.dismiss('cancel');
};
But of course this is terrible. Fortunately, you can control what scope transclusion will use for rendered template if you make transclusion manually. For this you need to use link function with the fifth argument which is transclude function.
Your directive will become (note, that you don't use ng-tranclude directive in template anymore):
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h4 ng-bind="dialogTitle"></h4>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ng-click="cancel()">Close</button>' +
'<button type="button" class="btn btn-primary" ng-click="ok()">Save</button>' +
'</div>' +
'</div>',
link: function(scope, element, attrs, controller, transclude) {
transclude(scope, function(clone) {
var modalBody = element[0].querySelector('.modal-body');
angular.element(modalBody).append(clone);
});
}
};
});
Demo: http://plnkr.co/edit/I7baOyjx4pKUJHNkxkDh?p=preview

Angularjs autocomplete in already created directives

I already have a directive for inputbox. i need to implement Autocomplete feature on that
here is my directive code
app.directive("bhAddCategory", ["$rootScope", "$timeout",function($rootScope, $timeout) {
return {
scope: {
rmText: '=bhRmText'
},
replace: true,
template: '<div>' +
'<div class="pull-left forDrop"><input type="text" focus-on="focusMe" ng-class="myColonyList" class="effect1" placeholder="Add a colony" data-ng-model="newCategoryName" data-ng-trim="true" ng-keypress="pressEnter($event)"></div>' +
'<div class="pull-right"><img src="/images/greyplus.png" ng-class="{imageoverflow: imageOverflow}" ng-show="loadplus" data-ng-click="addCategory()" alt="add category"><img src="/images/loader.gif" ng-class="{imageoverflow: imageOverflow}" alt="" ng-show="loadgif" class="colonyloder"></div >' +
'</div>',
link: function(scope, element, attrs) {
scope.loadplus = true;
scope.pressEnter = function(keyEvent) {
if (keyEvent.which === 13)
scope.addCategory();
};
scope.resetNewCategoryName = function() {
if (scope.rmText) {
scope.newCategoryName = '';
}
};
}
};
}]);
How can i implement Auto complete here.
Any Suggestions
Thanks

Resources