I've written a filter in AngularJS, which was working fine when using it with ng-options on an input select.
Since I needed a more powerful select box with additional functionality, I've written a directive.
The directive has an isolated scope with the filtered options being passed via a two-way binding. Unfortunately, my filter produces a "10 $digest() iterations reached"-error.
This is my filter:
angularAMD.filter('gasUsableInSpacer', function(){
return function(gasTypes, isSealed) {
var filtered = [];
angular.forEach(gasTypes, function(gasType){
if(isSealed){
if(gasType.usableInSealedSpacer)
filtered.push(gasType);
}
if(!isSealed){
if(gasType.usableInSecondarySpacer)
filtered.push(gasType);
}
});
return filtered;
};
});
and this is how I included my directive:
<filter-select options="allGasTypes | gasUsableInSpacer:element.sealed" element="element.gasType" id="gasTypeX"></filter-select>
The above way isn't working, while this is working fine:
<select class="form-control" ng-model="element.gasType" ng-options="gasType as gasType.naturalName for gasType in allGasTypes | gasUsableInSpacer:element.sealed track by gasType.id" name="gasTypeX" id="gasTypeX">
</select>
This is my directives controller:
angularAMD.directive("filterSelect", function (angularLoad) {
return {
templateUrl: 'rmlModMainApp/directives/filterSelect.html',
restrict: 'E',
scope: {
options : '=',
element: '='
},
link: function(scope, element, attr) {
angularLoad.loadCSS(requirejs.toUrl('rmlModMainApp/directives/filterSelect.css'));
scope.listVisible = false;
scope.selectOption = function(option){
scope.element = option;
scope.toggleList();
};
scope.toggleList = function(){
scope.listVisible = !scope.listVisible;
}
}
}
});
});
And the part of the view that is supposed to render the options:
<div class="filterSelect">
<div class="filterSelectContent" ng-show="listVisible">
<form class="filterSelectSearch">
<div class="input-group col-xs-12">
<input type="text" class="form-control" id="filterText" ng-model="search.text" placeholder="filter" ng-model-options="{debounce: {'default':500, 'blur':0}}">
<span class="input-group-addon"><i class="fa fa-lg fa-filter"></i></span>
</div>
</form>
<div class="filterSelectList">
<ul>
<li ng-repeat="option in options track by option.id" ng-click="selectOption(option)">{{option.x}} <span ng-if="option.y">({{option.y}}mm)</span> [{{option.z}}] </li>
</ul>
</div>
</div>
</div>
I have no idea what I'm doing wrong.
Related
I am facing this problem while selecting file and showing its name on span.
So basically I am trying that whenever I select some file I want to change the span Tag. This is my view
<form name="fileUploadForm" ng-submit="submitFile(fileUploadForm.$valid)">
<div class="form-group upload-btn-wrapper">
<input type="file" class="form-control" id ="myFileField" file-input="patient_file" required />
<button class="csv-upload-btn">choose file</button>
<span id="file-chosen">{{ patient_file }}</span>
</div>
<div class="form-group">
<input type="submit" value="Upload" class="btn btn-primary">
</div>
</form>
following is my directive code
(function() {
angular.module('practimyze.dashboard')
.directive('csvUploader', ["$parse", function($parse) {
return {
restrict: 'EA',
templateUrl: "dashboard/breakdown/csv-uploader/csv-uploader.tpl.html",
replace: true,
controller: function($scope, valueHandler, Auth, $rootScope, blockUi, DashboardApi, fileUploadService, apiLinks, toaster){
},
link: function(scope, element, attrs) {
var model = $parse(attrs.csvUploader);
var modelSetter = model.assign;
element.bind('change', function() {
scope.$apply(function() {
modelSetter(scope, element[0].patient_file);
});
});
}
};
}]);
})();
Initially I did this
$parse.assign(scop, element[0].files);
this was getting me error that parse has not function assign. Then I changed it to what i have written now it says (modelSetter is not a function).
I am not sure where i am doing wrong, Please help me out thanks
Edited:
this is the fiddle link, it seems to be working perfect there fiddle link
I am using angular.js 1.6.4 version. I created a directive for server-side validation and I see that it's firing when I am loading the form, which is wrong. I only want to fire my code when I changed the value. My HTML code is
<div class="col-xs-6">
<div class="controls">
<label class="control-label"
ng-hide="editRetailTrackingForm.barcode.$dirty && editRetailTrackingForm.barcode.$error">
#Labels.barcode:
</label>
<input type="text" name="barcode"
ng-model="currentItem.barcode"
sm-code-unique-validator
table-name="items"
column-to-test="barcode"
error-message="itemBarcodeErrorMessage"
primary-key="currentItem.itemId"
ng-model-options="{ updateOn: 'blur', debounce: { default : 500, blur: 0 }}"
class="form-control"
ng-maxlength="100" />
<div ng-class="{loading:editRetailTrackingForm.barcode.$pending}">#String.Format(Messages.validatingX, Labels.barcode)</div>
<div ng-if="editRetailTrackingForm.barcode.$dirty">
<div ng-messages="editRetailTrackingForm.barcode.$error">
<div ng-message="maxlength">
<label class="field-validation-error control-label-error animate-show">
#String.Format(Messages.cannotExceed, Labels.barcode, "100")
</label>
</div>
<div ng-message="smCodeUnique">
<div class="info-text-block-error">
{{itemBarcodeErrorMessage}}
</div>
</div>
</div>
</div>
</div>
</div>
and my directive code is very similar to http://www.codelord.net/2014/11/02/angularjs-1-dot-3-taste-async-validators/
return {
require: "ngModel",
scope: {
primaryKey: "=?",
tableName: "#",
columnToTest: "#",
errorMessage: "=?"
},
link: function ($scope, element, attrs, ngModel) {
ngModel.$asyncValidators.smCodeUnique = function (modelValue, viewValue) {
if (!viewValue) {
return true;
}
let codeObject = {
id: $scope.primaryKey, tableName: $scope.tableName,
columnToTest: $scope.columnToTest, code: viewValue
};
return services.Http.put('api/items/checkCodeUniqueness', codeObject).then(
function (response) {
if (!response.data.isValid) {
$scope.errorMessage = response.data.errorMessage;
return services.Deferred.reject(response.data.errorMessage);
}
return true;
}
);
};
}
};
When I am debugging my code I see that server side code is running multiple times when I only instantiate my form (and not even open the correct tab where this field is).
So, how should I change my directive or ng-options to only fire validations when I changed my value?
I am having a problem with my code basically i have this span (dynamically created via ng-repeat)
<span my-size id="abc"></span>
<span my-size id="def"></span>
<span my-size id="ghi"></span>
what I need to achieve is to get the id of each span and split its character and created checkbox and used the split[] i get when i split the id to be its value
<span id="abc">
<input type="checkbox" value="a">
<input type="checkbox" value="b">
<input type="checkbox" value="c">
</span>
<span id="def">
<input type="checkbox" value="d">
<input type="checkbox" value="e">
<input type="checkbox" value="f">
</span>
<span id="ghi">
<input type="checkbox" value="g">
<input type="checkbox" value="h">
<input type="checkbox" value="i">
</span>
could anyone help me do this in angular js directives
this is all i have so far :
myApp.directive('mySize', function () {
return {
restrict: 'E',
scope: { html: '#' },
template: '<span ng-bind-html="html"></span>',
link : function (scope, element, attrs) {
scope.html="<h1>sample html </h1>";
}
}
});
I can't go further since my directives wont generate html tags
I'm not sure what your initial intention was when writing the directive, because there is really no need to either use a link-function or ng-bind-html.
For what you've asked the directive could be as simple as:
(function (app, ng) {
'use strict';
app.directive('mySize', function () {
return {
restrict: 'A',
scope: { id: '#id' },
template: '<input type="checkbox" data-ng-repeat="char in id track by $index" value="{{ char }}">'
}
});
}(angular.module('app', []), angular));
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
<div data-ng-app="app">
<span data-my-size id="abc"></span>
<span data-my-size id="def"></span>
<span data-my-size id="ghi"></span>
</div>
Edit
If you you want to add extra information just fiddle with the template. E.g.:
(function (app, ng) {
'use strict';
app.directive('mySize', function () {
return {
// actually obsolute, because it's the default setting
restrict: 'A',
// reusing the directive name to simplify the required view-html,
// but mapping it to a more fitting name when used internally
scope: { str: '#mySize' },
// the actual template (this time using a label)
template: '<label data-ng-repeat="char in str track by $index"><input type="checkbox" value="{{ char }}"> {{ char }}</label>'
}
});
}(angular.module('app', []), angular));
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
<div data-ng-app="app">
<div data-my-size="abc"></div>
<div data-my-size="def"></div>
<div data-my-size="ghi"></div>
</div>
It doesn't work because you restricted it's usage to element and use it as attribute. Change restrict: 'E' to restrict: 'A'.
var app = angular.module('app', []);
app.directive('mySize', function($sce) {
return {
restrict: 'EA',
scope: {
id: '#'
},
template: '<label ng-repeat="i in array"><input type="checkbox" ng-value="i" >{{i}}<label>',
link: function(scope, element, attrs) {
scope.array = scope.id.split("")
}
}
});
app.controller('homeCtrl', function($scope) {
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<span my-size id="abc"></span>
<span my-size id="def"></span>
<span my-size id="ghi"></span>
</div>
</div>
The directive can look like this:
.directive('mySize', function() {
return {
restrict: 'AE',
link: function(scope, element, attrs) {
element[0].id.split('').map(function(el) {
element.append('<label><input type="checkbox" value="' + el + '"> ' + el + '</label>');
});
}
}
});
Demo: http://plnkr.co/edit/nZMEdUjMXG3MDYv19H5C?p=preview
In my directive, I need to select out certain DOM elements, some of which are generated dynamically in an ng-repeat loop. If I do it in a straightforward way, I will only get the static elements. However, if I delay the selection by, say, 500ms, I will get all elements, which is what I want.
Although this works, it is not an ideal solution, and certainly doesn't seem like best practise. On the one hand, you'd like to keep the timeout as short as possible, but on the other hand, you want to be sure that the DOM is ready before selecting.
Is there an event which fires when all dynamic DOM is ready? What is the recommended way to select dynamically generated elements from an AngularJS directive?
EXAMPLE:
HTML:
<div data-my-directive>
<div class="modal-body">
<label data-localize>type:</label>
<select class="form-control" ng-model="assetFilter.appCode" ng-change="loadassets(assetFilter.appCode)" ng-options="type.code as type.name for type in types"></select>
<table class="table table-default" ng-show="hasLoaded">
<tbody ng-repeat="asset in assets | filter:assetFilter | orderBy:'assetKey':false">
<tr>
<td>
<div class="container-fluid">
<div class="row vert-align">
<div class="col-sm-4">
{{asset.assetKey}}
</div>
<div class="col-sm-8" style="height:100%">
<input ng-hide="asset.assetKey.length >= 80" type="text" class="form-control" ng-model="asset.assetValue" ng-change="asset.isModified=true">
<textarea ng-show="asset.assetKey.length > 80" class="form-control" ng-model="asset.assetValue" ng-change="asset.isModified=true"></textarea>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="save(saveassets, $event)" ng-disabled="!(assets | anyModified)" data-localize>Save</button>
<button class="btn btn-warning" ng-click="close($event)" data-localize>Close</button>
</div>
</div>
Directive:
myApp.directive('myDirective', function ($timeout) {
return {
restrict: 'A', //attribute only
link: function (scope, elem, attr, ctrl) {
var context = elem[0];
var availableFormElements = 'input:not([disabled]):not([class*=ng-hide]),' +
'select:not([disabled]):not([class*=ng-hide]), textarea:not([disabled]):not([class*=ng-hide]),' +
'button:not([disabled]):not([class*=ng-hide]),' +
'*[class*=btn]:not([disabled]):not([class*=ng-hide])';
var allFormElements = context.querySelectorAll(availableFormElements);
// Will only get static elements, nothing from ng-repeat loop
$timeout(function () {
allFormElements = context.querySelectorAll(availableFormElements);
// Will include all elements, also from ng-repeat loop
}, 500);
// Code to manipulate selected form elements
};
});
This is a simple example how you could work it out.
Imo the only drawback to this solution is you can't use an isolate scope.
html
<div data-ng-controller="MainController">
<div outer-directive>
<ul>
<li ng-repeat="asset in assets" inner-directive>
{{asset}}
<input type="text" class="form-control">
</li>
</ul>
</div>
</div>
js
var app = angular.module('myApp', []);
app.controller('MainController',function($scope) {
$scope.assets = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
});
app.directive('outerDirective', function() {
return {
restrict: 'A',
controller: function($scope) {
}
};
});
app.directive('innerDirective', function() {
return {
restrict: 'A',
require: '^outerDirective',
link: function(scope, elem, attrs,ctrl) {
var context = elem[0];
if (scope.$last){
var availableFormElements = 'input,textarea';
var allFormElements = context.querySelectorAll(availableFormElements);
console.log(allFormElements);
}
}
};
});
or better
.directive('myParent', function ($timeout) {
return {
restrict: 'A', //attribute only
controller: function ($scope, $element) {
this.isDone = function(){
var context = $element[0];
var availableFormElements = 'input,textarea';
var allFormElements = context.querySelectorAll(availableFormElements);
console.log(allFormElements);
}
}
};
})
.directive('myChild', function ($timeout) {
return {
require:'^myParent',
restrict: 'A', //attribute only
link: function (scope, elem, attr, ctrl) {
if (scope.$last){
ctrl.isDone();
}
}
};
})
BTW
Don't touch the dom in the controller :)
code for the directive template
//"textBox.html"
<div class="well">
<label class="control-label">Text</label>
<div class="controls">
<input id="label" type="text" class="txt span3" ng-model="label" placeholder='Label for text field...'>
<input type="text" class="span3" ng-model="value" placeholder='Default value...'>
<input type="text" class="span3" ng-model="helpText" placeholder="Help text...">
<input type="checkbox" class="span1" ng-model="required" ng-true-value="true" ng-false-value="false">Required
<input type="checkbox" class="span1" ng-model="advanced" ng-true-value="true" ng-false-value="false">Advanced?
<img src="../../images/bullet_cross.png" alt="Remove" style="cursor: pointer" id="text" border="0" ng-click="deleteField($event)">
</div>
</div>
directive is using like this in main html page
//"algorithm.html"
<text-box></text-box>
controller for the custom directive
//"controller.js"
var algorithm = angular.module('algorithmController',[]);
/***********directive to render text field***********/
algorithm.directive('textField' , function(){
return{
restrict: 'E',
templateUrl: '../partials/algorithm/textBox.html',
require: 'ngModel',
replace: true,
link: function(scope, iElement, iAttrs, ngModelCtrl) {
// how should i get updated data(i.e. if user change after typing) over here entered by user??
}
};
});
You can create an isolate scope using the '=' syntax, which will create two way binding to your controller and the directive. You don't even necessarily need ngModel required in your directive.
.directive("textField", function () {
return {
restrict : "E",
template : "<input type='text' ng-model='val'/>",
scope : {
val : "="
}
};
});
Here is a very simple example doing what you requested;
http://jsfiddle.net/smaye81/xaohrv53/2/