There is the following custom directive code:
angular.module('app.directives', ['ng']).directive('liveSearch', [
'$compile', '$timeout', function($compile, $timeout) {
return {
restrict: 'E',
replace: true,
require: 'ngModel',
scope: {
ngModel: '=',
liveSearchCallback: '=',
liveSearchSelect: '=?',
liveSearchItemTemplate: '#',
liveSearchWaitTimeout: '=?',
liveSearchMaxResultSize: '=?',
liveSearchResultField: '=?'
},
template: "<input type='text' />",
link: function(scope, element, attrs, controller) {
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
scope.ngModel = '10';
element.val(item[attrs.liveSearchResultField]
}
});
}
}}]);
It's not a complete code of my directive, but it's enough to understand the problem. Also view code:
<live-search class="form-control" id="search1" live-search-callback="mySearchCallback" live-search-result-field="title"ng-model="skill" type="text"></live-search>
Value: {{ skill }}
<button class="btn btn-success" type="submit" ng-click="createEmployee">Сохранить</button>
My controller code:
$scope.createEmployee = function() {
console.log($scope.skill);
};
As you can see my custom directive has 'ng-model' attribute with name of variable 'skill'. How can I change 'skill' value in my custom directive? My way doesn't work. Thanks in advance!
In your link: try using
link:function(scope, element, attrs, ngModel){
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
ngModel.$setViewValue(10);
ngModel.$render() // will update the input value as well
element.val(item[attrs.liveSearchResultField];
}
});
}
also it seems you have missed space between to separate ng-model attribute in your HTML
<live-search class="form-control" id="search1" live-search-callback="mySearchCallback" live-search-result-field="title" ng-model="skill" type="text"></live-search>
See the documentation here
When you use require: 'ngModel', the 4th parameter passed to your link function is ngModelController. Use method $setViewValue to update the value of ngModel passed to your directive, then call $render() if your view needs to be updated as well.
link: function(scope, element, attrs, ngModelController) {
scope.$watch('selectedIndex', function(newValue, oldValue) {
var item;
item = scope.results[newValue];
if (item) {
ngModelController.$setViewValue(10);
ngModelController.$render();
element.val(item[attrs.liveSearchResultField]
}
});
}
Related
I'm using compile in my directive controller to get the first directive element and compile it and then use it for other purpose I don't want to use the linking method of my directive, is there anyway to get rid this error ?
I've reproduced the issue in this JSFIDDLE:
var app = angular.module('app', []);
app.directive('panel', function ($compile) {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<div><h1>handrouss</h1><div ng-transclude ></div></div>",
controller: function($scope, $element) {
var el = $compile($element.find('div')[0])($scope); // <--- this causing the issue
$scope.handrouss = el.html();
},
link: function (scope, elem, attrs) {
}
}
});
app.directive('panel1', function ($compile) {
return {
restrict: "E",
replace:true,
transclude: true,
template:"<div ng-transclude></div>",
link: function (scope, elem, attrs) {
elem.children().wrap("<div>");
}
}
});
HTML :
<div data-ng-app="app">
<panel1>
<panel>
<input type="text" ng-model="firstName" />{{firstName}}
</panel>
<input type="text" ng-model="lastname" />
</panel
Remove the ng-transclude attribute from the element before compiling in the controller.
app.directive('panel', function ($compile) {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<div><h1>handrouss</h1><div ng-transclude ></div></div>",
controller: function($scope, $element) {
var div = $element.find('div');
//REMOVE ng-transclude attribute
div.removeAttr('ng-transclude');
var el = $compile(div[0])($scope);
$scope.handrouss = el.html();
},
link: function (scope, elem, attrs) {
}
}
});
As the transclusion has already been done in the compile phase of the directive the ng-transclude directive is no longer needed when compiling in the controller.
The DEMO on JSFiddle
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>
I have custom directive and want to send some parameters(objects) to this directive.
In directive I have select list based on one of this parameters and I need to follow this parameter.
My directive:
<custom-directive rows="users">
</custom-directive>
and template:
<ui-select ng-model="value">
<ui-select-match allow-clear="true">
{{$select.selected}}
</ui-select-match>
<ui-select-choices repeat="row in rows | filter: $select.search">
<div>
<span ng-bind-html="row | highlight: $select.search"></span>
</div>
</ui-select-choices>
</ui-select>
normally if rows change ui-select will automatically update select list. But if rows is variable from outside of this directive, it wont.
Can I have the same variable outside and inside directive?
I think I can use ng-model, but I'm not sure it is good solution.
EDIT:
My directive code:
.directive('custom-directive', function () {
return {
restrict: 'E',
replace: true,
scope: {
rows: '='
},
templateUrl: 'template.html',
controller: ['$scope', function($scope) {
console.log($scope);
}]
};
})
You can modify your custom directive to work with ng-model in the following way:
directive('customDirective', function() {
return {
// ...
require: '?ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
// Write data to the model
// Call it when necessary
scope.setValue = function(value) {
ngModel.$setViewValue(value);
}
}
};
});
Initial state of ng-model value can be retrieved using that method:
ngModel.$render = function() {
var value = ngModel.$viewValue || [];
};
You can use link instead of the controller to handle functionality with rows. Setting rows : '=' should already take care of the two-way binding.
.directive('custom-directive', function () {
return {
restrict: 'E',
replace: true,
scope: {
rows: '='
},
templateUrl: 'template.html',
link: function(scope, element, attr) {
console.log(scope.rows);
};
};
})
EDIT:
Add a watch to the value that you need to do behavior on external change.
.directive('custom-directive', function () {
return {
restrict: 'E',
replace: true,
scope: {
rows: '='
},
templateUrl: 'template.html',
link: function(scope, element, attr) {
scope.$watch('rows', function(oldValue, newValue) {
console.log('This is the newValue : ' + newValue);
};
};
};
})
I want to create a directive that be dynamic. In this directive define a template that have an input element. In fact this element ng-model must be dynamic, and use $scope.name in controller.
app.directive('helloWorld', function() {
return {
restrict: 'E',
replace: true,
scope: {
name: '#',
path:'#',
},
template: '<input\
type="text"\
name="{{name}}"\
ng-model="{{name}}"\
/>\,
link: function(scope, elem, attrs) {
},
controller:{
$scope.$watch($scope.name, function (newValue, oldValue) {
}
}
});
A working JSFiddle
Code
var app = angular.module('app',[])
app.directive('helloWorld', function() {
return {
restrict: 'E',
scope: {
name: '#',
path:'#',
},
template: '<input type="text" name="{{name}}" ng-model="name"/> {{name}}',
controller: function($scope) {
$scope.name = "initial value";
$scope.$watch('name', function (newValue, oldValue) {
console.log("newValue: ",newValue);
})
}
}
});
Firstly, your directive syntax is wrong, here is the right one:
app.directive('helloWorld', function() {
return {
restrict: 'E',
scope: {
name: '#',
path:'#',
},
template: '<input type="text" name="{{name}}" ng-model="name">',
link: function(scope, elem, attrs) {
},
controller: function($scope) {
$scope.name = 'asd';
$scope.$watch('name', function (newValue, oldValue) {
})
}
}
});
Secondly, if you are looking to have a dynamic model, you should use scope: {name: '='} as # is for one-time binding
edit
changed name="name in template to name="{{name}}"
And here is a demo
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.