Is there a way to use javascript to apply the ng-model directive to a created element? In the code below, I want the new select element to be bound using ng-model to a scoped variable inside the controller:
angular.module('myApp').directive('dynamicHtml', function ($q, $http, $compile) {
return {
restrict: 'EAC',
scope: '=',
compile: function(element, attr) {
return function(scope, element, attr) {
var select = document.createElement('select');
// Here I want to use javascript to apply ng-model='controllerVar'
// to the new select element
element.append(select);
}
}
};
});
Whoops - my apologies...I forgot to compile the thing...carry on.
Related
I'm trying to extend functionality of any directive by simply attaching an attribute directive, but I'm having trouble getting the scope of the element on which the attribute is defined.
For example, I have this template:
<div class="flex-item-grow flex-item flex-column report-area">
<sv-report sv-reloadable id="reportId"></sv-report>
</div>
Here, sv-reloadable has some implicit understanding of sv-report, but sv-report has no idea about sv-reloadable.
I've defined sv-reloadable as:
angular
.module( 'sv-reloadable', [
'sv.services',
])
.directive('svReloadable', function(reportServices, $timeout) {
return {
restrict: 'A',
controller: function($scope, $timeout) {
$scope.$on('parameter-changed', function(evt, payload) {
evt.stopPropagation();
$scope.viewModel = getNewViewModel(payload);/* hit the server to retrieve new data */
});
}
};
});
Now, $scope in sv-reloadable is the parent scope of sv-report. I'm wanting sv-reloadable to be able to attach a listener to sv-report's scope, and swap out properties of that scope. I understand that it's possible to grab the sibling scopes, but that causes problems when trying to figure out exactly which element it's attached to.
I attempted the following:
link: function(scope, element, attrs) {
ele = element;
var actualScopyThingy = element.scope();
},
Which I had assumed would give me the scope of the element the attribute was defined on, but alas, it still returns the parent scope of the element.
If it's important, sv-report is defined as the following, but I'd like to be able to keep it the same (since sv-reloadable is going to be attached to many different elements, all of which must have viewModel defined on their scope)
return {
restrict: 'E',
replace: true,
templateUrl: 'sv-report/sv-report.tpl.html',
scope: {
id: '=',
reportParameters: '='
},
controller: function ($scope, svAnalytics) {
/* unrelated code here */
},
link: function(scope, element, attrs) {
initialLoadReport(scope);
}
};
After a bit of digging around, isolateScope() is what I was after (rather than scope()). sv-reloadable's directive becomes:
return {
restrict: 'A',
link: function(scope, element, attrs) {
var elementScope = element.isolateScope();
elementScope.$on('parameter-changed', function(evt, payload) {
...
});
}
};
Is there a way to pass configuration object into custom directive which defined as a attribute-directive?
I've got an object in Controller that I want to send to directive:
$scope.configObject = {
count: 5,
mode: 'fast',
getData: myService.getData // function of external service that avaliable in controller
}
In my View I declare directive:
<div class='list-class' my-list='configObject'></div>
Directive looks like:
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var config = angular.fromJson(attrs.myList);
}
}
i've tried to get config object using angular.getJson - but it doesn't work for functions (it's possible to get only count and mode). Is .getJson() the incorrect way to get config?
Also (I guess it's not even possible) - is there a way to get config object avoiding accessing to
attrs.myList
directly? I mean if I change initializing of directive from
.directive('myList', function() { ... }) to
.directive('myCustomList', function() { ... })
shall I change accessing to
attrs.myCustomList
because view would look like
<div class='list-class' my-custom-list='configObject'></div>
you can pass it using isolate scope if you want
return {
restrict: 'A',
scope: { config : '=myList' }
link: function(scope, elem, attrs) {
//access using scope.config
}
}
or as already answered you can parse it from attrs
$parse(attrs["myList"])(scope);
and yes if you change the directive to myCustomList, you will have to change the code
scope: { config : '=myCustomList' }
or
$parse(attrs["myCustomList"])(scope);
You can use $parse service to fetch the config object.
(function(){
var directiveName = 'myList';
angular.module('YourModule').directive(directiveName,['$parse',function($parse){
return {
restrict: 'A',
link: function(scope, elem, attrs) {
var config = $parse(attrs[directiveName])(scope);
}
};
}]);
})();
You can $eval the attribute
link: function(scole, element, attrs) {
var config = scope.$eval(attrs['myList']);
}
I'm making a directive for a States Select in angular. It's working, but I spent a while trying to figure out a way to compile the template before it's in the DOM. It currently works like this:
app.register.directive('stateDropdown', ['StatesFactory', '$compile', function (StatesFactory, $compile) {
function getTemplate(model) {
var html = '<select ng-model="' + model + '" ng-options="state.abbreviation as state.name for state in states" class="form-control"></select>';
return html;
}
function link (scope, element, attrs) {
scope.states = StatesFactory.States;
element.html(getTemplate(attrs.stateModel));
$compile(element.contents())(scope);
}
return {
replace: true,
link: link
}
}]);
But as such it inserts the template into the element THEN compiles it against scope. Is there a better way to do this? Such as compiling the template before it's even inserted?
Scratch what I had before.
[Edit 2]
Using a dynamic model is a bit problematic trying to fit it into the normal Angular workflow.
Instead you will need to compile the template in the directive by hand but add the ng-model before doing so, You will also need to manage the replacement of the existing element with the built template.
module.directive('stateDropdown', function (StatesFactory, $compile) {
var template = '<select ng-options="state.abbreviation as state.name for state in states" class="form-control"></select>';
return {
scope: true,
controller: function($scope) {
$scope.states = StatesFactory.states;
},
compile: function($element, $attrs) {
var templateElem = angular.element(template).attr('ng-model', '$parent.' + $attrs.stateModel);
$element.after(templateElem);
$element.remove();
var subLink = $compile(templateElem);
return {
pre: function(scope, element, attrs) {
subLink(scope);
},
post: function(scope, element, attrs) {
}
}
}
};
});
A working example of this can be found here: http://jsfiddle.net/u5uz2po7/2/
The example uses an isolated scope so that applying the 'states' to the scope does not affect existing scopes. That is also the reason for the '$parent.' in the ng-model.
I want to create a new scope with this object:
$scope.model = {
itemA: "First item",
itemB: "Second item"
};
// I know, this is wrong, but I want to show you, what I would like to do.
var newScope = $scope.$new($scope.model);
The new scope I want to access in the ngTransclude-Part of my directive:
link: function (scope, element, attrs, ctrl, transclude) {
transclude(scope.model, function (clone, scope) {
element.find('section').html("").append(clone);
});
And in the template:
<p>{{itemA}} - {{itemB}}
But this doesn´t work
I have the idea from: http://angular-tips.com/blog/2014/03/transclusion-and-scopes/
but I don´t want to work in the scope of the directive, but in a new scope.
AFAIK when you are creating directive usually it inherits the scope. The idea is to create isolated scope and this is done by doing.
.directive('directiveName', function ($compile) {
return {
restrict: "AE",
scope:{
yourModelName:"=";
}
link: function (scope, element) {
var el = angular.element('<div>Here you put your template scope.yourModelName</div>');
$compile(el)(scope);
element.append(el);
}
};
})
Will be copied from the upper scope but you can change it in the directive without changing it in the upper scope
his is my solution I found to a similar problem. Slightly long-winded but it works!
Create a new, 'empty' class directive with its own scope.
Add this directive as a class attribute to your DOM element. It automatically takes the scope of the new directive.
In your case, you would use it on your p tag. You would then select this element in your link function and call scope() on it:
1. <p id="ID" class="my-empty-directive">{{itemA}} - {{itemB}}
2. create your new directive:
angular.module('sgComponents').directive('panelData', [function () {
return {
restrict: 'C', // a class Directive
scope: true // with its own scope
};
}]);
2. link: function (scope, element, attrs, ctrl, transclude) {
var pTag = jQuery('#ID');
var angularElement = angular.element(pTag);
var newScope = angularElement.scope(); // THIS WILL BE THE NEW EMPTY DIRECTIVE'S SCOPE
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