I want to have an attribute directive a bit similar to ng-model. I just want to additionally bind an input fields value to a scope variable (just in one direction input field -> scope variable). So I have just tried this directive but I can not get the directive called anyway.
script:
.directive('passivemodel', function () {
return {
restrict: "A",
scope: {
passivemodel: '='
},
link: function (scope, element, attrs) {
scope.$watch('passivemodel', function(newPassivemodel, oldPassivemodel) {
console.log("passive model", newPassivemodel);
});
}
};
})
html:
<input data-passivemodel="keyword">
Edit:
Hmmm .. based on vilo20 answer I am running into a very strange behavior.
while this code is working very well:
<input data-ng-model="keyword" data-passivemodel="keyword">
this one does not (note the value of passivemodel):
<input data-ng-model="keyword" data-passivemodel="keyword2">. Sure I have defined the variable in the controller.
Controller:
.controller('SearchController', function($scope, $routeParams, $search) {
$scope.search = new $search();
$scope.keyword = "";
$scope.keyword2 = "";
})
Edit2: here is a fiddle http://jsfiddle.net/HB7LU/12107/
try this:
.directive('passivemodel', function () {
return {
restrict: "A",
scope: {
passivemodel: '='
},
link: function (scope, element, attrs) {
console.log("passive model", scope.passivemodel);
$scope.$watch('passivemodel', function(newPassivemodel, oldPassivemodel) {
//put your logic when passivemodel changed
});
}
};
})
Hope it helps
Edit: Here is a plunker http://plnkr.co/edit/T039I02ai5rBbiTAHfzv?p=preview
Use the scope attribute:
.directive('passivemodel', function () {
return {
restrict: "A",
scope: {
"passivemodel": "="
},
link: function (scope, element, attrs) {
console.log("access passivemodel: ", scope.passivemodel);
}
};
})
Finally it was as simple as that:
.directive('modelRed', [function(){
return {
require: 'ngModel',
restrict: 'A',
scope: {},
link: function (scope, element, attrs, ngModel) {
scope.$watch(function () {
return ngModel.$modelValue;
}, function(newValue) {
scope.$parent[attrs.modelRed] = newValue;
//console.log(attrs.modelRed, newValue, scope);
});
}
}
}])
And in the html:
<p><input type="text" data-ng-model="testInput" data-model-red="redInput">{{redInput}}</p>
Of course you have to define testInput and redInput in the $scope object.
Related
How can I pass a child attribute directive's scope or attr value to a parent directive?
Given widget directive, with in-viewport attribute directive, I want to update the attribute inView each time the document is scrolled, and pass the updated value to the parent directive widget:
<widget in-viewport></widget>
In Viewport directive: passed in as an attribute of parent directive "widget"
angular.module('app').directive('inViewport', function() {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(document).on('scroll', function() {
// I've tried binding it to attr and parent scope of "widget" directive
attr.inView = isElementInViewport(element);
scope.inView = isElementInViewport(element);
});
}
};
});
Widget Directive:
angular.module('app').directive('widget', function() {
return {
restrict: 'AE',
scope: {
inView: '='
},
transclude: false,
templateUrl: 'directives/widgets/widgets.tpl.html',
link: function(scope) {
console.log('In Viewport: ', scope.inView); // Null
Here is the way you can access parent directive variables,
angular.module('myApp', []).directive('widget', function() {
return {
restrict: 'E',
template: '<viewport in-view="variable"></viewport> <h1>{{variable}}</h1>',
link: function(scope, iAttrs) {
scope.variable = 10;
}
}
}).directive('viewport', function() {
return {
restrict: 'E',
scope: {
inView: "=",
},
template: '<button ng-click="click()">Directive</button>',
link: function(scope, iElement, iAttrs) {
scope.click = function() {
scope.inView++;
}
}
}
});
HTML
<div ng-app="myApp" ng-controller="Ctrl1">
<widget></widget>
</div>
Here is the working jsfiddle
http://jsfiddle.net/p75DS/784/
If you have any question, ask in the comment box
Here is a working fiddle using your directive structure:
http://jsfiddle.net/ADukg/9591/
Markup is like this:
<div ng-controller="MyCtrl" style="height: 1200px;">
{{name}}
<hr>
<widget in-viewport></widget>
</div>
Just scroll the window to trigger the event. Note that the parent directive has a watch just to prove that the var gets updated...
var myApp = angular.module('myApp',[]);
myApp.directive('inViewport', function($timeout) {
return {
restrict: 'A',
scope: false, // ensure scope is same as parents
link: function(scope, element, attr) {
angular.element(window).bind('scroll', function() {
console.log('Called');
$timeout(function() {
scope.inView++;
}, 0);
});
}
};
});
myApp.directive('widget', function() {
return {
restrict: 'AE',
transclude: false,
template: '<p>This is a widget</p>',
link: function(scope) {
scope.inView = 0;
console.log('In Viewport: ', scope.inView); // Null
scope.$watch('inView', function(newVal, oldVal) {
console.log('Updated by the child directive: ', scope.inView);
});
}
}
});
function MyCtrl($scope) {
$scope.name = 'Angular Directive Stuff';
}
You can expose an API on your parent directive and use isolateScope() to access it.
Here's a working fiddle.
var app = angular.module("app",[]);
app.directive("widget", function($rootScope){
return {
template: "<div>Scroll this page and widget will update. Scroll Y: {{scrollPosY}}</div>",
scope: {}, // <-- Creating isolate scope on <widget>. This is REQUIRED.
controller: ['$scope', function DirContainerController($scope) {
$scope.scrollPosY = 0;
// Creating an update function.
$scope.update = function(position) {
$scope.scrollPosY = position;
$scope.$digest();
};
}],
}
});
app.directive("inViewport", function($window, $timeout, $rootScope){
return {
restrict: 'A',
link:function(scope, element, attrs, parentCtrl){
// Get the scope. This can be any directive.
var parentScope = element.isolateScope();
angular.element(document).on('scroll', function() {
// As long as the parent directive implements an 'update()' function this will work.
parentScope.update($window.scrollY);
console.log('parentScope: ', parentScope);
});
}
}
});
I've created some basic directive. It works well if I use it with some objects in html file
<some-directive some-data="123"></some-directive>
But if I dynamically load this object to my webpage:
//getting html source as a string, then appending it to DOM:
elem.html("<some-directive some-data='123'></some-directive>");
The directive doesn't work (object is being added properly to DOM)
app.directive('someDirective', function (notes, parts) {
return {
restrict: 'AE',
scope: {
someData: '='
},
link: function (scope, elem, attrs) {
console.log("directive fired");
}
};
});
What can I do to make it work properly?
For dynamic directives, you have to use $compile service that compiles scope into template. Look at sample below, <some-directive-wrapper /> will add <some-directive /> element into itself and compile scope value
var app = angular.module('app', []);
app.directive('someDirective', function () {
return {
restrict: 'AE',
scope: {
someData: '='
},
template: '<h2>someDirective compiled, someData is {{someData}}</h2>',
link: function (scope, elem, attrs) {
console.log("directive fired");
}
};
});
app.directive('someDirectiveWrapper', function ($compile) {
return {
restrict: 'AE',
link: function (scope, elem, attrs) {
//get value from ajax maybe
//and set to scope
scope.data = 123;
//replace directive with another directive
elem.html('<h1>someDirectiveWrapper compiled </h1>\n<some-directive some-data="data"></some-directive>');
//compile scope value into template
$compile(elem.contents())(scope);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<some-directive-wrapper></some-directive-wrapper>
</div>
let assume that I want to create directive that matched only for element that match amInput[type=dropdown] how can I do that?
I can for example:
.directive('amInput',function () {
return {
restrict: "E",
scope: {
data:'#'
},
compile:function(tElement, tAttrs){
if (tAttrs.type != 'dropdown') return;
return function link ($scope, element, attr, ctrl) {
var parseResult = parse($scope.data);
}
}
}
});
but if I define another directive with isolate scope for am-input[type=checkbox]
.directive('amInput',function () {
return {
restrict: "E",
scope: {
data2:'#'
},
compile:function(tElement, tAttrs){
if (tAttrs.type != 'checkbox') return;
return function link ($scope, element, attr, ctrl) {
var parseResult = parse($scope.data2);
}
}
}
});
angular#$compile throw exception about two directives define isolate scope.
Error: [$compile:multidir] Multiple directives [amInput, amInput] asking for new/isolated scope on: <am-input type="checkbox"></am-input>
directive name should be unique (as long as they match the same restrict)
in your case you should probably merge them into one.
(just as a reference, this might help: Angular directive name: only lower case letters allowed?)
Ok, I end up with that solution that make the differntes between the directives by postlink. when prelink is for everythig is similar between them:
directive('amInput',function () {
var template = {
'dropdown' :require('./dropdown.drv.html'),
'checkbox-group' :require('./checkbox-group.drv.html')
};
var postLinkPerType = {
'dropdown': require('./postLink-OneFromMany'),
'checkbox-group':require('./postLink-ManyFromMany')
};
return {
restrict: "E",
require: 'ngModel',
scope:{
selectAll:'='
//,...
},
transclude:true,
controller: function(scope, element, attr) {
/*for binding stuff that use by post links */
},
controllerAs:'inputCtrl',
bindToController:false,
template: function (element, attr) {
return template[attr.type];
},
compile: function (element, attr, ctrl, transcludeFn) {
return {
pre: function (scope, element, attr, ctrl, transcludeFn) {
/*******************************************/
/* some general/init code for all collection*/
/*******************************************/
/* init options list */
if( attr.data ){
/***********/
/* some code just if there is some attribute*/
/***********/
}
},
/*the little different code that make every directive in collection different*/
/*different by TYPE attribute*/
post: postLinkPerType[attr.type]
}
}
}
})
I am new to parsers and formatters. I have a directive that will be doing validation on change of the model.One way to do this is the $watch but from what I understand that is not a good way since it allows the model to be updated.
So I was looking at parsers and tried this code
app.directive('myDirective', function($compile) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
},
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
ctrl.$formatters.push(function(value) {
console.log("hello1");
return value;
});
ctrl.$parsers.unshift(function(value) {
debugger;
console.log("hello");
return value;
});
}
};
});
But the parser function is never called. The formatter is called once. Please see the plunkr .Can anyone tell me what I am doing wrong ,why is the parser function not getting called when i type in the textbox ?
This is a late response, but for reference:
I think you where missing the "glue" that will call the $parsers while ui changes occurs. This should be something like:
app.directive('myDirective', function($compile) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
},
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
ctrl.$formatters.push(function(value) {
console.log("hello1");
return value;
});
ctrl.$parsers.unshift(function(value) {
return value;
});
scope.$watch('directive model here', function() {
ctrl.$setViewValue(<build model from view here>);
});
}
};
});
For a full reference please see this (awesome) post.
Your link function is not called because the associated DOM element is not changing, just the model is. This works:
HTML:
This scope value <input ng-model="name" my-directive>
JS:
app.directive('myDirective', function($compile) {
return {
require: 'ngModel',
link: function($scope, elem, attr, ctrl) {
ctrl.$parsers.unshift(function(value) {
console.log("hello");
});
}
};
});
See here for more on when the link function is called.
how do I share a method between two directives? When I try it now the console says scope.triggerInput(); is undefined; If I replace the scope.triggerinput with just a string it works. So I guess I do it somewhere wrong in the attribute.
app.directive('myImage', function () {
return{
restrict: 'A',
template: ' <img ng-src="images/needed/noPhoto.png"class="hover">',
link: function (scope, element, attr) {
element.bind('click', function () {
scope.triggerInput();
});
}
};
});
app.directive('myFileInput', function (httpPostFactory) {
return{
restrict: 'A',
scope: {
triggerInput: '='
},
link: function (scope, element, attr) {
scope.triggerInput = function () {
console.log('triggerInput');
element[0].click();
};
}
};
});
html
<div data-my-image></div>
<input data-my-file-input type="file" name="file" triggerInput='triggerInput()'>
Very good question! If I understand your situation correctly, Angular Service is what you need to use for situations like these.
app.service('clickResponder',function(){
var srvc={};
srvc.triggerInput=function(){
// Do your stuff here.
}
return srvc;
}
Now, to use this service, its very simple just inject it on your directive like below:
app.directive('myImage', function (clickResponder) {
return{
restrict: 'A',
template: ' <img ng-src="images/needed/noPhoto.png"class="hover">',
link: function (scope, element, attr) {
element.bind('click', function () {
clickResponder.triggerInput();
});
}
};
});
Thats all you need to do. Hope this helps you in some way!