ng-click in directive not calling function defined in controller scope - javascript

Everything seems to render properly but my ng-click="check($event)" is not firing when I click on one of the four buttons. I even added an alert to the check($event) function but it's not coming up. Heres the codepen http://codepen.io/theMugician/pen/MKOzzV?editors=101
simonApp.directive('pads', ['lightIt', 'soundIt', function(lightIt,soundIt) {
var template = '';
template += '<button ng-click="check($event)" id=' + '{{index}} ' + 'class="pad pad-' + '{{color}}' + '">';
template += '<div class="ripple js-ripple">';
template += '<span class="ripple-circle ' + 'ripple-' + '{{color}}' + '"></span></div>';
template += '</button>';
return {
restrict: 'E',
replace: true,
scope: {
color: '#color',
index: '#index'
},
link: function(scope, element, attrs) {
scope.createRipple = function(e){
lightIt.createRipple(e, this);
}
//scope.$emit('index', scope.index);
/*
var index = parseInt(scope.index);
scope.playSound = function() {
soundIt(1);
}*/
console.log("scope.index: " + scope.index);
//console.log("scope.playSound: " + scope.playSound);
element.on('click', scope.createRipple);
//element.on('animationend', scope.endRipple);
},
template: template
};
}]);
Here is the check() function in the controller
$scope.check = function(e) {
alert("check works");
var id = e.currentTarget.id;
if($scope.init){
if(sequence[turn] === id){
turn++;
if(turn === sequence.length){
$scope.step++;
setTimeout(function(){
newRound();
}, 1000);
}
}else{
if($scope.isStrict){
setTimeout(function(){
alert('game over');
$scope.reset();
}, 300);
}else{
$scope.displaySeq(sequence);
}
}
}
}

You cannot call controller method from directive like that.
In case you need scope isolation you have to use expression binding ( '&' scope parameters) or turn off scope isolation all together ( remove scope key from directive definition )
See excellent video from John Lindquist for example of using scope expression binding.
var app = angular.module('app', [])
app.directive("pads", function() {
return {
restrict: "E",
scope: {
check: "&"
},
template: '<div class="button" ng-click="check({message: value})">click</div></div>',
link: function(scope) {
scope.value = 'message to controller'
}
};
});
app.controller('myCtrl', function() {
this.doCheck = function(message) {
alert(message) // alerts "message to controller"
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='myCtrl as ctrl'>
<pads check="ctrl.doCheck(message)"></pads>
</div>

Related

jQuery doesn't select the element with dynamic ID, how to do that?

I am having a problem when I pass the ID through a directive. I can't obtain the element using jQuery inside the Link function, and the element is using the correct dynamic ID coming as parameter:
The Directive:
(function(angular) {
var app = angular.module('pi.core');
app.directive('piSearch', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
idelement: '#'
},
link: function(scope, element, attrs, controller, transcludeFn) {
var idelement = scope.idelement;
console.log('idElement: ' + idelement);
console.log($('#' + idelement + ' .typeahead'));
},
template: '<div id="{{idelement}}"></div>'
};
});
})(angular);
var myApp = angular.module('piCore', []);
myApp.directive("piSearch", function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
idelement: '#'
},
link: function(scope, element, attrs, controller, transcludeFn) {
var idelement = scope.idelement;
scope.elementSelected = $('#' + idelement + ' .typeahead');
console.log('idElement: ' + idelement);
console.log($('#' + idelement + ' .typeahead'));
},
template: '<div id="{{idelement}}"></div>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body ng-app="piCore">
<pi-search></pi-search>
{{$scope.elementSelected}}
</body>
Any hints? Thanks in advance for your help!
I'll refactor your link function, remember angular has their own life cycle, and you need to make sure that your template compiles when your model has a value, wrap your logic in a watch
app.directive('piSearch', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
idelement: '#'
},
link: function(scope, element, attrs, controller, transcludeFn) {
var idelement = scope.idelement;
scope.$watch('idelement',function(){
scope.elementSelected = $('#' + idelement + ' .typeahead');
console.log('idElement: ' + idelement);
console.log($('#' + idelement + ' .typeahead'));
});
},
template: '<div id="{{idelement}}"></div>'
};
});
try to use angular.element("#"+ idelement);
and also make sure this template: '<div id="{{idelement}}"></div>' is not generated multiple times
One thing you must know regarding dynamic ID's and elements in global:
1) First you must generate your dynamic HTML inside javascript
2) Print it, append or just insert somwhere in the DOM
3) Now to can access to that new element you must use document to read that new element like this:
document.getElementById('new-generic-id');
or
$(document).on('click touchstart select', '#new-generic-id', function(){});
I hope you understand.

scope variable isn't reflected in directive

I'm trying to fill the body attribute, so my module will print it as it's body part, yet I have no idea why it's not working, even I tried $scope.$apply() in several places.
var app = angular.module('ClientLogger', ['ngRoute'])
app.controller('global', function($scope, $compile) {
$scope.window = window
$scope.sampletext = "sampleText";
$scope.showModal = false;
$scope.toggleModal = function(text) {
$scope.text = text;
$scope.showModal = !$scope.showModal;
};
});
app.directive('modal', function() {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body" ng-transclude> {{ body }} </div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.body = attrs.body;
scope.$watch(attrs.visible, function(value) {
if (value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
});
<body ng-app='ClientLogger' ng-controller='global'>
<a class="btn btn-default" ng-click="toggleModal(sampletext)"> sample </a>
<modal title="Message Details" body="{{text}}" visible="showModal"></modal>
</body>
As you can see, after i click the link, it will change the variable $scope.text and it will reflect to the modal. But I can't manage to do it. Since I#m very new about this Angular, I still have troubles about understanding its mechanics, so any specific details will be really good for me.
Any recommendations?
When you retrieve your attrs.body, you are retrieving undefined data.
You can use a Factory to share your data. You have to know that all angular services are singletons, so there is only one instance of a given service.
Thanks to this, you can easily share your data between your controller and your directive.
So when you will trigger an action in your Controller, you will set your data into your factory, then you can retrieve your data into your directive.
Controller
(function(){
function Controller($scope, Service) {
$scope.myText = 'sample';
$scope.showModal = false;
$scope.toggleModal = function() {
//Set the myText value by using our Service.set() method
Service.set($scope.myText);
$scope.showModal = !$scope.showModal;
};
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Service
(function(){
function Service(){
var data;
function set(value){
data = value;
}
function get(){
return data;
}
return {
get: get,
set: set
};
}
angular
.module('app')
.factory('Service', Service);
})();
Directive
(function(){
function modal(Service) {
return {
template: '<div class="modal fade">' +
'<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
'<h4 class="modal-title">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body"> {{ body }} </div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function (scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value) {
value
//Retrieve your data by using Service.get() method, and show our modal
? ( scope.body = Service.get(), $(element).modal('show') )
: $(element).modal('hide')
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
}
angular
.module('app')
.directive('modal', modal);
})();
Then, you can call your directive into your HTML.
HTML
<body ng-app='app' ng-controller="ctrl">
<input type="text" ng-model="myText">
<a class="btn btn-default" ng-click="toggleModal()"> sample </a>
<modal title="Message Details" visible="showModal"></modal>
</body>
You can see the Working Plunker

angularjs - $destroy isn't called on custom compiled html

I made a directive that compiles a 'div' with an ng-include and ng-controller, and appends it to the element. It works fine, but for some reason the "$destroy" event isn't called on the controller's scope when it is destroyed.
Is what I'm doing correct?
This is the html:
<my-Directive tr-model="someBindedObject"></my-Directive>
And this is the JS:
angular.module('myModule').
directive('myDirective', function ($compile, controllerMapper) {
function compileTemplate(scope, element, attrs) {
var controller = controllerMapper.getController(scope.model);
innerHtml = '<div ng-include src="' + "'" + controller.templateUrl + "'" + '" ng-controller="' + controller.controllerName + '" ></div>';
var innerElement = $compile(innerHtml)(scope);
element.empty();
element.append(innerElement);
};
return {
scope: {
model: "=trModel"
},
link: compileTemplate
}
}).
controller('myController', function($scope){
$scope.$on('$destroy', function () {
//this is never called
});
});

Is the way of creating a directive scope the same in versions 1.3 and 2 of AngularJS?

I have the following directive that my application is using. I was under the impression that my application was working fine with AngularJS 1.3 but after a lot of changes including a move to the latest version, the removal of jQuery, and also the use of controller as then now this directive is giving me errors:
app.directive('pagedownAdmin', 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, ngModel) {
var editorUniqueId;
if (attrs.id == null) {
editorUniqueId = nextId++;
} else {
editorUniqueId = attrs.id;
}
var newElement = $compile(
'<div>' +
'<div class="wmd-panel">' +
'<div data-ng-hide="modal.wmdPreview == true" id="wmd-button-bar-' + editorUniqueId + '"></div>' +
'<textarea 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">test div</div>' +
'</div>')(scope);
iElement.html(newElement);
var help = function () {
alert("There is no help");
}
var editor = new Markdown.Editor(converter, "-" + editorUniqueId, {
handler: help
});
var $wmdInput = iElement.find('#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();
}
}
});
Can someone explain to me what the following is doing:
scope: {
modal: '=modal'
},
and also the
)(scope);
Here is how I am calling this directive:
<textarea id="modal-data-text"
class="pagedown-admin wmd-preview-46"
data-modal="modal"
data-pagedown-admin
ng-model="home.modal.data.text"
ng-required="true"></textarea>
If anyone can see anything that may not work in 2 then I would much appreciate some help. In particular it seems that the following code returns null:
var $wmdInput = iElement.find('#wmd-input-' + editorUniqueId);
You dropped jQuery, so your code now relies on jQLite. Functions of element objects support less functionality when using jqLite. See the full details in the doc:
https://docs.angularjs.org/api/ng/function/angular.element
var $wmdInput = iElement.find('#wmd-input-' + editorUniqueId);
Under jqLite, the find function only support searching by tag names, ids will not work. You can use the following tricks from ( AngularJS: How to .find using jqLite? )
// find('#id')
angular.element(document.querySelector('#wmd-input-' + editorUniqueId))
$compile is a service that will compile a template and link it to a scope.
https://docs.angularjs.org/api/ng/service/$compile
scope: {
modal: '=modal'
}
allows you to define a isolated scope for the directive with some bindings to the scope in which the directive is declared. '=' is used for two-way data bindings. Other options are '# and &' for strings and functions.
https://docs.angularjs.org/guide/directive

AngularJS input ng-model not updating

I am trying to create a simple pagination directive with an isolated scope. For some reason when I manually change the value it gets a bit finnicky. Here is my problem:
When I page forward and backward, it works great. Awesome
When I enter a page into the field it works. Great
However, if I enter a page into the field and then try to go forward and backward, the ng-model seems to break after I enter a page into the field. I had it working when I did not isolate my scope but I am confused as to why it would break it. Here is my code:
HTML:
<paginate go-to-page="goToPage(page)" total-pages="results.hits.pages" total-hits="results.hits.total"></paginate>
Directive:
'use strict';
angular.module('facet.directives')
.directive('paginate', function(){
return {
restrict: 'E',
template: '<div class="pull-right" ng-if="(totalPages !== undefined) && (totalPages > 0)">'+
'<span class="left-caret hoverable" ng-click="changePage(current-1)" ng-show="current > 1"></span> Page'+
' <input type="number" ng-model="current" class="pagination-input" ng-keypress="enterPage($event)"/> of'+
' {{totalPages}} '+
'<span class="right-caret hoverable" ng-click="changePage(current+1)" ng-show="current < totalPages"></span>'+
'</div>',
scope: {
goToPage: '&',
totalPages: '=',
totalHits: '='
},
link: function(scope) {
scope.current = 1;
scope.changePage = function(page) {
scope.current = page;
window.scrollTo(0,0);
scope.goToPage({page:page});
};
scope.enterPage = function(event) {
if(event.keyCode == 13) {
scope.changePage(scope.current);
}
}
}
}
});
What am I doing wrong?
Beware of ng-if - it creates a new scope. If you change it to just ng-show, your example would work fine. If you do want to use ng-if, create a object to store the scope variable current. Maybe something like scope.state.current?
scope.state = {
current: 1
};
To avoid confusion like this, I always keep my bindings as something.something and never just something.
Edit: Good explanation here - http://egghead.io/lessons/angularjs-the-dot
Please always try to use model rather than using primitive types while using the ng-model because of the javascript's prototypical hierarchies.
angular.module('facet.directives').directive('paginate', function () {
return {
restrict: 'E',
replace: true,
template: '<div class="pull-right discovery-pagination" ng-if="(totalPages !== undefined) && (totalPages > 0)">' +
'<span class="left-caret hoverable" ng-click="changePage(current-1)" ng-show="current > 1"></span> Page' +
' <input type="number" ng-model="current.paging" class="pagination-input" ng-keypress="enterPage($event)"/> of' +
' {{totalPages}} ' +
'<span class="right-caret hoverable" ng-click="changePage(current+1)" ng-show="current < totalPages"></span>' +
'</div>',
scope: {
goToPage: '&',
totalPages: '=',
totalHits: '='
},
link: function(scope) {
scope.current = {paging:1};
scope.changePage = function(page) {
scope.current.paging = page;
window.scrollTo(0, 0);
scope.goToPage({ page: page });
};
scope.enterPage = function(event) {
if (event.keyCode == 13) {
scope.changePage(scope.current.paging);
}
};
}
};
});
Hope this will solve your problem :)
For detail about this, please go through Understanding-Scopes

Categories

Resources