I have simple scenario with costume directive in angularjs. In which if there will be only one item in drop down it would be selected by default else it work as normal drop down list.
for that I try below code,
var app = angular.module("testapp", []);
(function () {
'use strict';
angular.module('testapp').controller('TestCtrl', ['$scope', TestCtrl]);
function TestCtrl($scope) {
$scope.Test=[];
$scope.Test.id=0;
$scope.data = [{
"id": 1,
"name": "abc"
}];
}
})()
angular.module('testapp').directive('advanceDropdown', function () {
var directive = {}
directive.restrict = 'A';
directive.require = 'ngModel';
directive.scope = {
items: '=advanceDropdown'
}
directive.link = function (scope, element, attrs, ctrl) {
if (scope.items.length == 1) {
element.val(scope.items[0]);
}
}
return directive;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.2/angular.min.js"></script>
<div ng-app="testapp" ng-controller="TestCtrl">
<select name="ddlTest" ng-model="Test.id" ng-options="d.id as d.name for d in data" advance-dropdown="data" required>
<option value="" ng-selected="selected" disabled>--Select--</option>
</select>
</div>
but it can't works. what I missing in the code.
Your code is correct, only a small issue inside directive, try the following please
directive.link = function (scope, element, attrs, ctrl) {
if (scope.items.length == 1) {
element.val = scope.items[0];
}
}
Demo
Related
How to expose directive methods without using $broadcast or '=' between modules?
Using $broadcast (events) if there are multiple directives all will be notified. It cannot return value too.
Exposing directive's function by html attribute I think it is not that best that Angular has to offer.
Angular Bootstrap UI do it using services (I guess): It have a service named "$uibModal".
You can call a function "$uibModal.open()" of Modal Directive by injecting $uibModal service.
Is that the right way?
An example of a directive that registers its API with a service:
app.service("apiService", function() {
var apiHash = {};
this.addApi = function (name,api) {
apiHash[name] = api;
};
this.removeApi = function (name) {
delete apiHash[name];
};
this.getApi = function (name) {
return apiHash[name];
};
});
app.directive("myDirective", function (apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs)
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.removeApi(name);
});
}
});
Elsewhere in the app, the title of the directive can be set with:
apiService.getApi('myDirective').setTitle("New Title");
Notice that the directive registers the api with a name determined by the name attribute of the directive. To avoid memory leaks, it unregisters itself when the scope is destroyed.
Update
How could I use it from a controller?
app.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<body ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
The DEMO
angular.module('myApp', [])
.service("apiService", function() {
var apiHash = {};
this.addApi = function(name, api) {
apiHash[name] = api;
};
this.getApi = function(name) {
return apiHash[name];
};
})
.directive("myDirective", function(apiService) {
return {
restrict: 'E',
scope: {},
template: `<h1>{{title}}</h1>`,
link: postLink
};
function postLink(scope, elem, attrs) {
var name = attrs.name || 'myDirective';
var api = {};
api.setTitle = function(value) {
scope.title = value;
};
apiService.addApi(name, api);
scope.$on("$destroy", function() {
apiService.addApi(name, null);
});
}
})
.controller('home', function($scope,apiService) {
$scope.title = "New Title";
$scope.setTitle = function() {
apiService.getApi('mainTitle').setTitle($scope.title);
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="home">
<my-directive name="mainTitle"></my-directive>
<p>
<input ng-model="title" />
<button ng-click="setTitle()">Set Title
</button>
</p>
</body>
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
this service exposes function charCount();
in your directive you have to inject it like this
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
and, of course call it
$scope.strLen = myService.charCount( $scope.myTestString );
<html>
<style>
#out {
width:96%;
height:25%;
padding:10px;
border:3px dashed blue;
font-family: monospace;
font-size: 15px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var APP = angular.module('MYAPP', []);
APP.controller('main', ['$scope', '$element', '$compile', 'myService', function($scope, $element, $compile, myService) {
$scope.test = 'my Test Controller';
$scope.directiveTest = "directive test";
var testSvc = myService.charCount($scope.test);
$scope.showTestDir = true;
}])
.directive('testDirective', ['myService', function(myService) {
return {
restrict: 'A',
replace: true,
template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
link: function($scope, el, attrs) {
$scope.myTestString = 'string of length 19';
$scope.strLen = myService.charCount( $scope.myTestString );
}
}
}])
.factory('myService', [function() {
return {
charCount: function(inputString) {
return inputString.length;
}
}
}])
.filter('toUpper', function() {
return function(input) {
return input.toUpperCase();
}
})
.filter('toLower', function() {
return function(input) {
return input.toLowerCase();
}
})
;
</script>
<body ng-app="MYAPP">
<div id="out" ng-controller="main">
{{test}} - not filtered
<br/>
{{test|toUpper}} - filtered toUpper
<br/>
{{test|toLower}} - filtered toLower
<br/>
<br/>
<div test-directive ng-if="showTestDir"></div>
</div>
</body>
</html>
I am trying to call a parent function from a directive. But my functions are not being called.
Here is the code for your reference.
Controller
'use strict';
angular.module('myApp')
.controller('MyCtrl', function($scope) {
$scope.iconSelected = function() {
console.log('iconSelected');
var icon = angular.element('#icon').prop('files');
if (!icon) {
return;
}
icon = icon[0];
var _URL = window.URL || window.webkitURL;
$scope.utility.icon = _URL.createObjectURL(icon);
}
$scope.sourceSelected = function() {
console.log('sourceSelected');
var source = angular.element('#source');
console.log(source.prop('files'));
};
});
Directive
'use strict';
angular.module('myApp')
.directive('uploadButton', function() {
return {
templateUrl: 'app/directives/upload-button/upload-button.html',
restrict: 'E',
transclude: true,
scope: {
onSelect: '&'
},
link: function(scope, element, attrs) {
scope.name = attrs.name;
scope.id = attrs.id || attrs.name;
scope.label = attrs.label || attrs.name;
scope.accept = attrs.accept;
scope.showDialog = function() {
element.find('#' + scope.id).trigger('click');
};
element.find('input').change(function() {
scope.$apply(attrs.onSelect);
});
}
};
});
Directive Template
<md-input-container class="upload-button">
<md-button class="md-raised" ng-click="showDialog()">
<span ng-transclude></span>
</md-button>
<input type="file" name="{{name}}" id="{{id}}" aria-label="{{label}}" accept="{{accept}}">
</md-input-container>
Directive Usage
<upload-button name="icon" on-select="iconSelected()" accept=".svg">Choose an icon</upload-button>
<upload-button class="source-btn" name="source" on-select="sourceSelected()" accept=".zip">Select source code</upload-button>
Inside your directive code you are calling onSelect using attrs.onSelect change it to scope.onSelect. attrs.onSelect will just give you the string value iconSelected(). You need the function reference which will be available in the isolated scope which is created by the directive.
element.find('input').change(function() {
scope.$apply(scope.onSelect);
});
I'm a bit stuck on an directive which add attributes and recompile the element.
If I had a scope on the directive ng-change is not triggered anymore (without it it works). I based my test on this answer
The HTML
<div ng-app="myApp">
<div ng-controller='testController'>
<div ng-repeat="field in fields">
<input type="text" ng-model="ngModel[field.fieldName]" property="{{formParams.getProperties(field.fieldName)}}" update-attr ng-change="test()" />
</div>
</div>
</div>
The directive:
angular.module('myApp', [])
.controller('testController', function ($scope) {
$scope.properties = {
"something": {
"style": "float:left;"
},
"something2": {
"style": "float:right;"
}
};
$scope.ngModel = {};
$scope.fields = [{
fieldName: 'something'
}, {
fieldName: 'something2'
}];
$scope.test = function () {
alert('i dont get triggered');
};
$scope.formParams = {
getProperties: function (fieldName) {
return $scope.properties[fieldName];
}
};
})
.directive('updateAttr', function ($compile) {
return {
restrict: 'A',
replace: true,
terminate: true,
scope: {
ngModel : '='
},
link: function (scope, elem, attrs) {
if (angular.isDefined(attrs['property']) && attrs['property'].lenght != 0) {
var json = JSON.parse(attrs['property']);
angular.forEach(json, function (value, key) {
elem.attr(key, value);
});
elem.removeAttr('property');
var $e = $compile(elem[0].outerHTML)(scope);
elem.replaceWith($e);
}
}
};
});
Here a fork of the fiddle to test with a scope on the directive: fiddle
Do you have any suggestion ?
I found why ng-change was not trigger so I share the answer:
When we add scope attribute on the directive, a new scope is created. So we have to use $scope.$parent for the compilation. I have updated the fiddle with the correction.
I'm changing data attribute of a dom element and using $observe within the directive to detect for any changes but it doesn't seem to work after clicking on the "change data" button
HTML
<body ng-controller="MainCtrl">
<div id="container" data-name="somename" mydirective>Data</div>
<button ng-click="changeData();">Change Data Attribute</button>
</body>
JS
app.controller('MainCtrl', function($scope) {
$scope.changeData = function() {
var el = document.querySelector('#container');
angular.element(el).attr('data-name', 'hello');
}
});
app.directive('mydirective', function() {
return {
link : function(scope, element, attrs, ngModel) {
attrs.$observe("name", function (newValue) {
console.log(newValue);
});
}
}
Plnkr : http://plnkr.co/edit/saM7fO0DdsaaDBW7ADQH?p=preview
why dont you use expression data-name="{{datax}}" ?
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.datax = "somename";
$scope.changeData = function() {
//var el = document.querySelector('#container');
//angular.element(el).attr('data-name', 'hello');
$scope.datax = "hello";
}
});
app.directive('mydirective', function() {
return {
link : function(scope, element, attrs, ngModel) {
attrs.$observe("name", function (newValue) {
console.log(newValue);
});
}
}
});
I am building a angular directive.
I am binding a property to isolated scope in directive like
scope : {
contentModel : '='
}
'use strict';
/**
* Tc markdown directive
*/
var myapp = angular.module('myapp',[]);
myapp.directive('tcMarkdown',[function() {
var directive = {};
directive.restrict = 'E';
directive.template = '<div><div class="row"><!--Content edit pane --><div class="col-md-12"><textarea class="form-control editor" ng-model="someobj.text.data"></textarea></div></div></div>{{contentModel}}';
directive.scope = {
contentModel : '='
};
directive.link = function(scope, element, attrs) {
scope.options = {selected : 0};
scope.$watch(function() {
return scope.options.selected;
}, function(newVal) {
if(newVal===1) {
scope.buttonCaption = {text : 'Edit'};
} else if(newVal === 0) {
scope.buttonCaption = {text : 'Preview'};
}
});
};
return directive;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp">
<data-tc-markdown content-model="content"></data-tc-markdown>
</div>
The two way binding is not working.
As I am typing in textarea the model is not updated.
What am I missing ?
I don't see how you are binding the internal contentModel to your textarea.
Here is an updated working fiddle.
I replaced the someobj.text.data assigned to ng-model with contentModel:
myapp.directive('tcMarkdown',[function() {
var directive = {};
directive.restrict = 'E';
directive.template = '<div><div class="row"><!--Content edit pane --><div class="col-md-12"><textarea class="form-control editor" ng-model="contentModel"></textarea></div></div></div>';
directive.scope = {
contentModel : '='
};
directive.link = function(scope, element, attrs) {
scope.options = {selected : 0};
scope.$watch(function() {
return scope.options.selected;
}, function(newVal) {
if(newVal===1) {
scope.buttonCaption = {text : 'Edit'};
} else if(newVal === 0) {
scope.buttonCaption = {text : 'Preview'};
}
});
};
return directive;
}]);
And then I pulled {{contentModel}} out to make sure that {{content}} binds in the outer scope:
<div ng-app="myapp">
<data-tc-markdown content-model="content"></data-tc-markdown>
{{content}}
</div>
this seems to work.
The "content" variable should be defined on an outer scope of your directive. For example, see below: I defined content1 and content2 on an outer controller. These contain the values themselves.
http://jsfiddle.net/jajtzyhh/3/
var myapp = angular.module('myapp',[]).controller('MyController', ['$scope', function($scope) {
$scope.content1 = 'Hello';
$scope.content2 = 'World';
}]);
<div ng-app="myapp">
<div ng-controller="MyController">
<data-tc-markdown content-model="content1"></data-tc-markdown>
<data-tc-markdown content-model="content2"></data-tc-markdown>
</div>
</div>