AngularJS ng-click doesn't fire - angularjs

I generate some a Tags dynamically from a json file and add them like this to the page:
for(var i = 0; i < currentScene.hotpoints.hotpoint.length; i++)
{
var hotpoint = currentScene.hotpoints.hotpoint[i];
var pos = hotpoint.pos.split(";");
var x = parseFloat(pos[0]) * multiplierX;
var y = parseFloat(pos[1]) * multiplierY;
htmlstring += '<a href ng-controller="main" id="hp'+ hotpoint.ID + '" class="hotpoint animated infinite" style="top: ' + parseInt(y) + 'px; left: ' + parseInt(x) + 'px;" ng-click="enterScene(' +hotpoint.sceneID + ',' + hotpoint.ID +')"></a>';
}
$scope.hotpoints = $sce.trustAsHtml(htmlstring);
That works great. Now like you see I want to enable the click event for each element. So I use ng-click. But it doesn't get fired.
When I add this ng-click to an "static" element which is already on the site everything works.
What I have to care about that this works?
Thanks

Yes... $compile shall be used for this..
(function(){
"use strict";
angular.module("CompileDirective", [])
.directive('dynamicElement', ['$compile', function ($compile) {
return {
restrict: 'E',
scope: {
message: "="
},
replace: true,
link: function(scope, element, attrs) {
var template = $compile(scope.message)(scope);
element.replaceWith(template);
},
controller: ['$scope', function($scope) {
$scope.clickMe = function(){
alert("hi")
};
}]
}
}])
.controller("DemoController", ["$scope", function($scope){
$scope.htmlString = '<div><input type="button" ng-click="clickMe()" value="click me!"/> </div>';
}])
}());
With the following HTML:
<div ng-controller="DemoController">
<dynamic-element message='htmlString'></dynamic-element>
</div>
OR you may also go for injecting $compile in controller..
app.controller('AppController', function ($scope, $compile) {
var $el = $('<td contenteditable><input type="text" class="editBox" value=""/></td>' +
'<td contenteditable><input type="text" class="editBox" value=""/></td>' +
'<td>' +
'<span>' +
'<button id="createHost" class="btn btn-mini btn-success" data-ng-click="create()"><b>Create</b></button>' +
'</span>' +
'</td>').appendTo('#newTransaction');
$compile($el)($scope);
$scope.create = function(){
console.log('clicked')
}
})
And the easiest way..
$("#dynamicContent").html(
$compile(
"<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>"
)(scope)
);

Related

data-ng-click does not work when element html is set from custom directive

I have a custom directive to show contents when depending on if it is marked special:-
myApp.directive('actionSpace', function() {
//define the directive object
var directive = {};
//restrict = E, signifies that directive is Element directive
directive.restrict = 'E';
directive.link = function(scope, elem, attr) {
console.log(scope.typeEv);
if(attr.special == "1") {
elem.html("");
} else {
elem.replaceWith('<div class="event-list-control"><button class="event-action-btn" data-ng-click="whoWas()"> '+
'<i class="fas fa-edit"></i>' +
'</button><button class="event-action-btn" data-ng-click="tellMe()">' +
'<i class="fas fa-trash-alt"></i></button></div>');
}
}
return directive;
});
I can see in console directive's parent scope is available (its printing one of the variables), but data-ng-click does not work.
You need to compile the inserted html before inserting it into the element, please refer the below example. I use the $compile method to make the data-ng-click work!
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
});
app.directive('actionSpace', function($compile) {
//define the directive object
var directive = {};
//restrict = E, signifies that directive is Element directive
directive.restrict = 'E';
directive.link = function(scope, elem, attr) {
var html = ''
scope.tellMe = function(){console.log("telling");}
scope.whoWas = function(){console.log("this is");}
if(attr.special == "1") {
html = '';
} else {
html = '<div class="event-list-control"><button class="event-action-btn" data-ng-click="whoWas()">'+ '<i class="fas fa-edit"></i>' +
'Who Was</button><button class="event-action-btn" data-ng-click="tellMe()">' +
'<i class="fas fa-trash-alt"></i>Tell Me</button></div>';
}
var el = $compile(html)(scope);
elem.append(el);
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller='MyController' ng-app="myApp">
<action-space></action-space>
</div>
Instead of composing the template in the postLink function, do it in a template function:
myApp.directive('actionSpace', function() {
//define the directive object
var directive = {};
//restrict = E, signifies that directive is Element directive
directive.restrict = 'E';
directive.template = function(tElem, tAttrs) {
if (tAttrs.special == "1") {
return "";
} else {
return `
<div class="event-list-control">
<button class="event-action-btn" data-ng-click="whoWas()">
<i class="fas fa-edit"></i>
</button>
<button class="event-action-btn" data-ng-click="tellMe()">
<i class="fas fa-trash-alt"></i>
</button>
</div>
`;
};
}
return directive;
});
For more information, see AngularJS Comprehensive Directive API Reference - template

How to focus on a templates input field on replace in angularjs

node.js and angularjs noob here, so be gentle :).
I am using meanjs for my stack.
I have setup a click to edit function using template replace to add an input field, but I can't work out how to set focus to the input field once automatically when the template changes. My directive looks like this:
angular.module('core').directive("clickToEdit", function(){
var editorTemplate = '<div class="click-to-edit">' +
'<div data-ng-click="enableEditor()" data-ng-hide="view.editorEnabled">' +
'{{value}} ' +
'</div>' +
'<div data-ng-show="view.editorEnabled">' +
'<input data-ng-model="view.editableValue" data-ng-blur="disableEditor()" />' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
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();
};
}
};
});
You can use focus() function for element like in simple JS. One trick: I wrapped it in $timeout to let template render.
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body ng-app="plunker" ng-controller="MainCtrl">
<div click-to-edit="value"></div>
</body>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', ['$scope', function($scope) {
$scope.value="Click to edit";
}]).directive("clickToEdit", function(){
var editorTemplate = '<div class="click-to-edit">' +
'<div data-ng-click="enableEditor()" data-ng-hide="view.editorEnabled">' +
'{{value}} ' +
'</div>' +
'<div data-ng-show="view.editorEnabled">' +
'<input data-ng-model="view.editableValue" data-ng-blur="disableEditor()" />' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
controller: function($scope, $element, $timeout) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editorEnabled = true;
$scope.view.editableValue = $scope.value;
var input = $element.find('input');
$timeout(function() {
input[0].focus();
});
};
$scope.disableEditor = function() {
$scope.view.editorEnabled = false;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.disableEditor();
};
}
};
});
</script>
</html>

How to pass multiple attribute in AngularJS directive

How to pass multiple attribute to a directive.
How to pass value 12 of click-to-edit1 inside below div like
<div click-to-edit="location.state" click-to-edit1=12></div>
and should be accessible in directive controller.please help me out.
Code:
App HTML:
<div ng-controller="LocationFormCtrl">
<h2>Editors</h2>
<div class="field">
<strong>State:</strong>
<div click-to-edit="location.state"></div>
</div>
<h2>Values</h2>
<p><strong>State:</strong> {{location.state}}</p>
</div>
App directive:
app = angular.module("formDemo", []);
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
'{{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
},
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();
};
}
};
});
App controller:
app.controller("LocationFormCtrl", function($scope) {
$scope.location = {
state: "California",
};
});
Add new property inside directives scope:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.location = {
state: "California",
};
});
app.directive("clickToEdit", function() {
var editorTemplate = '<div class="click-to-edit">' +
'<div ng-hide="view.editorEnabled">' +
' {{value}} ' +
'<a ng-click="enableEditor()">Edit</a>' +
'</div>' +
'<div>{{value1}}</div>' +
'<div ng-show="view.editorEnabled">' +
'<input ng-model="view.editableValue">' +
'Save' +
' or ' +
'<a ng-click="disableEditor()">cancel</a>.' +
'</div>' +
'</div>';
return {
restrict: "A",
replace: true,
template: editorTemplate,
scope: {
value: "=clickToEdit",
value1: "=clickToEdit1"
},
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();
};
}
};
});
html:
<div class="field">
<strong>State:</strong>
<div click-to-edit="location.state" click-to-edit1="12"></div>
</div>
working example: http://plnkr.co/edit/e7oTtZNvLdu6w5dgFtfe?p=preview
you first tell the div that you want your directive on it.
<div click-to-edit value='location.state' value1='12'></div>
app.directive("clickToEdit", function() {
return {
restrict: "A",
scope : {
value : "=",
value1 : "="
},
link : function($scope) {
console.log("the first value, should be location.state value on the controller", $scope.value);
console.log("the second value, should be '12'", $scope.value);
}
}
it might seem more logic when you use the directive as a element.
but bottom line is that the diretive looks for attributes on the element, which you then can attach to the directive via the scope. '=' for two way binding, '#' for one way.

Angular directive check element?

I'm hooking up a $modal service for confirmation boxes in my app and made a directive that only works for ng-click. Well I also need it to work for ng-change so I did it like the following:
.directive('ngConfirmClick', ['$modal',
function($modal) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope:{
ngConfirmClick:"&",
item:"="
},
link: function(scope, element, attrs) {
element.bind('click', function() {
var message = attrs.ngConfirmMessage || "Are you sure ?";
if(element == 'select'){
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<div class="modal-footer"><button class="btn btn-success" ng-model="" ng-change="ok()">OK</button><button class="btn btn-warning" ng-change="cancel()">Cancel</button></div>';
} else {
var modalHtml = '<div class="modal-body">' + message + '</div>';
modalHtml += '<div class="modal-footer"><button class="btn btn-success" ng-click="ok()">OK</button><button class="btn btn-warning" ng-click="cancel()">Cancel</button></div>';
}
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngConfirmClick({item:scope.item});
}, function() {
});
});
}
}
}
]);
You can see I'm trying to check if the element is a 'select' element but I'm not sure how angular's link method/function reads the element. Can I check it with a string like how I did it? (It doesn't work when I try this btw).
How can I check if the element I'm attaching my directive to is a select?
Angular's jqLite is a subset of jQuery and that is the element parameter passed into the link function (unless you load the full jQuery library, then it will be a jQuery object). As described in this post using element.prop('tagName') will return the element type which is a method included in the jqLite library.
So I got confused and the if statement should of been at the element.bind not at the var modalHtml...
Here's the updated code for me to get this to work with both ng-change and ng-click. I just added bind on click and bind on change with an if statement to check the element.context.tagName was select or not
directive('ngConfirmClick', ['$modal',
function($modal) {
var ModalInstanceCtrl = function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close();
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
};
return {
restrict: 'A',
scope:{
ngConfirmClick:"&",
item:"="
},
link: function(scope, element, attrs) {
console.log(element.context.tagName);
if(element.context.tagName == 'SELECT'){
element.bind('change', function() {
var message = attrs.ngConfirmMessage || "Are you sure ?";
var modalHtml = '<div class="modal-header"><h4 id="title-color" class="modal-title"><i class="fa fa-exclamation"></i> Please Confirm</h4></div><div class="modal-body">' + message + '</div>';
modalHtml += '<div class="modal-footer"><button class="btn btn-primary" ng-click="ok()">OK</button><button class="btn btn-warning" ng-click="cancel()">Cancel</button></div>';
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngConfirmClick({item:scope.item});
}, function() {
});
});
} else {
element.bind('click', function() {
var message = attrs.ngConfirmMessage || "Are you sure ?";
var modalHtml = '<div class="modal-header"><h4 id="title-color" class="modal-title"><i class="fa fa-exclamation"></i> Please Confirm</h4></div><div class="modal-body">' + message + '</div>';
modalHtml += '<div class="modal-footer"><button class="btn btn-primary" ng-click="ok()">OK</button><button class="btn btn-warning" ng-click="cancel()">Cancel</button></div>';
var modalInstance = $modal.open({
template: modalHtml,
controller: ModalInstanceCtrl
});
modalInstance.result.then(function() {
scope.ngConfirmClick({item:scope.item});
}, function() {
});
});
}
}
}
}
]);

How to use compile function in AngularJS directive to ng-repeat elements from a service

Could someone, please, show me what I need to do to render in a directive new elements stored in an array in a service. In the example below, an alert from the service shows that each new element is added to the elements array, but how to make the directive show these new elements on the page?
I tried to read everything about compile function in a directive, but could not understand how to make my example work.
Here is a jsfiddle. All I need is to render new messages inside the directive after they are added to the message array in the service.
The Chat service is injected into directive as a chat variable, and in the directive template I want to repeat every message from the service:
'<ul>' +
'<li ng-repeat="message in chat.messages">' +
'<strong>{{message.name}}</strong> {{message.text}}' +
'</li>' +
'</ul>'
Sample code is on jsfiddle and below:
HTML:
<div ng-app="myApp">
<my-simple-chat title="AngularJS Chat"></my-simple-chat>
</div>
JavaScript:
angular.module('myApp', [])
.factory('Chat', function () {
var messages = [];
function sendMessage(name, text) {
messages.push(
{
name: name,
text: text
});
alert("Sending message from factory: " + name + ", " + text + " : " + messages.length);
};
return {
messages: messages,
sendMessage: sendMessage
};
})
.directive('mySimpleChat', ['Chat', function (chat) {
var tmpl = '<div><h2>{{title}}</h2>' +
'<input type="text" ng-model="name" placeholder="Type your name"/>' +
'<hr />' +
'<form ng-submit="sendMessage()">' +
'<input type="text" ng-model="text" required placeholder="Type a new message..."/>' +
'<input type="submit" id="submit" value="Send"/>' +
'</form>' +
'<ul>' +
'<li ng-repeat="message in chat.messages">' +
'<strong>{{message.name}}</strong> {{message.text}}' +
'</li>' +
'</ul>' +
'<div>';
return {
restrict: 'E',
replace: true,
scope: { title: '#title' },
template: tmpl,
controller: ['$scope', '$element', '$attrs', '$transclude',
function ($scope, $element, $attrs, $transclude) {
$scope.name = 'MyName';
$scope.text = '';
$scope.sendMessage = function () {
chat.sendMessage($scope.name, $scope.text);
$scope.text = '';
};
}],
};
}]);
You are using ngRepeat on chat.messages but never assigning chat to be part of $scope.
controller: ['$scope', '$element', '$attrs', '$transclude',
function ($scope, $element, $attrs, $transclude) {
$scope.name = 'MyName';
$scope.text = '';
$scope.chat = chat; //<--this so you can use ngRepeat
$scope.sendMessage = function () {
chat.sendMessage($scope.name, $scope.text);
$scope.text = '';
};
}],
updated fiddle.
Looks like writing a question on SO is 90% way to its answer... All I needed to do was to add a link function to the directive:
link: function ($scope, element, attributes) {
$scope.messages = chat.messages;
}
and remove chat. from ng.repeat:
'<ul>' +
'<li ng-repeat="message in messages">' +
'<strong>{{message.name}}</strong> {{message.text}}' +
'</li>' +
'</ul>'
I am not sure that this is the right way to do the task, but it just works. In docs I have read that I have to use compile function if I use ng-repeat in a template, so I am still confused somewhat.
An updated jsfiddle.
You can create a bridge like this:
<li ng-repeat="message in messages()">
$scope.messages = function () {
return chat.messages;
}

Resources