In the following AngularJS code, when you type stuff into the input field, I was expecting the div below the input to update with what is typed in, but it doesn't. Any reason why?:
html
<div ng-app="myApp">
<input type="text" ng-model="city" placeholder="Enter a city" />
<div ng-sparkline ng-model="city" ></div>
</div>
javascript
var app = angular.module('myApp', []);
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngModel',
template: '<div class="sparkline"><h4>Weather for {{ngModel}}</h4></div>'
}
});
http://jsfiddle.net/AndroidDev/vT6tQ/12/
Add ngModel to the scope as mentioned below -
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngModel',
scope: {
ngModel: '='
},
template: '<div class="sparkline"><h4>Weather for {{ngModel}}</h4></div>'
}
});
Updated Fiddle
It should be
template: '<div class="sparkline"><h4>Weather for {{city}}</h4></div>'
since you are binding the model to city
JSFiddle
The basic issue with this code is you aren't sharing "ngModel" with the directive (which creates a new scope). That said, this could be easier to read by using the attributes and link function. Making these changes I ended up with:
HTML
<div ng-sparkline="city" ></div>
Javascript
app.directive('ngSparkline', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var newElement = '<div class="sparkline"><h4>Weather for {{' + attrs.ngSparkline + '}}</h4></div>';
element.append(angular.element($compile(newElement)(scope)));
}
}
});
Using this pattern you can include any dynamic html or angular code you want in your directive and it will be compiled with the $compile service. That means you don't need to use the scope property - variables are inherited "automatically"!
Hope that helps!
See the fiddle: http://jsfiddle.net/8RVYD/1/
template: '<div class="sparkline"><h4>Weather for {{city}}</h4></div>'
the issue is that require option means that ngSparkline directive expects ngModel directive controller as its link function 4th parameter. your directive can be modified like this:
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngModel',
template: '<div class="sparkline"><h4>Weather for {{someModel}}</h4></div>',
link: function(scope, element, attrs, controller) {
controller.$render = function() {
scope.someModel = controller.$viewValue;
}
}
}
});
but this creates someModel variable in scope. that I think isn't necessary for this use case.
fiddle
Related
I have the following problem:
I have a custom directive, which shows one or more tables, by using ng-repeat inside the template string. Inside each table, several other custom directives are placed. I want these to know the index of the element used, but cant manage to get this done. My code looks like this now:
.directive('myStuffText', function ($rootScope){
return {
restrict: 'A',
require: '^form',
replace: true,
scope: true,
template:
......
'<table border="1" ng-repeat="elt in myModel.newStuffList">
......
'<tr>' +
'<td colspan="3"><div my-add-some-editor my-element-index="$index"/></td>' +
'</tr>'
'</table>',
link: function (scope, elt, attrs){
scope.cockpitPolicyModel.newPolicyList = [];
}
};
})
Independently from how I try, I always get the string $index or {{$index}} in the template function of the my-add-some-editor directive, not the value of it..
Edit - added the nested directive:
.directive('myAddSomeEditor', function($rootScope){
return {
restrict: 'A',
require: '^form',
scope: true,
template: function ($scope, $attr){
return
.....
'<span id="myAddSomeEditor" name="myAddSomeEditor" class="form-control" my-generic-editor '+
'my-input-mapping="myModel.someText"></span>'
.....
;
}
};
})
That probably happens because in your my-add-some-editor directive you have this definition in the isolate scope:
myElementIndex: '#'
That's why you're getting the literal string of what you're writing there in the HTML.
Change that to:
myElementIndex: '='
EDIT: Since you said you're not using isolated scope, try this in the parent directive: try doing my-element-index="{{$index}}". And this in the child directive's link function:
link: function (scope, elem, attr) {
attr.$observe('index', function(val) {
scope.index = val;
});
}
I'm new to angular. I want to write a directive which has all the attributes that I added to it when using in html. For example:
This is my directive
'use strict';
app.directive('province', function($compile) {
return {
restrict: 'E',
link: function (scope, element, attrs, controller) {
var markup = "<select></select>";
var elem = angular.element(element);
elem.replaceWith($compile(markup)(scope));
}
};
})
HTML:
<province class="form-control" data-target"elemntId"></province>
I want my <select> contain the class and other attributes that I added to directive in html.
output that I want: <select class="form-control" data-target="elementId"></select>
I used angular.element(element).attr(attr);, but it does not worked;
Any help is appreciated in advance.
Edit
I want all the attributes that exist in attrs of link function to be added to markup.
I would iterate over directive's attr array and apply it to your template:
app.directive('province', function($compile) {
return {
restrict: 'E',
replace:true,
template: "<select></select>",
link: function (scope, element, attrs) {
var attr;
for (attr in attrs.$attr) {
if(attrs.hasOwnProperty(attr)){
element.attr(attr, attrs[attr]);
}
}
}
};
})
Directive Tag:
<province foo="bar" foo1="bar1"></province>
Compiled into:
<select foo="bar" foo1="bar1"></select>
Plunkr
Depending on your needs, you don't need to compile yourself. You can use template and replace instead.
app.directive('province', function() {
return {
restrict: 'E',
template: '<select></select>',
replace: true,
link: function (scope, element, attrs) {
}
};
});
See plnkr
You can make use of the attrs parameter of the linking function - this will get you the values of the attributes:
attrs.class and attrs.dataTarget are the ones you need.
You can take a look at the documentation here that elaborates further uses of the linking function
How do I get the input element from within the directive before the template overwrites the contents?
html
<div xxx>
<input a="1" />
</div>
js
app.directive('xxx', function(){
return {
restrict: 'A',
template: '<p></p>',
replace: true, //if false, just leaves the parent div, still no input
compile: function(element, attrs) {
console.log(element);
return function (scope, iElement, iAttrs) {
}
}
};
});
i am on angular 1.0.x, I cannot pass in optional scope parameters with the '=?' syntax and i want to be able to override a portion of the default template of the directive in a very flexible way. instead of adding a scope variable or attribute everytime that I just plan on passing through the directive, I want to be able to supply the whole element to be used.
edit
the input must retain the scope of the directive, and not the parent.
edit
I am trying to include a partial template inside a directive that will overwrite a piece of the actual template. The piece I am including therefore needs to have access to the directive's scope and not the parent's.
Update
It seems if I do not provide a template or a template URL and instead replace the contents manually using the $templateCache I can have access to the inner elements. I want to let angular handle the template and the replacement though and just want to be able to access the contents in the directive naturally before they get replaced.
Solution
Plunkr
html
<body ng-controller="MainCtrl">
<div editable="obj.email">
<input validate-email="error message" ng-model="obj.email" name="contactEmail" type="text" />
</div>
</body>
js
app.controller('MainCtrl', function($scope) {
$scope.obj = {
email: 'xxx'
};
});
app.directive('editable', function($log){
return {
restrict: 'A',
transclude: true,
template: '<div ng-show="localScopeVar">{{value}}<div ng-transclude></div></div>',
scope: {
value: '=editable'
},
link: function(scope) {
scope.localScopeVar = true;
}
};
});
app.directive('validateEmail', function($log){
return {
restrict: 'A',
require: 'ngModel',
scope: true,
link: function(scope, el, attrs, ctrl) {
console.log(attrs['validateEmail']);
}
};
});
I believe you're looking for the transclude function (link is to 1.0.8 docs). You can see what's going on with:
app.directive('xxx', function($log){
return {
restrict: 'A',
transclude: true,
compile: function(element, attrs, transclude) {
$log.info("every instance element:", element);
return function (scope, iElement, iAttrs) {
$log.info("this instance element:", element);
transclude(scope, function(clone){
$log.info("clone:", clone);
});
}
}
};
});
I'm trying to create a custom component that uses a dynamic ng-model inside-out the directive.
As an example, I could invoke different components like:
<custom-dir ng-model="domainModel1"></custom-dir>
<custom-dir ng-model="domainModel2"></custom-dir>
With a directive like:
app.directive('customDir', function() {
return {
restrict: 'EA',
require: '^ngModel',
scope: {
ngModel: '=dirValue',
},
template: '<input ng-model="dirValue" />',
link: function(scope, element, attrs, ctrl) {
scope.dirValue = 'New';
}
};
});
The idea is that the textbox from the directive would change if the model changes, and in the other way around.
The thing is that I've tried different approaches with no success at all, you can check one of this here: http://plnkr.co/edit/7MzDJsP8ZJ59nASjz31g?p=preview In this example, I'm expecting to have the value 'New' in both of the inputs, since I'm changing the model from the directive and is a bi-directional bound (=). But somehow is not bound in the right way. :(
I will be really grateful if someone give some light on that. Thanks in advance!
Something like this?
http://jsfiddle.net/bateast/RJmhB/1/
HTML:
<body ng-app="test">
<my-dir ng-model="test"></my-dir>
<input type="text" ng-model="test"/>
</body>
JS:
angular.module('test', [])
.directive('myDir', function() {
return {
restrict: 'E',
scope: {
ngModel: '='
},
template: '<div><input type="text" ng-model="ngModel"></div>',
};
});
I have a form based on twitter bootstrap, each field have it's own configuration
// controller (the template shows this in ng-repeat
$scope.fields = [{name:"f1", label:"Field 1", with_button: false},
{name:"f2", label:"Field 2", with_button: true}]
I'm trying to make a "conditional directive" that customize the template according to "field.with_button"
// Without button
<div class="controls">
<input type="text" id="i_{{field.name}}">
</div>
// With button
<div class="controls">
<div class="input-append">
<input type="text" id="i_{{field.name}}">
<span class="add-on">bt</span>
</div>
</div>
I searched a lot and didn't find any solution, I tried to create only one div and put contents inside with a compiler function but it didn't parse, and if I call $apply it crashes.
How could I make this directive?
wrong My last try:
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls">{{innerContent}}</div>',
controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
$scope.$eval('$scope.innerContent = \'<input type="text" id="input_{{field.name}}" placeholder="{{field.name}}" class="input-xlarge">\'');
}]
};
});
//<ss-field field="{{field}}"></ss-field>
You can use the $http and $compile services to do what you're after.
http://plnkr.co/edit/Xt9khe?p=preview
This plnkr should demostrate what needs to be done, but basically:
Use $http to load the template depending on the condition.
Compile the loaded template against the current scope with $compile.
angular.module('mymodule',[]).directive('ssField', ['$http', '$compile', function($http, $compile) {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls"></div>',
link: function(scope, element, attrs) {
var template;
var withButtonTmpl = 'with_button.html';
var withoutButtonTmpl = 'without_button.html';
if (scope.field.with_button) {
$http.get(withButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
} else {
$http.get(withoutButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
}
}
};
}]);
You can change the directive to be more robust so the URLs aren't directly embedded in the directive for re-usability, etc., but the concept should be similar.
Just to further expand on Cuing Vo's answer here is something similar to what I use(without using external partials and additional $http calls):
http://jsfiddle.net/LvUdQ/
myApp.directive('myDirective',['$compile', function($compile) {
return {
restrict: 'E',
template: '<hr/>',
link: function (scope, element, attrs, ngModelCtrl) {
var template = {
'templ1':'<div>Template 1</div>',
'templ2':'<div>Template 2</div>',
'default':'<div>Template Default</div>'
};
var templateObj;
if(attrs.templateName){
templateObj = $compile(template[attrs.templateName])(scope);
}else{
templateObj = $compile(template['default'])(scope);
}
element.append(templateObj);
}
};
}]);
However Im not quite sure its by the bible from performance perspective.
In AngularJS, directly manipulate the DOM must only be a last resort solution. Here, you can simply use the ngSwitch directive :
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template:
'<div class="controls" data-ng-switch="field.with_button">' +
'<input type="text" id="i_{{field.name}}" data-ng-switch-when="false">' +
'<div class="input-append" data-ng-switch-default>' +
'<input type="text" id="i_{{field.name}}">' +
'<span class="add-on">bt</span>' +
'</div>' +
'</div>',
};
});