I am a beginner to AngularJs and I cannot figure out how to retrieve data from outside a directive. I have various input being updated and I need the directive to take this data and work with it.
For example, in the code below, the first input field is hooked up to the directive and works fine, but without putting the directive attribute on the second input field, how can the data typed in that field be updated in the directive?
HTML:
<div ng-app="myDirective">
<input type="text" ng-model="test1" my-directive>
<input type="text" ng-model="test2">
</div>
Directive:
angular.module('myDirective', [])
.directive('myDirective', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(attrs.ngModel, function (v) {
console.log('New Value from field 1: ' + v);
//console.log('New Value from field 2: ' + ???);
});
}
};
});
I could have explained that in text, but I think it would be much better if you watch these 3 videos by john lindquist:
Isolate Scope "#"
Isolate Scope "="
Isolate Scope "&"
And summary.
They are really short (~4min each) but very simple and useful.
PS: by the way, i recommend you to watch others as well. They are all short and precise, loved them.
Since your directive does not create a new scope, the scope variable inside the directive's link method points to the outer scope containing the two inputs. So you can replace:
//console.log('New Value from field 2: ' + ???);
with
console.log('New Value from field 2: ' + scope.test2);
Make sure to enter some data in the second input when testing or it will print undefined.
Here is a working fiddle
EDIT: If you did need to use isolate scope in your directive, you could do the following in your HTML:
<input type="text" ng-model="test1" my-directive="test2">
<input type="text" ng-model="test2">
The difference here is now passing in the test2 model into the directive, and setting up a binding to it in your directive by adding the scope property:
scope: {
otherInput: '=myDirective'
// this tells the directive to bind the local variable `otherInput`
// to whatever the `my-directive` attribute value is. In this case, `test2`
},
This allows you to access passed values in your directive. You would then change your $watches as follows:
scope.$watch(attrs.ngModel, function (v) {
console.log('New Value from field 1: ' + v);
console.log('New Value from field 2: ' + scope.otherInput);
});
// notice the syntax for watching a scope variable
scope.$watch('otherInput', function (v) {
console.log('New Value from field 1: ' + scope.test1);
console.log('New Value from field 2: ' + v);
});
I've included this in my fiddle as another example, test3 and test4.
AngularJs directive lets you to use scope with different ways and do many cool things you need.You can use your scope as not inherit, inherit and isolated.If you use scope as isolated,you can pass variables and bind it wherever you want.
here are 2 cool articles with examples, which can help you
http://www.w3docs.com/snippets/angularjs/change-variable-from-outside-of-directive.html
http://www.w3docs.com/snippets/angularjs/bind-variable-inside-angularjs-directive-isolated-scope.html
Related
So I'm working with Angular 1.5. I'm trying to accomplish something quite trivial. Apologies if this sounds a bit off as backend is more my fortè. What I'm trying to do is pass a scope variable to an input attribute. Is this a known issue?
angular.module('CompDirective',
['ngMaterial']
).directive('pitchButton', function(){
return {
restrict: 'E',
scope: {
campaign: '='
},
templateUrl: '/static/html/compensation.html',
link: function(scope, element, attrs){
scope.minCompensation = 100;
}
)
and in my html.
<input
type="number"
ng-model='fields.monetary_compensation'
name="compensation" required
ng-pattern='/^[1-9]+[0-9]*$/'
placeholder="Compensation"
min="minCompensation"
></input>
Echoing out minCompensation works as expected but the param min with minCompensation is not validating which leads me to believe that there is an issue with the input directive's min param being able to recognize scope variables.
min is not in the documentation, so I'm guessing it doesn't accept an angular expression.
You need to interpolate the value as in <input ... min="{{minCompensation}}" />
Alternatively, you can create your own ng-min and ng-max directives.
The scope value needs to be interpolated/evaluated. So the ng-attr attribute will evaluate and bind the appropriate value in addition to bracketting the variable.
Code
<input
type="number"
ng-model='fields.monetary_compensation'
name="compensation"
placeholder="Compensation"
ng-attr-min="{{minCompensation}}"
></input>
I've been using Angular directives and have encountered a slightly weird issue where a trailing space is appended to element IDs when I try to autogenerate them.
I have two directives that are nested. In my outer directive template I have the following code snippet:
<div combobox
id="{{questionId}}-list-id"
name="{{questionId}}-list"
model="model"
options="options"></div>
Then, in my inner combobox directive I have the following:
<select
name="{{name}}"
required
id="{{id}}"
ng-model="model">
<option
ng-repeat="option in options"
value="{{option.value}}"
ng-selected="model === option.value">{{option.caption}}</option>
</select>
Let's say I set questionId to "own-or-rent", I'd expect the id attribute on my select element to have the value "own-or-rent-list-id", but in fact it has the value "own-or-rent-list-id ".
I've also tried using ng-attr-id="{{questionId + '-list-id'}}", instead of setting the value of id directly but the result is the same. I've also experimented with different values for the generated ID but to no avail.
How can I get rid of this trailing space? It's causing us problems with our tests and, when we need to start customising individual questions, is also going to play havoc with them.
Any help would be greatly appreciated.
Many thanks!
Bart
Edit
This is with Angular 1.2.26 (because we have to maintain compatibility with IE8).
Here's the JS for my outer directive:
(function () {
"use strict";
angular
.module("question")
.directive("comboboxQuestion", function () {
return {
scope: {
questionId: "#",
questionText: "#",
options: "=",
model: "="
},
restrict: "AE",
replace: "true",
transclude: true,
templateUrl: "/content/pages/directives/combobox-question.html"
};
});
}());
And here's the JS for my inner combobox directive:
(function () {
"use strict";
angular
.module("question")
.directive("combobox", function () {
return {
scope: {
name: "#",
id: "#",
options: "=",
model: "="
},
restrict: "AE",
replace: "true",
transclude: true,
templateUrl: "/content/pages/directives/combobox.html"
};
});
}());
OK, I've figured this out. The problem is due to a naming collision.
I'm using attributes called id and name in my directive. What's happening is that the value of these attributes is being concatenated with whatever values the underlying id and name and value attributes on the select element have regardless of whether or not I set them on the select element as within my directive's markup template.
This is illustrated if I set the values of these attributes to something fixed on the select, like 'hello'. If, for example, I do this for id I end up with a value of the form "generated-id hello". Note the space in the middle between the concatenated values.
To fix it I've added a three letter vendor prefix to my id and name attributes to give me ctmId and ctmName in the directive JS (and any interpolations where they're used), and ctm-id and ctm-name in the markup.
It's not the nicest, but not the end of the world, and really my own lookout in the first place for using attribute names that collide with attributes that can legitimately appear on a div anyway.
Hope this comes in useful for somebody else too!
I am working with Javascript and the d3 library, and AngularJS.
Is there a way to dynamically change an input box's placeholder? I am working with a calendar widget and multiple views and wanted to see if there was a way to have the placeholder always be the value that was last inputted into the field.
I wrote a small function that always returns the last thing that was entered into the input field...but then when I tried setting placeholder=functionIwrote() it literally makes the placeholder "fucntionIwrote()" instead of running the function.
I can't comment but... If you're using angularJs, you just have to bind the model of your input to the placeholder!
<input type="date" placeholder="myModel" ng-model="myModel" />
This way, your input would always be the latest filled value.
If you want to change your view, and then retrieve datas, then you have to store them outside of the controller scope - which is "cleaned" every time if you're using the ngIf directive -.
A good way to do this is to use a service as persistance layer.
Since you want the placeholder to be changed or updated both automatically and dynamically, you may use the jQuery code below:
$(function() {
$('input').on('change blur', function() {
!this.value || $(this).attr('placeholder', this.value);
});
});
WORKING JS FIDDLE DEMO
Yes you can do it in this way ,
<input
placeholder="Select Date"
onfocus="(this.type='date')"
onblur="if (!this.value) this.type = 'text'">
here's a fiddle: http://jsfiddle.net/bBh3L/
'placeholder' is an element attribute, so you can use $('#myInput').attr(attributeName, attributeValue) to set it.
In this case, I mapped the button click to change the placeholder using:
$('#myInput').attr('placeholder', 'new one');
I guess that you're trying to wrap your calendar widget into an angular directive, if so, here's what I did for a similar use case (I display the accepted/valid format as placeholder):
module.directive('yourDirective', [function() {
return {
link: function($scope, $element, $attrs, $controller) {
// bind 'yourValue' (the one you want to show as placeholder) in the scope
$scope.$watch('yourValue', function(value) {
$attrs.$set('placeholder', value);
});
}
};
}]);
There exists a conditional attribute property in AngularJS ng-attr-{property_name}
For example, I'm using different placeholders for different search options using
ng-attr-placeholder="{{isAdvanceSearch ? setPlaceholder(searchOption) : 'John Smith, 08/23/1970, 123'}}"
Here on the basis of isAdvanceSearch variable, I'm setting different placeholders in setPlaceholder method.
setPlaceholder method returns the placeholder to set in the input field.
$scope.setPlaceholder = function(searchOption) {
if (searchOption === "foo") {
return "Search by foo… e.g. foo1";
} else if (searchOption === "bar") {
return "Search by bar… e.g. bar123";
} else {
return "John Smith, 08/23/1970, 123";
}
};
Note: John Smith, 08/23/1970, 123 is the default placeholder.
Don't forget to wrap the expression in the {{}} brackets.
I'm trying to bind a text input to an attribute within a directive, the reason being I don't want to have to introduce a controller each time I add a new directive of this type. Is this even possible or do you always have to specify a controller when using ng-model. Code example is worth a thousand words so please take a look at http://codepen.io/scottwio/pen/HKqiw . As you type in the input the amount should change.
There are two issues with your code:
scope.$watch(attrs.amount, function (v) {...}); <=>
scope.$watch('100', function (v) {...});
which is never going to change, so does not do what you want.
Since the elements attribute is never going to change, function draw(aniTime) { var amount = attrs.amount; is not so usefull.
You can fix them like this:
scope.$watch('amount', function (v) {...});
and
function draw(aniTime) {
var amount = scope.amount;
...
See, also, this short demo.
If you want to share the specified amount with the parent scope, then you need to set up a two-way data-binding and specify a property in the parent scope to bind to. E.g.:
// Parent scope
$scope.someAmount = 100;
// In HTML
<custommarc amount="someAmount" ...
// In directive
scope: {
amount: '='
...
I'm creating a questionnaire form using AngularJS, and I'm templatizing the different types of questions I have to keep my HTML lightweight.
HTML:
<qtnyesno qtn-variable="testVar">This is a test question. Yes or No?</qtnyesno>
testVar = {{testVar}}
JS:
var module = angular.module('app', [])
.directive('qtnyesno', function() {
return {
restrict: 'E', // must be an HTML element
transclude: true,
scope: {
valvar: '#qtnVariable',
},
template:
'<div>' +
'<label ng-transclude></label>' +
'<label class="radio inline"><input type="radio" name="rdo{{valvar}}" value="1" ng-model="{{valvar}}">Yes </label>' +
'<label class="radio inline"><input type="radio" name="rdo{{valvar}}" value="0" ng-model="{{valvar}}">No </label>' +
'<hr></div>',
replace: true,
};
});
When I use within the input tag ng-model="{{valvar}}" I get compilation errors.
When I try using instead ng-click="{{valvar}} = 0" I get no compilation errors, but my display statement to get the value of testVar never works when I click on the radio buttons.
How would I fix this such that the last line of my HTML gets properly updated with the value of testVar when I click on the buttons?
I'm using AngularJS along with Bootstrap. I'm fairly new to both frameworks, so I'm not sure if this is even the best way to go about doing this.
Brandon's right, use = in your isolate scope to get the two-way binding, but there's one more thing you have to do: remove the curly braces from around your ng-model declarations.
ng-model declarations take an angular expression and evaluate them in the scope, so there's no need for the braces to force evaluation. In fact, the braces will mess things up.
// so just
ng-model="valvar"
Here's a fiddle with your code with these changes.
To get bi-directional binding in your isolate scope, use =:
scope: {
valvar: '=qtnVariable',
},
# makes the resulting scope variable a string with interpolation.