How to use custom directive on Input fields in Angular 1.x? - javascript

I'm trying to modify the input element using a custom AngularJS directive. Basically I want to replace any <input type="country"> fields with a country drop-down.
But the directive doesn't seem to work with input fields. If I change it to any other tag, it works?
Here is the code:
angular.module('plunker', [])
.controller('MainCtrl', function ($scope) {
$scope.name = 'World';
});
angular.module('plunker')
.directive('input', function() {
return {
restrict: 'E',
scope: {ngModel: '=?'},
link: function(scope, elem, attr) {
if(attr.type === 'country') {
elem.html('html code for select');
alert(elem);
}
}
};
});
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
Name: <input type="country" ng-model="name"/> <br/>
</body>
</html>
Can someone please explain and suggest a workaround?
P.S. I've also tried doing this in the directive, but it doesn't work either!
replace: true,
template:'<div>hello</div>'
P.S. I know I can use a ng-country or some other custom tag but I want to change input tag only because I want learn why this is happening or possibly find out what I'm doing wrong?

Latest Update:
Your code is just setting the html on the element, instead of replacing it. You would want to use replaceWith instead like this:
var app = angular.module("TestApp",[]);
app.controller("TestController", function($scope){
$scope.message = "Input Directive Test"
});
app.directive("input", function() {
return {
restrict: "E",
link: function(scope, elem, attr) {
if(attr.type === "country") {
var select = angular.element("<select><option>USA</option></select>");
elem.replaceWith(select);
}
}
}
});
And here's the JSBin: https://jsbin.com/juxici/4/edit?html,js,output
Initial Version of my answer:
Here's an example that works without any issues when 'replace' and 'template' are used. I'm not checking for type and such, but you could do that in the linker code.
JSBin: https://jsbin.com/juxici/2/edit?html,js,output
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
</head>
<body ng-app="TestApp">
<div ng-controller="TestController">
<h2>{{message}}</h2>
<input type="country" ng-model="name"/>
</div>
</body>
</html>
Javascript:
var app = angular.module("TestApp",[]);
app.controller("TestController", function($scope){
$scope.message = "Input Directive Test"
});
app.directive("input", function() {
return {
restrict: "E",
replace: true,
template:"<select><option>USA</option></select>"
}
});

Related

angular directive not working

Angular directive is not working for the following case. In the my following angular application I have two kinds of item I wish to display which are stored in controller.
To display them I have created the directives for both cases, and iterating over list with ng-repeat, but the items are not being rendered.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<style media="screen">
.selected {
background: #cdcdcd;
}
</style>
</head>
<body ng-app="myApp">
<div ng-controller="ListController as listctrl">
<div class="" ng-repeat="item in listctrl.items">
<itemtype1 ng-if="item.type_ === 'type1'" val="item.val"></itemtype1>
<itemtype2 ng-if="item.type_ === 'type2'" val="item.val"></itemtype2>
</div>
</div>
<script type="text/javascript">
angular
.module('myApp',[])
.controller('ListController', function($scope) {
var listctrl = this;
listctrl.items = [];
listctrl.items.push({'val':'A', 'type_': 'type1'});
listctrl.items.push({'val':'B', 'type_': 'type2'});
listctrl.items.push({'val':'C', 'type_': 'type1'});
listctrl.items.push({'val':'D', 'type_': 'type2'});
})
.directive('itemtype1', function() {
return {
template: '<strong>{{$scope.val}}</strong>',
restrict: 'E',
scope: {
val: '='
},
link: function postLink(scope, element, attrs) {
}
};
})
.directive('itemtype2', function() {
return {
template: '<i>{{$scope.val}}</i>',
restrict: 'E',
scope: {
val: '='
},
link: function postLink(scope, element, attrs) {
}
};
});
</script>
</body>
</html>
My first guess, just glancing through it, is that you should change the following:
template: '<strong>{{$scope.val}}</strong>'
to this:
template: '<strong>{{val}}</strong>'

using jQuery function in AngularJS

codepen this is the working code
I have a working code for changing the status of the network.
But I used jQuery to achieve it and now I am using angular app for the same thing.
Could you please help me.
Below is the Angular Code I have tried from already existing plunker:
Plunker
HTML:
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
Hello {{name}}! {{online}}
</body>
</html>
js:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.run(function($window, $rootScope) {
$rootScope.online = navigator.onLine;
$window.addEventListener("offline", function () {
$rootScope.$apply(function() {
$rootScope.online = false;
});
}, false);
$window.addEventListener("online", function () {
$rootScope.$apply(function() {
$rootScope.online = true;
});
}, false);
});
Use ng-class like this:
<div id="connection" ng-class="{'connected': online == true}">Online<div></div></div>
Here is your corrected Plunker
Hope this is what you wanted.
http://plnkr.co/edit/C2oJAYtEbb2lMixY2rVa?p=preview
<div id="connection" ng-class="{'connected': online, 'disconnected': !online}">
<span ng-if="$root.online">Online<div></div></span>
<span ng-if="!$root.online">Offline<div></div></span>
</div>
Note that with this approach, the initial state of 'connecting...' is not handled.

Bind dynamic loaded content to Angular

I want to bind some dynamic content, loaded by jQuery, to Angular but i couldn't get it work.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/angular/angular.min.js"></script>
<script type="text/javascript">
var module = angular.module('ctrl', []);
module.directive('helpMe', function() {
return {
restrict: 'E',
template: '<div><button ng-click="clicker()">Click 1</button><button ng-click="create()">Create</button><div ng-hide="true">FooBar</div></div>',
controller: ['$scope', function($scope) {
$scope.clicker = function () {
console.log('Clicked...');
};
$scope.create = function () {
$('#foo').contents().clone().appendTo('#bar');
}
}]
}
});
</script>
</head>
<body ng-app="ctrl">
<div id="foo">
<help-me></help-me>
</div>
<div id="bar">
</div>
</body>
If i click the "Click 1" button, the console logs it. If i click the "create" button, a new set of buttons appears. But the new buttons don't work. I could not find out how to get this work. Any ideas?
You need to call $compile on the HTML string before inserting it into the DOM so that angular gets a chance to perform the binding.
$scope.create = function() {
var clone = $('#foo').contents().clone();
$compile(clone)($scope);
$('#bar').append(clone);
}
check this plnkr
Try this it will work:-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/angular/angular.min.js"></script>
<script type="text/javascript">
var module = angular.module('ctrl', []);
module.directive('helpMe', function() {
return {
restrict: 'E',
template: '<div><button ng-click="clicker()">Click 1</button><button ng-click="create()">Create</button><div ng-hide="true">FooBar</div></div>',
controller: ['$scope','$compile' function($scope,$compile) {
$scope.clicker = function () {
console.log('Clicked...');
};
$scope.create = function () {
$('#foo').contents().clone().appendTo('#bar');
$compile($('#bar'))($scope);
}
}]
}
});
</script>
</head>
<body ng-app="ctrl">
<div id="foo">
<help-me></help-me>
</div>
<div id="bar">
</div>
</body>

Can't update $scope value in directive

This directive fires when ng-repeat finishes using scope.$last:
simpleSearchApp.directive('afterResults', function($document) {
return function(scope, element, attrs) {
if (scope.$last){
scope.windowHeight = $document[0].body.clientHeight;
}
};
});
I'm trying to update $scope.windowHeight with a new value but I can't access $scope inside the directive.
My ng-repeat HTML with directive:
<div ng-show="items" class="ng-cloak" ng-repeat="item in items" after-results>
{{item}}
</div>
Honestly saying I have also not understand why it's not updating. But the the alternate approach to update variable in controller is to define a function wich update the variable and call that function in controller from directive. Please take a look at the following example it might help you.
angular.module('app',[]).controller('MyController',['$scope',function($scope){
$scope.windowHeight = 0;
$scope.updateWindowHeight = function(height){
$scope.windowHeight = height;
}
}])
.directive('afterResults', function($document) {
return function(scope, element, attrs) {
if (scope.$last){
scope.windowHeight = $document[0].body.clientHeight;
scope.updateWindowHeight( scope.windowHeight);//calling function in controller
}
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.1" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="MyController">
<div class="ng-cloak" ng-repeat="item in [1,2,3,4,5]" after-results="updateWindowHeight()">
{{item}}
</div>
<hr/>{{ "$scope.windowHeight ="+windowHeight}}
</body>
</html>

ng-transclude replaces the content instead of append

I am learning AngularJS.
I went through this video, here it shows putting ng-transclude on a tag in the template of directive, will append the already present DOM stuff in this tag.
Code :
<html ng-app="myApp">
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
</head>
<body ng-controller="AppCtrl">
<panel>
<button>Click Me</button>
</panel>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('AppCtrl', function() {
});
app.directive('panel', function() { //can return a function (link) or an object
return {
restrict: 'E',
transclude: true,
template: '<span> This is my custom span</span><div class="panel" ng-transclude>this is my div</div><span>Another span follows</span>'
}
});
</script>
</body>
</html>
Here's the fiddle, demonstrating the problem.
That is the normal behavior of the latest version.
From Angular API on ng-transclude.
"Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted."
If you run your fiddle with Angular version 1.0.8, then it will work like in the video.
Use a <span> as the transclusion host:
// Assign tpl var to template configuration property
var tpl = [
'<span> This is my custom span</span>',
'<div class="panel">',
'<span ng-transclude></span>',
' this is my div',
'</div>',
'<span ng-switch-when="divider"></span>',
'<span>Another span follows</span>'', // End
].join('\n');

Categories