I created a directive which has a directive inside it, and when I try to give it parameters I get this message: This error message
Here is my directive
app.directive('percentageSquare', function(){
return {
restrict: 'E',
scope: {
color: '#',
chartConfig: '#'
},
templateUrl: '/templates/dashboard/charts/PercentageChart.html'
};
});
Which loads this template:
<div class="drop-shadow">
<highchart id="{{chartConfig}}" config="{{chartConfig}}" style="background-color: {{color}};" ng-show="true"></highchart>
</div>
Here is how I am calling it:
<percentage-square color="yellow" chart-config="chartBounceRate"></percentage-square>
I am not sure what I can do to fix it, because it look just fine to me...
The way highcharts works, it want to have config to be an object from scope. so you should use two way binding here
Directive
scope: {
color: '#',
chartConfig: '=', //<--two way binding here
selector: '#'
},
Directive Use
<percentage-square color="'yellow'" chart-config="chartBounceRate" selector="{{selector}}"></percentage-square>
Template
<div class="drop-shadow">
<highchart id="{{selector}}" config="chartConfig"
ng-style="{background-color: color}" ng-show="true">
</highchart>
</div>
EDIT
See this famous stackoverflow question and answer to understand #, & and = in angularjs directive definition
Related
We created some directives to speed up our coding and clean the htmls, for example:
<div class="form-group">
<label class="form-label">{{label}}
<input ng-disabled="ngDisabled" ng-model="ngModel" type="text" class="form-control"/>
</label>
<div ng-if="invalidMessage" class="error">{{invalidMessage}}</div>
</div>
and the js
angular.module('ui.default-input', [])
.directive('defaultInput', function(){
return {
restrict: 'E',
templateUrl: '_component.html',
scope: {
label: '#',
invalidMessage: '#',
ngModel: '=',
ngDisabled: '='
}
}
});
So we can use it anywhere like
<default-input label="im a label" ng-model="vm.model"></default-input>
The problem is, whenever we want to add another directive to the input, we have to defined it in the directive own scope and manually apply it. (Like we did with ngDisabled and ngModel).
Is there a smarter solution? Is it wrong to use directives in that way?
Thanks in advance.
I started to learn Angular not so long time ago and I'm trying to understand scope, binding and etc.
I have an order details controller:
orderApp.controller('OrderDetailsController', ['$http','$routeParams','$scope','config', function($http, $routeParams, $scope, config){
var orderCtrl = this;
orderCtrl.orderId = $routeParams.orderId;
orderCtrl.order = {};
orderCtrl.editingView = false;
...
}]);
On order details page I want to output all information about selected order. Also we need to give user ability to edit order. Information about editing mode is stored in orderCtrl.editingView.
I decided to create custom directive. If edit mode is off - display text, otherwise display input.
orderApp.directive('editableText', function(){
return {
restrict: 'E',
scope: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
This is template:
<div class="col-xs-8" ng-if="!editMode">{{property}}</div>
<div class="col-xs-8" ng-if="editMode"><input type="text" class="form-control" ng-model="property"></div>
And this is how I use directive in html files:
<editable-text property="orderCtrl.order.coid" edit-mode="orderCtrl.editingView"></editable-text>
Text and input are switching when edit mode is on/off. Problem is that orderCtrl.order.coid property is not updated when I change it in input.
Before edit property looks like:
Turn on edit mode and change value:
Turn off edit mode and we see old value:
Do I need to synchronise controller values and directive scope? I thought that with 2-ways binding it should happen automatically. Probably there is any other way to write this functionality? Will appreciate any help.
UPD
Directive code:
orderApp.directive('editableText', function(){
return {
restrict: 'E',
bindToController: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
Directive template:
<div class="col-xs-8" ng-if="!orderCtrl.editMode">{{orderCtrl.property}}</div>
<div class="col-xs-8" ng-if="orderCtrl.editMode"><input type="text" class="form-control" ng-model="orderCtrl.property"/></div>
Directive usage:
<editable-text property="orderCtrl.order.coid" edit-mode="orderCtrl.editingView"></editable-text>
I'm not sure that we really need to pass edit-mode attribute.
You should use bindToController: { ..scope properties.. } option here inside your directive to make sure that isolated scope properties should get bounded to controller this context.
Directive
orderApp.directive('editableText', function(){
return {
restrict: 'E',
bindToController: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
Template
<div class="col-xs-8" ng-if="!orderCtrl.editMode">
{{orderCtrl.property}}
</div>
<div class="col-xs-8" ng-if="orderCtrl.editMode">
<input type="text" class="form-control" ng-model="orderCtrl.property"/>
</div>
Note:- this above bindToController: { ..scope properties.. } option available for angular 1.4+ versions.
For Angular 1.3 > version & 1.4 > version you should use former way of doing it by having bindingToController: true to bind scope variable to controller context & do keep the varaibles inside scope: { ...props... }
scope: {
property: '=property',
editMode: '=editMode'
},
bindToController: true
I'm looking for an answer to this question:
Multi State button like toggle in angularJS
...but using a directive. The main reason being that I'm trying to achieve isolate scope in order to create a reusable button. I have tried:
angular.module('myApp', [])
.directive('buttonToggle', function() {
return {
restrict: 'A',
scope: {
myBtnArr: "="
},
myBtnTxt: ["AND", "OR", "NOT"],
template: '<button>{{ myBtnTxt[myBtnArr] }} </button>'
}
});
With something like this in the HTML:
<div button-toggle my-btn-arr=0></div>
But Angular doesn't seem to like any flavor of this, either showing the button but not the text or throwing the cryptic a.match is not a function error. Thoughts?
You need to modify your directive to include a link function. Then place myBtnTxt on scope in there. Like so:
app.directive('buttonToggle', function() {
return {
restrict: 'A',
scope: {
myBtnArr: "="
},
template: '<button>{{myBtnTxt[myBtnArr]}}</button>',
link: function(scope){
scope.myBtnTxt = ["AND", "OR", "NOT"];
}
};
});
So I'm struggling with passing the scope to my custom directives.
Here's my html
<li ng-repeat="post in posts | filter:search | orderBy:sortField:reverse" class="archives-listing" ng-class="{'last-border':$last}">
<archive-notes></archive-notes>
</li>
Here's my directives
app.directive('archiveNotes', function(){
return {
restrict: 'E',
transclude: true,
scope: {
notes: '#',
paths: '#'
},
controller: function($scope) {
},
templateUrl: '/wp-content/themes/twentythirteen/js/angular/templates/notes.html'
}
})
app.directive('archiveFolders', function(){
return {
require: '^archiveNotes',
restrict: 'E',
transclude: true,
scope: {
path: '#'
},
link: function(scope, element, attrs) {
},
templateUrl: '/wp-content/themes/twentythirteen/js/angular/templates/folders.html'
}
});
here are my templates.
notes.html
<div ng-class="{'found' : find(post.paths[$index], search)}" class="arch-notes">
<div ng-bind-html="is_NotesEmpty(post.notes)">{{post.notes}}</div>
<archive-folders></archive-folders>
</div>
folders.html
<div ng-repeat="path in post.paths | filter:search track by $index" ng-transclude>
<span class="arch-paths">{{path}}</span>
</div>
I left several things blank i.e controller and link because at this point i was just trying to figure how to make everything show up first before i start manipulating the DOM
I followed the example in the angularjs documentation, and it got me this far. I guess cant seem to figure out how to access the scope?
Any help is appreciated.
Based on your templates, it seems that your archiveNotes directive definition should actually look like this:
app.directive('archiveNotes', function(){
return {
...
scope: {
post: '='
},
...
}
})
To get the post variable passed in from ng-repeat's scope, you also need to set the post attribute on the directive's element:
<archive-notes post="post"></archive-notes>
Similarly, you need to set it on the child directive:
app.directive('archiveFolders', function(){
return {
...
scope: {
post: '='
},
...
}
});
... and change your notes.html template:
<archive-folders post="post"></archive-folders>
Isolate scope is sort of like a firewall, where you can set exceptions, in this case on specific scope variables. All we're doing here is setting those exceptions in the directive definitions, then passing them through using the attribute on the elements.
These videos by John Lindquist really shed some light on isolate scope for me:
Understanding Isolate Scope
Isolate Scope '#'
Isolate Scope '='
Isolate Scope '&'
Here's my directive:
app.directive("helloWorld", function() {
return {
restrict: "E",
scope: {
name: "bind"
},
template: "<div>a {{name}} a</div>"
};
});
Here's how I use it:
<hello-world name="John Smith"></hello-world>
I expect this page to be like this, when I run it:
<hello-world>
<div>a John Smith a</div>
</hello-world>
But for some reason, name is not injected and actual result is like this:
<hello-world>
<div>a {{name}} a</div>
</hello-world>
Anything I'm missing? I'm using Angular JS 1.0.2
The scope declaration is strange. I'm not sure about the "bind" declaration - maybe its something from the previous versions.
The current syntax for binding to a directive's attribute is like this:
return {
restrict: "E",
scope: {
name: "#name"
},
template: "<div>a {{name}} a</div>"
};
In general, #attributeName. See here for more information on directives.