I'm trying to wrap a ui-select in a custom directive. (https://github.com/angular-ui/ui-select)
this.adminv2.directive('eventSelect', function() {
return {
restrict: 'E',
replace: true,
scope: {
ngModel: '=',
placeholder: '='
},
controller: function($scope, $http) {
return $scope.refreshEvents = function(searchTerm) {
return $http.get('/events/autocomplete', {
params: {
term: searchTerm
}
}).then(function(response) {
return $scope.events = response.data;
});
};
},
template: "<div>{{ngModel}}\n <ui-select ng-model=\"ngModel\"\n theme=\"bootstrap\"\n ng-disabled=\"disabled\"\n reset-search-input=\"false\">\n <ui-select-match placeholder=\"Enter an event\">{{$select.selected.name}}</ui-select-match>\n <ui-select-choices repeat=\"event in events track by $index\"\n refresh=\"refreshEvents($select.search)\"\n refresh-delay=\"0\">\n <span ng-bind-html=\"event.name | highlight: $select.search\"></span>\n <i class=\"icon-uniF111 fg type-{{raceType}} pull-right\" ng-repeat='raceType in event.racetypes'></i>\n <br>\n {{event.dates}} <i class='pull-right'>{{event.location}}</i>\n </ui-select-choices>\n </ui-select>\n</div>"
};
});
The select works properly, but the binding with ng-model doesn't work. I cannot set the model or read it.
I don't get it since it works when I use a simple template such as
<div><input ng-model="ngModel"></div>
Is there something special to do because I wrap a directive in directive ?
I managed to make the binding work by setting the ng-model in the template as
ng-model="$parent.ngModel"
Related
I currently have the following directive:
return {
restrict: 'E',
scope: {
value: '=',
},
controller: ['$scope',
function ($scope) {
$scope.checked = function (...) { ... };
$scope.toggle = function (...) { ... };
},
],
template: '<input type="checkbox" ng-checked="checked(...)" ng-click="click(...)" />',
};
This works using this syntax:
<checker value='shoes' />
<checker value='pants' />
However - I'd also like to bind to the ng-click event for other purposes. How can I convert to a 'A' directive and still bind to ng-checked and ng-click (without the template) and allow me to use the following syntax:
<input type="checkbox" checker ng-click="other()" />
Similar to this question, I have a directive that I want to use in a couple different situations. I'm passing in a value that it will work with, but I'd also like to pass in a filter to apply to it. What's the right syntax for doing this?
app.directive('showMe', function() {
return {
restrict: 'E',
scope: {
value: '=',
filter: '=',
placeholder: '='
},
templateUrl: 'show-me.html'
};
});
template:
<input ng-model="value" ng-show="editMode" placeholder="{{placeholder}}" />
<p ng-hide="editMode">{{(value | percent)||placeholder}}</p>
use:
<show-me value="item.discount" filter="percent" placeholder="0%" />
expected result:
<input value="25" placeholder="0%" />
or
<p>25%</p>
You could achieve this at a basic level by using the method below; simply use the filter provider within your directive controller, to parse the given value. For example:
<my-directive value="1461782417" filter="date"></my-directive>
Within "myDirective" you could implement the filter very easily like this:
angular
.module('myApp')
.directive('myDirective', {
restrict: 'E',
template: '<span>{{displayValue}}</span>',
scope: {
value: '=',
filter: '='
},
controller: ['$scope', '$filter', function ($scope, $filter) {
// Pass the value
$scope.displayValue = $filter($scope.filter)($scope.value);
}]
});
.filter('percent', function () {
return function (text, length, end) {
if (text == null || text == '') text = '0';
text = parseInt(text) / 100;
return text.toFixed(2);
};
I've defined an Angular directive stField (<st-field>). It dynamically creates a DOM element, <st-field-vov>, and inserts it inside using $compile. I need this dynamic injection because there are other types of fields. The DOM would look like this:
<st-field>
<st-field-vov></st-field-vov>
</st-field>
stFieldVov is another custom directive I create, it handles the rendering of the concrete field. Here is the JS:
(function(module) {
'use strict';
module
.directive('stField', _stField)
.directive('stFieldVov', _stFieldVov);
/*ngInject*/
function _stField($compile, stFieldSvc) {
return {
restrict: 'E',
scope: {
stFieldTmpl: '=',
stDataObjects: '='
},
link: function(scope, $elem) {
var _fieldTmpl = scope.stFieldTmpl,
template = '<st-field-' + stFieldSvc.getFieldTypeShortName(_fieldTmpl.type) + '></div>';
$elem.append($compile(template)(scope));
}
};
}
function _stFieldVov() {
return {
restrict: 'E',
link: function(scope) {
var _fieldTmpl = scope.stFieldTmpl,
_dataObjects = scope.stDataObjects,
_isValue = true;
scope.dataObjectDropDownOptions = {
dataSource: new kendo.data.DataSource({
data: _dataObjects
}),
dataTextField: 'name',
dataValueField: 'id'
};
},
templateUrl: '/widgets/fields/directives/templates/vov.html'
};
}
})(angular.module('st.widgets.fields'));
Here is the template for stFieldVov:
<div class="input-group">
<input type="text" class="form-control" ng-show="isValue()">
<input kendo-drop-down-list
k-options="dataObjectDropDownOptions"
k-option-filter="contains">
<span class="input-group-btn">
<button type="button" class="btn"
ng-class="{'st-btn-do': isDataObject()}">
<span class="icon-server"></span>
</button>
</span>
</div>
The problem is that the k-options for configuring the Kendo widget, kendoDropDownList, doesn't work. I think this is because I used $compile to generate <st-field-vov> as k-options works well if use kendoDropDownList in <st-field>.
The browser doesn't throw any error, it is just that the drop down data is empty.
Thanks for reading.
Try to use this:
$elem.append(template);
$compile($elem.contents())(scope);
Good day! I need to create directive with ui-select inside.
Here is directive's controller and template:
app.controller("servicesCtrl", ["$resource", '$scope', function($resource, $scope){
$scope.clinicServices = [];
console.log($scope.data);
var Services = $resource(window.baseurl + "services/getAllbyClinic", null, {
get:{responceType: "JSON", isArray: false, method: 'POST'}
});
$scope.refresh = function(service){
if(service.length < 1) return false;
Services.get({service: service},function(data){
$scope.clinicServices = data.services;
});
};
}]);
<ui-select
multiple
tagging
tagging-label="false"
ng-model="$parent.data"
theme="bootstrap"
title="Выберите услугу"
search-enabled="true"
>
<ui-select-match placeholder="Введите услугу">{{$item.name}}</ui-select-match>
<ui-select-choices repeat="clinicService in clinicServices" refresh="refresh($select.search)">
<div ng-bind-html="(clinicService.name | highlight: $select.search)"></div>
</ui-select-choices>
</ui-select>
{{data}}
And here is directive initialization.
app.directive('services', function() {
return {
restrict: 'E',
templateUrl: window.baseurl + 'views/services',
scope: {
data: "=ngModel"
},
controller: "servicesCtrl",
controllerAs: "services"
}
});
<services ng-model="meEvent.services"></services>
Can't render $scope.data into view from servicesCtrl to edit previously selected data. There are an object inside $scope.data, but empty view's field. I tried ng-model="$parent.data" and ng-model="data", but have no effect.
There where errors in my version of ui-select library
I am trying to write custom directive and one of the requirements is that I should be able to disable one of the elements based on the expression set on attribute of the directive. Directive is declared like this
<sr-payment-edit payment="vm.paymentInfo" disablepaymenttypeselection="!vm.isPolicySelected || someOtherCondition">
Basically it is supposed to hide a payment type if a policy is not selected yet.
Once policy gets selected, payment type would be enabled. Here is html template for that portion of the directive
<div class="row" data-ng-hide='hidePaymentType'>
<div class="col-xs-12 p-l-0 p-r-0">
<div class="col-xs-3">
<div class="form-group">
<label>Payment Type:</label>
<select data-ng-model="payment.paymentTypeCode"
data-ng-disabled="disablePaymentType" class="form-control" style="width: 150px">
<option value="CASH">Cash</option>
<option value="CHCK">Check</option>
<option value="CCPP">Credit Card - Pre Pay</option>
<option value="MNOD">Money Order</option>
</select>
</div>
</div>
</div>
</div>
Here is directive code, in early stages
(function (angular) {
'use strict';
angular.module('app').directive('srPaymentEdit', srPaymentEditDirectiveFactory);
function srPaymentEditDirectiveFactory() {
return {
restrict: 'E',
templateUrl: 'app/app_directives/sr-paymentEdit/sr-paymentEdit.html',
scope: {
payment: '=',
disablepaymenttypeselection: '#',
hidepaymenttypeselection: '#'
},
transclude: false,
controller: controller,
link: link
};
}
function link($scope, element, attrs, model) {
if (attrs.hidepaymenttypeselection) {
$scope.hidePaymentType = $scope.$eval(attrs.hidepaymenttypeselection);
}
if (attrs.disablepaymenttypeselection) {
$scope.$watch(attrs.disablepaymenttypeselection, function (value) {
$scope.disablePaymentType = $scope.$eval(attrs.disablepaymenttypeselection);
});
}
}
function controller($scope) {
if ($scope.payment != null) {
if ($scope.payment instanceof PaymentInfo === false) {
throw 'Invalid datasource type, must be instance of PaymentInfo';
}
} else {
var info = new PaymentInfo();
info.paymentTypeCode = 'CHCK';
$scope.payment = info;
}
}
})(angular);
So far so good, watch fires and disables the selection, but after "vm.isPolicySelected" changes, naturally, watch for the attribute does not fire.
Is it possible to trigger watch so that "disablepaymenttypeselection" is re-evaluated?
Looks like the problem is that in directive scope configuration you need to use = binding, not attribute #. So try this:
scope: {
payment: '=',
disablepaymenttypeselection: '=',
hidepaymenttypeselection: '#'
},
and also change watch part a little (use just property name):
$scope.$watch('disablepaymenttypeselection', function(value) {
$scope.disablePaymentType = $scope.$eval(attrs.disablepaymenttypeselection);
});
You declare that your attributes 'disablepaymenttypeselection' and 'hidepaymenttypeselection' are one way binded as text:
scope: {
payment: '=',
disablepaymenttypeselection: '#',
hidepaymenttypeselection: '#'
},
I think you need to use a two way binding ('=') so you can toggle the (boolean) property to hide or show your html.
With your code if you 'console.log($scope.disablepaymenttypeselection)', the you would get the following output as string:
!vm.isPolicySelected || someOtherCondition
Read this for more info about directives and bindings: When writing a directive in AngularJS, how do I decide if I need no new scope, a new child scope, or a new isolated scope?
Got it working. My problem was that I had created an isolated scope, yet I wanted to respond to changes in parent scope, without explicitly passing in all dependencies. So I re-wrote directive like this
function srPaymentEditDirectiveFactory() {
return {
restrict: 'E',
templateUrl: 'app/app_directives/sr-paymentEdit/sr-paymentEdit.html',
scope: true,
//scope: {
// vm:'=',
// payment: '=',
// disablepaymenttypeselection: '=',
// hidepaymenttypeselection: '#'
//},
transclude: false,
//controller: controller,
link: link
};
}
function link($scope, element, attrs, model) {
var info = null;
if (attrs.payment == undefined) {
info = new PaymentInfo();
info.paymentTypeCode = 'CHCK';
} else {
info = $scope.$eval(attrs.payment);
if (info instanceof PaymentInfo === false) {
throw 'Invalid datasource type, must be instance of PaymentInfo';
}
}
$scope.payment = info;
if (attrs.hidepaymenttypeselection) {
$scope.hidePaymentType = $scope.$eval(attrs.hidepaymenttypeselection);
}
if (attrs.disablepaymenttypeselection) {
$scope.$watch(attrs.disablepaymenttypeselection, function (value) {
$scope.disablePaymentType = $scope.$eval(attrs.disablepaymenttypeselection);
});
}
}
})(angular);