I am following a course on Angular and as a complete newbie I have a newbie question to ask about custom directives. I wonder how can we set new variables in that custom directive and access them in our view, is that even possible, if somebody could explain that in a clear way?
For example:
myApp.controller('mainController', ['$scope', '$log', function($scope, $log) {
$scope.person = {
name: 'John Doe',
address: '555 Main St., New York, NY 11111'
}
}]);
myApp.directive("searchResult", function() {
return {
restrict: 'AECM',
templateUrl: 'directives/searchresult.html',
replace: true,
scope: {
personName: "#",
personAddress: "#",
newVariable: "someValue"
}
}
});
searchresult.html
<a href="#" class="list-group-item">
<h4 class="list-group-item-heading">{{ personName }}</h4>
<p class="list-group-item-text">
{{ personAddress }}
</p>
<p class="list-group-item-text">
{{ newVariable }}
</p>
main.html
<label>Search</label>
<input type="text" value="Doe" />
<h3>Search Results</h3>
<div class="list-group">
<search-result person-name="{{ person.name }}" person-address="{{ person.address }}" newVariable="{}"></search-result>
use link function for local scope
myApp.directive("searchResult", function() {
return {
restrict: 'AECM',
templateUrl: 'directives/searchresult.html',
replace: true,
scope: {
personName: "#",
personAddress: "#"
},
link: function(scope, elem, attr) { scope.newVariable='something'; },
};
});
Use This.
var app = angular.module("test",[]);
app.controller("Ctrl1",function($scope){ $scope.name = "Abc"; $scope.reverseName = function(){
$scope.name = $scope.name.split('').reverse().join(''); }; });
app.directive("myDirective", function(){ return {
restrict: "EA",
scope: false,
template: "<div>Your name is : {{name}}</div>"+
"Change your name : <input type='`enter code here`text' ng-model='name' />" }; });
Related
1. Directive:
app.directive('inputField', function() {
return {
restrict: 'E',
require: 'ngModel',
scope: {
words: '=ngModel'
},
transclude: true,
template: "<input type='text' ng-model='words' placeholder='Translate' />"
};
});
2. ng-click function:
$scope.clear = function() {
$scope.words = { word: '' };
};
3. View looks like this:
<input-field id='inputWord' value='' name="data1" ng-model="words.word"></input-field>
After click clear() {{words.word}} and value in input still exist and $scope is broken.
Please tell me how I can clear all inputs ng-repeat and update scope?
Try like this.
var app = angular.module('app',[])
app.controller('ctrl',function($scope){
$scope.words = [ {word : 'input1'}, {word : 'input2'}, {word : 'input3'}];
$scope.clear = function() {
$scope.words =[ {word : ''}, {word : ''}, {word : ''}];
};
});
app.directive('inputField', function() {
return {
restrict: 'E',
require: 'ngModel',
scope: {
words: '=ngModel'
},
transclude: true,
template: "<input type='text' ng-model='words' placeholder='Translate' />"
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="word in words">
<input-field id='inputWord' name="data1" ng-model="word.word"></input-field>
</div>
<button ng-click="clear()">Clear</button>
</div>
I'm creating a directive with ng-model. So I can pass the model to these directives.
Here is what I think about it:
app.js
app.directive('testDir', function(){
return {
templateUrl: 'assets/views/dir.html',
restrict: 'E',
required: 'ngModel'
};
});
dir.html
<div>
<h1>Test directive</h1>
<h3>{{name}}</h3>
</div>
index.html
<div class="container" ng-controller="testCtrl">
<test-dir ng-model="user"></test-dir>
</div>
and the contoller
$scope.user = {
name: 'John Doe'
};
I can see the <h1> tag with Test directive text but nothing in the <h3>
tag
I know it is a very beginner problem, but right know I can't find any solution.
Thank you!
The syntax for scope was missing. Please see a working example below
var app = angular.module("sa", []);
app.controller("testCtrl", function($scope) {
$scope.user = {
name: 'John Doe'
};
});
app.directive('testDir', function() {
return {
//templateUrl: 'assets/views/dir.html',
template: '<div>' +
'<h1>Test directive</h1>' +
'<h3>{{fooModel.name}}</h3>' +
'</div>',
restrict: 'E',
required: 'ngModel',
scope: {
fooModel: '=ngModel'
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="sa" class="container" ng-controller="testCtrl">
<test-dir ng-model="user"></test-dir>
</div>
Try This, you need to create a scope variable for ng-model
app.directive('testDir', function(){
return {
templateUrl: 'assets/views/dir.html',
restrict: 'E',
required: 'ngModel',
scope: {
ngModel:'='
}
};
});
HTML
<div>
<h1>Test directive</h1>
<h3>{{ngModel}}</h3>
</div>
Hey Stackoverflow peep.
This works:
angular.module('demoApp', [])
.controller('MainController', function($scope) {
$scope.Data = {
name: "Bob"
}
}).directive("formText", function() {
return {
template: '<div><input type="text" ng-model="data" name="{{key}}" id="{{key}}"></div>',
restrict: 'EA',
scope: {
label: "#label",
key: "#key",
data: "="
}
};
});
https://jsfiddle.net/ownmph09/4/
That fiddle works. You can change controller scope value. Pretty straight forward.
But this one does not:
angular.module('demoApp', [])
.controller('MainController', function($scope) {
$scope.Data = {
name: "Bob"
}
}).directive("formText", function() {
return {
template: '<div form-input label="{{label}}"><input type="text" ng-model="data" name="{{key}}" id="{{key}}"></div>',
restrict: 'EA',
scope: {
label: "#label",
key: "#key",
data: "="
}
};
}).directive('formInput', function() {
return {
template: '<div class="form-group"><label class="col-sm-4 control-label">{{label}}</label><div class="col-sm-8" ng-transclude></div></div>',
restrict: 'EA',
transclude: true,
scope: {
label: "#label"
}
};
});
https://jsfiddle.net/ownmph09/3/
I'm sure it has to do with the transclude and isolated scopes but I don't quite understand what's going on. Hoping someone that knows this stuff better than me can help.
Please consider this Plunk.
I'm trying to set up a test case for complex directive access, but I get an error calling a method from the parent directive:
Parent directive
app.directive('topParentDirective', [
'$compile',
function($compile){
return {
restrict: 'E',
transclude: true,
template: '<h3>I\'m the parent directive.</h3><div ng-transclude></div>',
controller: function($scope) {
$scope.ActivateMe = function(callerName) {
alert('Parent activated from caller ' + callerName);
};
}
};
}
]);
Child directive
app.directive('interactingChildDirective', [
'$compile',
function($compile){
return {
scope: {
name: '#'
},
restrict: 'E',
require: ['^topParentDirective'],
templateUrl: 'interactingChildDirective.html',
link: function($scope, $elem, $attrs, $ctrl) {
var self = {};
console.log($ctrl);
$scope.CallTopParent = function() {
$ctrl.ActivateMe($attrs.name);
};
}
};
}
]);
InteractingChildDirective.html
Contains:
My name is {{name}}, <button ng-click="CallTopParent()">Click me</button>!
Html
<body ng-app="ngApp">
<div ng-controller="myController">
<top-parent-directive>
<interacting-child-directive name="Child 1"></interacting-child-directive>
</top-parent-directive>
</div>
</body>
Issue
TypeError: $ctrl.ActivateMe is not a function
at n.$scope.CallTopParent
Which is the case because $ctrl doesn't seem to be correct.
How can I fix this? It's likely something ridiculously easy ...
It should be
controller: function($scope) {
this.ActivateMe = function(callerName) {
alert('Parent activated from caller ' + callerName);
};
}
Because $ctrl gets required controller's this.
Because you have nested the child in the parents controller you can access it's scope by using
$scope.$parent
in your case:
$scope.$parent.ActivateMe($attrs.name);
Plunker: http://plnkr.co/edit/YyppT9pWnn1PFWJXBAOF?p=info
The answer by estus, combined by the comments, works. To be complete, a working sample of the scenario I was aiming for:
Plunkr sample.
Updated Html
<body ng-app="ngApp">
<div ng-controller="myController">
<top-parent-directive>
<interacting-child-directive name="Child 1">
<meaningless-level-directive header="Sub 1">
<interacting-child-directive name="Child 3"/>
</meaningless-level-directive>
</interacting-child-directive>
<interacting-child-directive name="Child 2">
<meaningless-level-directive header="Sub 2">
<interacting-child-directive name="Child 4"/>
</meaningless-level-directive>
</interacting-child-directive>
</top-parent-directive>
</div>
</body>
meaninglessLevelDirective
As the name suggests this is just to add an extra level:
app.directive('meaninglessLevelDirective', [
'$compile',
function($compile){
return {
scope: {
header: '#'
},
restrict: 'E',
transclude: true,
templateUrl: 'meaninglessLevelDirective.html',
controller: function($scope){
}
};
}
]);
meaninglessLevelDirective.html
<div class="meaninglessLevelStyle">
{{header}}
<div style="padding: 10px" ng-transclude>
</div>
</div>
I have a list of items which are a custom directive and each of those items has a remove button. Now I want to disable this remove button when there is only one item left in my list, but for some reason it doesn't work as expected.
I've made a plunker example where you an watch this behavior.
I guess there is something wrong with the canRemove: '&' part of my directive. But I don't know how to get it working.
View:
<body ng-controller="MainCtrl as vm">
<div ng-repeat="item in vm.items">
<my-directive item="item"
canRemove="vm.items.length != 1"
remove="vm.remove(item)">
</my-directive>
</div>
</body>
Controller:
app.controller('MainCtrl', function($scope) {
var vm = this;
vm.items = [
{
number: 1
} , {
number: 2
}
];
vm.remove = function(item) {
vm.items.splice(vm.items.indexOf(item), 1);
}
});
Directive:
app.directive('myDirective', function() {
return {
restrict: 'EA',
scope: {
item: '=',
canRemove: '&',
remove: '&'
},
controller: function() {
var vm = this;
vm.onRemove = function() {
vm.remove({ item: vm.item });
};
},
controllerAs: 'vm',
bindToController: true,
template: '<button ng-disabled="!vm.canRemove" ng-click="vm.onRemove()">' +
' Remove {{ vm.item.number }}' +
'</button>'
}
});
PS: Since I'm pretty new to angular is the way I'm handling the removing of the items a good practice? Or should I use broadcast and on instead?
First of all attribute should look like can-remove:
<my-directive item="item" can-remove="vm.items.length > 1" remove="vm.remove(item)"></my-directive>
Then in scope configuration you need to use = binding instead of &:
scope: {
item: '=',
canRemove: '=',
remove: '&'
},
Demo: http://plnkr.co/edit/DlZafON6HEdoyhzvwNIh?p=preview