Load input directive by binding in Angular 1.x - angularjs

I have an input like this:
<input type="text" valid-text class="input-text" name="{{$ctrl.name}}">
the key thing is the valid-text directive. This input is a component and some times is valid-text and sometimes valid-number. How can I set dynamically this value?
I tested valid-{{$ctrl.validType}} with no success.

You can begin with the next directive that's get both model and attribute. Now it add both attribute string and number
myApp.directive('dynAttr', function() {
return {
scope: { list: '=dynAttr' },
require: 'ngModel',
link: function(scope, elem, attrs, ngModel){
for(attr in scope.list){
elem.attr(scope.list[attr].attr, scope.list[attr].value);
// you can change this logic
}
}
};
});
in yours controller
$scope.dynAttribute = [
{ attr: 'number', value: '' },
{ attr: 'string', value: '' }
];
and html
<div ng-controller="MyCtrl">
<input ng-model="val" dyn-attr="dynAttribute"/>
</div>

Related

AngularJS Custom Directive - Match More Than One Field?

I have a custom directive obtained from https://github.com/neoziro/angular-match that matches two form fields. However, how can I customize it to match more than one field? Here is better explanation of what I mean:
-Form Field 1
-Form Field 2
-Form Field 3
-Form Field 4
-Confirmation (I want this one to match either Field 1,2,3 OR 4.)
Currently, I can only match it up to one field.
HTML Form:
<input type="text"
name="correctAnswer"
ng-model="quiz.quizData.correctAnswer"
match="answer1">
<div ng-show="theQuiz.correctAnswer.$error.match && !theQuiz.correctAnswer.$pristine">Answers do not match!</div>
Directive:
angular.module('match', []).directive('match', ['$parse', matchDirective]);
function matchDirective($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
scope.$watch(function () {
return [scope.$eval(attrs.match), ctrl.$viewValue];
}, function (values) {
ctrl.$setValidity('match', values[0] === values[1]);
}, true);
}
};
}
It might be easier to write your own directive for this, especially since angular-match plugin is no longer maintained.
To watch multiple form inputs, just pass the ng-model of each desired input to the directive. Here I called it match.
<input type="text" name="firstNameOne" ng-model="firstNameOne"/>
<input type="text" name="firstNameTwo" ng-model="firstNameTwo"/>
<input type="text" name="firstNameThree" ng-model="firstNameThree"/>
<input type="text" name="confirmFirstName" ng-model="confirm" match="{{[firstNameOne, firstNameTwo, firstNameThree]}}"/>
Now for the directive
app.directive('match', function() {
return {
restrict: 'A',
controller: function($scope) {
$scope.doValidation = function(matches) {
//Validation logic.
}
},
link: function(scope, element, attrs) {
scope.$watch('confirm', function() {
scope.matches = JSON.parse(attrs.match); //Parse the array.
scope.doValidation(scope.matches); //Do your validation here.
});
}
}
});
Here is a fiddle showing validation of form inputs: https://jsfiddle.net/cpgoette/und9t5ee/

Angular X-editable and custom directive

look at this example : ( On jsfiddle )
html part :
<div ng-app="AppModul" ng-controller="AppController">
<p>Name : <input type="text" ng-model="name"></p>
<div counter-directive max-length="100" ng-model="name"></div>
{{ name || 'click on me' }}
</div>
Js part :
var app = angular.module('AppModul', ["xeditable"]);
app.controller('AppController', function($scope) {
});
app.directive('counterDirective', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
maxLength: '=',
ngModel: '&'
},
link: function(scope, element, attrs) {
console.log(scope.maxLength);
scope.$watch(scope.ngModel, function(value) {
if (value) {
var newValue = value.length;
console.log(value);
element[0].innerText = newValue;
}
});
}
}
});
If you type in normal text input , by one directive u can see how many letters you typed , in the real time . Now click on editable link and type something . Nothing happens and directive does not work. Actually angular-xeditable does not update the ng-model in the real time unless i click on save button .
How can i have the same behavior as text input tag in Angular-xeditable ?

When I change the value of an input clicking on a button $bindTo doesn't work firebase

So I'm beginner to angularjs and firebase and I'm trying to develop an app which adds values(numerical) on an input. So far I have this:
app.js:
var app = angular.module("app", ['firebase']);
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.bind('click', function() {
console.log(element.parent().find('input'))
element.parent().find('input')[1].value++;
});
}
}
});
and my view:
<section class="form-group">
<label for="">$</label> <input type="button" value="+" add-one>
<input ng-model="user.level" type="text" class="form-control" />
</section>
and my controller:
app.controller('mController', ['$scope', 'User',
function($scope, backHome, User, adicionar){
$scope.user = User(1);
User(1).$bindTo($scope, "user");
}
]);
the thing is that after I click the button with the directive add-one the value of the input changes but the $bindTo is not working...
So why does the bindTo doesn't work when I make a change directly in the DOM?
AngularJS doesn't care what the value of an input is set to, it only cares about what's in the ng-model. Try this...
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.on('click', function() {
scope.$apply(function(){
scope.user.level++
});
});
}
}
});
As pointed out by #PankajParkar, you also need to use scope.$apply when you want to update a binding from event.
angular.module('demo', [])
.controller('DemoController', function($scope){
$scope.user={level: 1};
})
.directive('addOne', function() {
return {
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$apply(scope.user.level++);
});
}
}
})
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<div ng-app="demo" ng-controller="DemoController">
<input type="text" ng-model="user.level">
<input type="button" value="+" add-one>
<hr>
<unary-input button-text="Add one" model="user.level" direction="increment"></unary-input>
<unary-input button-text="-" model="user.level" direction="decrement"></unary-input>
<hr>
<unary-input button-text="-" model="user.val" direction="decrement" initial-val="10"></unary-input>
</div>
In AngularJS, you want to change the view by changing the model that it's based on, versus doing it imperatively like you might with a traditional jQuery approach for example (traversing the DOM and incrementing the value).
UPDATE
Okay, so here's a nice reusable version of the (please check the snippet to see it in action).
The template includes both the button and the input. It accepts 4 values that you set as attributes:
button-text: The text you want to show on the button.
model: The model value for the input.
initial-val: The initial value for the input if you don't want to initialize on your controller.
direction: Whether to increment or decrement the values. This one currently accepts a string "decrement" to subtract. If you have no direction set or any other value set in the attribute, it will increment.
So, you would use it like this:
<unary-input button-text="Subtract One" model="user.val" direction="decrement" initial-val="10"></unary-input>
And the directive itself looks like this:
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
Browsing around I could find a solution doing the way you said in the comments (two buttons one incrementing and another decrementing) thanks a lot for the help! and here's the final version.
app.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "="
},
template: '<input type="text" ng-model="model" /><button ng-click="decrement()">-</button><button ng-click="increment()">+</button>',
link: function(scope, element) {
scope.increment = function() {
scope.model++;
}
scope.decrement = function() {
scope.model--;
}
}
};
});

ng-model with isolated scope directive

I have an isolated directive as an element
How can I bind the ngmodel of it since the return html is being overrided ?
<div ng-repeat="x in list">
<form-element ng-model="value[x.name]"></form-element>
</div>
I am having troubles adding the ngmodel to it
JS :
app.directive('formElement', function($compile) {
return {
restrict: 'E',
scope : {
type : '='
} ,
link : function(scope, element, attrs) {
scope.$watch(function () {
return scope.type ;
}, function() {
var templates = {};
templates['text'] = '<input type ="text" name="{{name}}">' ;
templates['radio'] = '<input ng-repeat="option in optionsList" name="{{name}}" type="radio">';
if (templates[inputType]) {
scope.optionsList = scope.type.data;
scope.name = scope.type.name;
element.html(templates[scope.type.inputType]);
} else {
element.html("");
}
$compile(element.contents())(scope);
}
);
}
}
});
Thanks in advance
As you want the ng-model to introduced inside the field created by the directive you could inject that ngModel inside isolated scope.
Markup
<div ng-repeat="x in list">
<form-element ng-model="x.value" type="x.inputType"></form-element>
</div>
Directive
app.directive('formElement', function($compile) {
return {
restrict: 'E',
scope: {
type: '=',
ngModel: '='
},
link: function(scope, element, attrs) {
var templates = {};
templates['text'] = '<input type ="text" name="{{name}}" ng-model="ngModel">';
templates['radio'] = '<input ng-repeat="option in optionsList" name="{{name}}" type="radio">';
if (templates[scope.type]) {
scope.optionsList = scope.type.data;
scope.name = scope.type.name;
element.append($compile(templates[scope.type])(scope));
} else {
element.html("");
}
//$compile(element.contents())(scope);
}
}
});
Demo Plunkr
What you are doing is to call a property of your isolated scope "ngModel", but you are not using the ngModelController. This doesn't mean that you are using the ngModel directive provided by Angular.
You can change the name of your ngModel property in any other name on your directive, and it should works fine.
Using ngModel it means that you add the require:'ngModel'" prop. in your directive object.
return {
restrict: 'E',
require:'ngModel',
scope: {
type: '='
}
and in the link function use the ngModelController to access to the $viewValue
function(scope, element, attrs, ngModelCtr)
{
var mymodel=ngModelCtr.$viewValue;
}
Here more info.
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

AngularJS : 2-way binding broke when creating new child scope in directive

I am using custom angular form validation to validate fields based off the field having a value AND the selected value of a drop down. I use the same section of code (and therefore the same directive) twice on the page. So I could do this re-use, I tried sending in an argument to the directive. This is all working fine. However when the child scope is created it breaks my 2-way binding back to fruit.cost.
Here is an example fiddle.
What am I doing wrong? I want all the validation to work the same but also preserve the 2-way binding. Here is a copy of my fiddle code:
JS
function MainCtrl($scope){
$scope.localFruits = [
{name: 'banana'},
{name: 'orange'},
{name: 'grape'}
];
$scope.fruits = [
{name: 'banana'},
{name: 'orange'},
{name: 'grape'}
];
$scope.costRequired = [
'local',
'overseas'
];
$scope.selectedCostRequired = '';
}
angular.module('test', []).directive('customRequired', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
requiredWhenKey: '=',
cost: '=' // I think the problem is here?? Can't figure out how to pass the property I want to bind to
},
link: function(scope, elem, attrs, ctrl) {
//console.log(scope);
function isValid(value) {
if (scope.$parent.selectedCostRequired === scope.requiredWhenKey) {
return !!value;
}
return true;
}
ctrl.$parsers.unshift(function(value) {
var valid = isValid(value);
scope.$parent.subForm.cost.$setValidity('customRequired', valid);
return valid ? value : undefined;
});
ctrl.$formatters.unshift(function(value) {
scope.$parent.subForm.cost.$setValidity('customRequired', isValid(value));
return value;
});
scope.$watch('$parent.$parent.selectedCostRequired', function() {
scope.$parent.subForm.cost.$setValidity('customRequired', isValid(ctrl.$modelValue));
});
}
};
});
HTML
<div ng-app="test" ng-controller="MainCtrl">
<form name="form">
Which grouping is required? <select name="costRequired" ng-model="selectedCostRequired" ng-options="t for t in costRequired"></select>
<h2>Local</h2>
<div ng-repeat="fruit in localFruits" ng-form="subForm">
{{fruit.name}}: <input name="cost" ng-model="fruit.cost" required-when-key="'local'" custom-required/> bound value is: [{{fruit.cost}}]
<span class="error" ng-show="subForm.cost.$error.customRequired">Required</span>
</div>
<h2>Overseas</h2>
<div ng-repeat="fruit in fruits" ng-form="subForm">
{{fruit.name}}: <input name="cost" ng-model="fruit.cost" required-when-key="'overseas'" custom-required/> bound value is: [{{fruit.cost}}]
<span class="error" ng-show="subForm.cost.$error.customRequired">Required</span>
</div>
<div ng-show="form.$invalid" class="error">some form error(s) still exist</div>
<div ng-show="!form.$invalid" class="okay">form looks good!</div>
</form>
</div>
Using ng-model with a directive that creates an isolate scope on the same element doesn't work.
I suggest either not creating a new scope, or use scope: true.
Here is a simplified example that does not create a new scope:
<form name="form">
<div ng-repeat="fruit in localFruits">
{{fruit.name}}:
<input ng-model="fruit.cost" required-when-key="local" custom-required/>
bound value is: [{{fruit.cost}}]
</div>
</form>
function MyCtrl($scope) {
$scope.localFruits = [
{name: 'banana'},
];
}
app.directive('customRequired', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
console.log(attrs.requiredWhenKey);
}
};
});
fiddle
Since the required-when-key attribute is just a string, you can get the value using attrs.requiredWhenKey.
If you don't create a new scope, you should also be able to remove most of the $parent lookups in your directive.

Resources