AngularJS: ng-click not working in custom input directive - angularjs

I'm new in AngularJS, currently I'm working on custom input directive with a label.
I refer to one article in stack overflow but encountered problem.
The running code is on fiddle:
http://jsfiddle.net/luneyq/bpN9b/18/
Core code is as below:
<!doctype html>
<html>
<head>
<script src="../common/angular.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MainController">
<my-input type="number" name="valueNumber1" ng-model="valueNumber1" label="Age" ng-change="change()" ng-click="click()"></my-input>
<div id="result">a</div>
</div>
</div>
<script>
var app = angular.module("myApp", []);
app.controller('MainController', function($scope, $window){
$scope.valueNumber1 = 10;
$scope.change = function() {
document.getElementById("result").innerHTML = 'change:' + $scope.valueNumber1;
};
$scope.click = function() {
document.getElementById("result").innerHTML = 'click:' + $scope.valueNumber1;
};
});
app.directive('myInput', function() {
return {
require: '^ngModel',
restrict: 'EA',
scope: {
ngModel: '=',
name: '#name',
label: '#label'
},
replace: true,
transclude: true,
//priority: 10,
template: '<div>' +
'<label for="{{ name }}">{{label}}</label>' +
'<input id="{{ name }}" ng-model="ngModel" />' +
'</div>',
compile: function(tElement, tAttrs, transclude) {
var tInput = tElement.find('input');
// Move the attributed given to 'custom-input' to the real input field
angular.forEach(tAttrs, function(value, key) {
if (key.charAt(0) == '$' || key == "class")
return;
tInput.attr(key, value);
tInput.parent().removeAttr(key);
});
tElement.removeAttr('ng-model');
return;
}
};
})
</script>
</body>
</html>
My problems are:
1. ng-click and ng-change not working on the input
2. ng-model="ngModel" on input element, why ngModel is used here? if I change ngModel to aaa, the initial value for the input disappeared
3. attributes copy in compile function removed the dash(-) signal, ng-click is copied as ngClick.
I'm not sure is this the cause of this problem.
Can anyone help on this problem?

Your problem was click and change methods not in your isolate scope - BUT you are not making use of Angular's two way binding here - instead simply use ng-bind on your result div - here's a fiddle http://jsfiddle.net/bpN9b/20/
var app = angular.module("myApp", []);
app.controller('MainController', function($scope, $window){
$scope.valueNumber1 = 10;
});
app.directive('myInput', function() {
return {
require: '^ngModel',
restrict: 'EA',
scope: {
ngModel: '=',
name: '#name',
label: '#label'
},
replace: true,
priority: 10,
template: '<div class="cbay-input-div">' +
'<label for="{{ name }}">{{label}}</label>' +
'<input id="{{ name }}" ng-model="ngModel" />' +
'</div>',
compile: function(tElement, tAttrs, transclude) {
console.log(tAttrs);
var tInput = tElement.find('input');
// Move the attributed given to 'custom-input' to the real input field
angular.forEach(tAttrs, function(value, key) {
//console.log(key + " = " + value);
if (key.charAt(0) == '$' || key == "class")
return;
tInput.attr(key, value);
tInput.parent().removeAttr(key);
});
tElement.removeAttr('ng-model');
return;
}
};
});
here's the template
<div ng-app="myApp">
<div ng-controller="MainController">
<my-input type="number" name="valueNumber1" ng-model="valueNumber1" label="Age" ng-change="change(this)" ng-click="click(this)"></my-input>
<div id="result" ng-bind="valueNumber1">a</div>
</div>
</div>

Related

directive reacting to attribute change

I have a directive, with an attribute :
html :
<my-directive id="test" myattr="50"></my-directive>
js :
myApp.directive('myDirective', function() {
var link = function(scope, element, attrs) {
scope.$watch('myattr', function(value) {
element.attr('myattr', value);
});
scope.change = function() {
// some code
};
};
return {
restrict: 'E',
template: '<input type="text" ng-change="change()" ng-model="myattr"/>',
scope: {myattr: '='},
link: link
};
});
My goal would be to keep myattr and the value of the input equal. With element.attr('myattr', value) I can force myattr to have the correct value, but how am I supposed to update the input when myattr changes?
For example, in this jsfiddle, when clicking on the button, I try to do :
$('#test').attr('myattr', Math.random() * 100);
But I can't find a way to 'catch' the change from within the directive.
I would like some help modifying the jsfiddle so that :
the function change is called after the jquery call.
the value of the input is always equal to myattr
You need to store the value of myattr as a property on a scope not as a value on an attribute.
Your directive is correct you need to also attach a controller.
var myApp = angular.module('myApp', []);
myApp.controller('MainController', function ($scope) {
$scope.calculate = function () {
// your logic here
alert($scope.val);
}
});
myApp.directive('myDirective', function() {
var link = function(scope, element, attrs) {
scope.change = function() {
console.log("change " + scope.myattr);
};
};
return {
restrict: 'E',
template: '<input type="text" ng-change="change()" ng-model="myattr"/>',
scope: {
myattr: '='
},
link: link
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MainController">
My Value: {{val}} <br/>
<button type="button" ng-click="calculate()">ok</button>
<my-directive id="test" myattr="val"></my-directive>
</div>
</div>

$ViewValue is not being used when Element type directive

I created this simple directive that uses a formatter and it is not updating the input when the directive is used as an element or when it is an attribute of a div.
JSBIN:
http://jsbin.com/rifaxuvihu/edit?html,js,output
angular.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.foo = '123';
console.log('------ MODEL CHANGED ($scope.foo = "123") ------');
$scope.changeModel = function() {
$scope.foo = 'abc';
console.log('------ MODEL CHANGED ($scope.foo = "abc") ------');
};
})
.directive('myDirective', function() {
return {
restrict: 'AE',
transclude: true,
require: 'ngModel',
template: function($element, $attrs, $ctrls) {
var inp, lbl = 'label';
if ($attrs.type == 'html'){
inp = [
'<summernote class="form-control" ng-model="ngModel" ',
' name="{{name}}" placeholder={{placeholder}} height="{{height}}"></summernote>'
];
lbl = 'div';
} else
inp = [
'<input class="form-control" ng-model="ngModel" ',
' type="{{type}}" />'
];
var ret = [
'<div class="advform-input form-group inp-{{type}} {{cols}}">',
' <' + lbl + ' class="advform-lbl">',
inp.join('\n'),
' <div class="helper" ng-if="type != \'html\'">{{ placeholder }}</div>',
' <div class="desc">{{ desc }}</div>',
' </' + lbl + '>',
'</div>'
].join('\n');
return ret;
},
scope: {
placeholder: '#',
ngModel: '=?',
type: '#',
name: '#',
height: '#'
},
link: function ($scope, $element, $attrs, $ngModel) {
console.log('ooi');
if (true){
$ngModel.$formatters.unshift(function(modelVal) {
console.log('-- Formatter --', JSON.stringify({
modelVal:modelVal,
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2));
return 1 + '-) ' +modelVal;
});
// same as $watch('foo')
$scope.$watch(function() {
return $ngModel.$viewValue;
}, function(newVal) {
console.log('-- $watch "foo" --', JSON.stringify({
newVal:newVal,
ngModel: {
viewVal: $ngModel.$viewValue,
modelVal: $ngModel.$modelValue
}
}, null, 2));
});
}
}
};
})
;
the html:
<!DOCTYPE html>
<html ng-app="myApp" ng-controller="myCtrl">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.min.js"></script>
</head>
<body>
<form name="form">
<my-directive type="text" name="foo" ng-model="foo"></my-directive>
<input my-directive type="text" name="foo" ng-model="foo" />
<div my-directive type="text" name="foo" ng-model="foo"></div>
</form>
<button ng-click="changeModel()">Change Model</button>
<p>$scope.foo = {{foo}}</p>
<p>Valid: {{!form.foo.$error.test}}</p>
</body>
</html>
The problem is that the directive creates a new scope and the viewValue of the ngModel of the directive is different from the one created within the template.
To fix it, you can change the ng-model of the template to $parent.ngModel.
So the format will work as expected.

ng-switch and manually $compile

I tried to make recursive directive like here
Angularjs: understanding a recursive directive
ng-switch options are all commented, ng-if works fine.
I don't know what is a problem.
Just inspect output of ng-switch
var module = angular.module('myapp', []);
module.controller("TreeCtrl", function($scope) {
$scope.controls = [
{
"label":"Checkbox",
"type":"checkbox"
},
{
"label":"input",
"type":"input"
},
{
"label":"Some group",
"type":"Group",
"controls":[
{
"label":"subcontrol",
"type":"input"
}
]
}
];
});
module.directive("tree", function($compile) {
return {
restrict: "E",
scope: {controls: '='},
template:
'<div ng-switch="control.type">' +
'<label>{{control.label}}</label>'+
'<div ng-switch-when="input"><input type="text" name="ctrl1"></input></div>'+
'<div ng-switch-when="checkbox"><input type="checkbox" "name="ctr2"></input></div>'+
'<div ng-repeat="subcontrol in control.controls">' +
'<tree control="subcontrol"></tree>' +
'</div>' +
'</div>',
compile: function(tElement, tAttr, transclude) {
//We are removing the contents/innerHTML from the element we are going to be applying the
//directive to and saving it to adding it below to the $compile call as the template
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents, transclude);
}
compiledContents(scope, function(clone, scope) {
iElement.append(clone);
});
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp">
<div ng-controller="TreeCtrl">
<div ng-repeat='control in controls'>
<tree control="control"></tree>
</div>
</div>
</div>

AngularJS How to parse html into directive template

How i can parse html in directive template???
app.directive('user', function(){
return {
restrict: 'E',
scope: false,
template: '<div class="clearfix buttons-container" ng-bind-html-unsafe="current_text"></div>'
}
});
//in controller
$scope.current_text = 'Hello <strong>'+current_username+'</strong> !!!'
use $sce.trustAsHtml. this method will create trusted HTML object for ng-bind-html directive.
$sce.trustAsHtml Docs
See below example
var app = angular.module('app', []);
app.controller('ctrl', function($scope, $sce) {
var current_username = "User";
var html = 'Hello <strong>' + current_username + '</strong> !!!';
$scope.current_text = $sce.trustAsHtml(html);
});
app.directive('user', function() {
return {
restrict: 'E',
scope: false,
template: '<div class="clearfix buttons-container" ng-bind-html="current_text"></div>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl">
<user></user>
</div>
</div>
you can do this using isolate-scope
in this way.
app.directive('user', function(){
return {
restrict: 'E',
scope: {
current_text : '#current_text'
},
template: '<div class="clearfix buttons-container" ng-bind-html-unsafe="current_text"></div>'
}
});
//in controller
$scope.currenText = 'Hello <strong>'+current_username+'</strong> !!!'
<div current_text="currenText" > </div> // your directive included here

how to call parent method from directive?

I have directive like this
.directive('myModal', function() {
return {
restrict: 'A',
replace: true,
scope: {
myModalId: '#'
},
template: '<div id="{{myModalId}}" class="modal">' +
'<div ng-click="parentMethod()" class="modal-dialog">' +
'...' +
'</div>' +
'</div>'
}
});
js code
function ParentController($scope) {
$scope.parentMethod = function() {
alert('clicked');
}
}
There are several ways you can achieve this. I would do it using attribute binding which allows you to execute an expression in the context of the parent scope. This is how you can use it:
<div my-modal my-modal-id="12312" on-click="parentMethod()"></div>
Then in directive you define scope like this:
scope: {
myModalId: '#',
onClick: '&'
}
and in directive template:
<div ng-click="onClick()" class="modal-dialog">
Demo: http://plnkr.co/edit/UDnJGRVqXqbCGSFEAMMA?p=preview
Another way (not recommended), you can directly refer the parent scope from isolated directive scope:
<div ng-click="$parent.parentMethod()" class="modal-dialog">
<body ng-app="myApp">
<div ng-controller="myCtrl">
<direct movied="movie" call-home="callFromDirect"></direct>
</div>
<script>
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope, $rootScope) {
$scope.callFromDirect = function (param) {
console.log('got call to direct ' + param);
}
});
myApp.directive("direct", function () {
return {
restrict: 'E',
scope: {
callHome: '='
},
template: "<input type=\"button\" value=\"click\" ng-click=\"callHome('movie')\" />"
}
});
</script>

Resources