I woud like to reuse a form to edit one type of property. I have a list of editors:
ModelerControllers.controller('ModelController', ['$rootScope', '$scope', '$http', '$q',
function ($rootScope, $scope, $http, $q) {
...
$scope.mappingTypes = [
{"name": "mixpanel", "label": "Mixpanel"},
{"name": "mongo", "label": "Mongo"},
{"name": "slicer", "label": "Slicer"},
{"name": "sql", "label": "SQL"},
{"name": "", "label": "Other"}
];
...
}
]);
Then I have a directive:
CubesModelerApp.directive("mappingEditor", function() {
return {
templateUrl: 'views/partials/mapping.html',
require: "ngModel",
scope: {
content: "=ngModel",
mappingTypes: "#mappingTypes"
},
link: function($scope, $element, $attrs) {
$scope.mappingTypes = $scope.$parent.mappingTypes;
}
}
});
Used as:
<div mapping-editor ng-model='content.mapping'></div>
With template content (relevant chunk):
<!-- language: html -->
...
<select class="form-control"
ng-change="mappingTypeChanged(storeType)"
ng-model="storeType"
ng-options="f.name as f.label for f in mappingTypes">
</select>
...
This yields empty list – as if the mappingTypes was empty.
The conent from ngModel is bound correctly.
How do I access kind-of-global (from one of the parent scopes) enumeration in a directive template? Or is there any other way to achieve this, such as defining the list differently instead of app $scope variable?
EDIT: Here is a fiddle.
There were a couple of issues with your code:
1.
According to the docs regarding the isolated scope of custom directives:
= or =attr - set up bi-directional binding between a local scope property and the parent scope property of name defined via the value of the attr attribute
(emphasis mine)
This means that you need to reference an attribute whose value is the name of the parent scope property you want to share. E.g.:
...
<editor ng-model="content" my-items="items"></editor>
...
scope: {
...
items: '=myItems'
},
Alternatively, you could do the binding in the directive;s link function:
...
<editor ng-model="content"></editor>
...
scope: {
content: 'ngModel'
// nothing `items` related here
},
...
link: function (scope, element, attrs) {
scope.items = scope.$parent.items;
}
2.
According to the docs regarding the select element, the ng-model attribute is mandatory. You have to add it into your template string:
...
template:
...
'<select ng-model="selectedItem"...
3.
According to the same docs regarding the select element, the ng-options attribute's value must have one of the specified forms, which you fail to provide (see the docs for moe info on the allowed forms). For the simple case at hand, the template string could be modified like this:
...ng-options="item for item in items"...
\______/
|_____(you need to have a label)
See, also, this short demo.
Try to set the argument mapping-types something like this
<div mapping-editor mapping-types='storeTypes' ng-model='content.mapping'></div>
If you set scope object in the directive, that means that this scope is isolated, and I'm not sure that you are able toe reach the parent scope. from the $parent object.
Related
I made directive with isolated scope with "=" method, in that directive i pass empty array, then i push data on that array.... How that change can be reflected on original array in my controller?
Here is the example:
angular.module('myModule').controller('MyController', ['$scope', function($scope) {
$scope.test = [];
}]);
angular.module('myModule').directive('mydirective', function() {
return {
scope: {
test: "=",
bread: "="
},
restrict: 'E',
link: function(scope, element, attribute) {
scope.test.push('one more')
},
replace: true,
templateUrl: 'some template'
};
});
HTML
<div ng-controller='MyController'>
<mydirective test='test'></mydirective>
<div ng-bind='test'> </div>
</div>
When i push something on array i dont have a reflection of that in my controller.
How can i fix that?
Here's how to do what you are trying to achieve.
HTML
<!-- myCtrl contains the original array, which we push here to the scope as 'ctrl' -->
<div ng-controller='myCtrl as ctrl'>
<!-- we pass your directive 'ctrl.test', which tells angular to two-way bind to the
test property on the 'ctrl' object on the current scope -->
<mydirective test='ctrl.test'>
<!-- we're inside the isolate scope: test here refers to mydirective's idea of test,
which is the two-way bound array -->
<div ng-bind='test'></div>
</mydirective>
</div>
JS
angular.module('app', [])
.directive('mydirective', function() {
scope: {
test: '='
},
link: function($scope) {
$scope.test.push('one more');
}
})
.controller('myCtrl', function() {
this.test = [];
});
Any alterations to the array will now be reflected in the ng-bind. Please note that it is bad practice to place primitives on $scope without being part of an object (due to the mechanics of prototypical inheritance) so you'd want to change $scope.test to something else.
I have an element directive that looks like this:
app.directive("ngArticle", [function(){
return {
restrict: "E",
replace: true,
templateUrl: "/partials/article.html",
scope: { article: "=article" }
}
}]);
And used like this:
<ng-article data-article="{{object goes here}}"></ng-article>
This works absolutely fine. But now, I want to access part of the object passed through the directive and process it before it renders out. For example, if the object inside data-article looks like this:
{
category: "Going Out",
id: "1234"
}
In the directive, I want to access the category value, replace whitespace with hyphens and change it all to lowercase. Is this possible within the directive?
I don't want to do this in a controller because the directive is used across multiple controllers.
You can access your object before it rendered using link function within directive:
app.directive("ngArticle", [function(){
return {
restrict: "E",
replace: true,
templateUrl: "/partials/article.html",
scope: { article: "=article" },
link(scope, element, attrs) {
...
}
}
}]);
Also, scope is not interpolated before the linking function so you can watch your article object:
scope.$watch('article', function() {
// do your transformations here
}
Remember to clean up your watchers before your directive destroy, this can prevent memory leaks
scope.$on('$destroy', function () {
// clean up watchers here
});
I try to put in my directive a way to different template, based on a $scope value. I use directive inside ng-repeat and i send her the object of datas and a other direct input
directive :
/**
* angular-streamlist directive
*/
angular.module('ngStreamlist', []).directive('webcams', function() {
return {
restrict: 'A',
scope: {
stream_webcam: '=webcam',
stream_margin: '#margin'
},
templateUrl: function(elem, attr) {
if (scope.stream_webcam.webcam_domain) {
if (scope.stream_webcam.webcam_domain == 'youtube') {
return 'templates/youtube.html';
}
if (scope.stream_webcam.webcam_domain == 'twitcam') {
return 'templates/twitcam.html';
}
}
}
};
});
html :
<div data-ng-repeat="webcam in datas.webcams">
<div data-webcams data-webcam="webcam" data-margin="no"></div>
</div>
and data is like :
{
"id": 1,
"webcam_domain": "youtube", ... etc
}
I have this error :
ReferenceError: scope is not defined
at Object.templateUrl (streamlist.js:13)
I don't understand, scope IS defined, no ?
The problem is that scope is not a local variable available to your templateUrl function. It is a field on the same object. You should be able to access it with this.scope so long as the calling angular code doesn't set this to another object.
You need to add the scope to your templateUrl function as well like so:
templateUrl: function(scope, elem, attr)
Otherwise your function won't have access to it, hence your error message.
I am learning AngularJS. I try to create a reusable component called .
Unfortunately I cannot prefill the fields inside element with the data obtained from JSON.
I looked around SO and the web but could not solve it. Could you please let me know what am I doing wrong?
I have two controllers. One gets a list of all countries:
app.controller('MainController', ['$scope', 'Countries',
function ($scope, Countries) {
$scope.countries = Countries.query();
}]);
The other gathers a specific address:
app.controller('AddressesController', ['$scope', '$routeParams', 'Address',
function($scope, $routeParams, Address) {
if ($routeParams.addressId) {
$scope.senderAddress = Address.get({addressId: $routeParams.addressId});
} else {
$scope.senderAddress = {"id":null, "country":null, "city":null, "street":null};
}
$scope.adData = {"id": 1, "country": "Poland", "city": "Warsaw", "street": "Nullowska 15"};
}]);
The services are defined as follows, they seem to work correctly and provide correct JSONs:
myServices.factory('Countries', ['$resource',
function($resource) {
return $resource('data/countries.json', {}, {
query: {method:'GET'}
})
}]);
myServices.factory('Address', ['$resource',
function($resource) {
return $resource('data/:addressId.json', {}, {
query: {method:'GET', params:{addressId:'addressId'}}
})
}])
I have routing set so that it directs to AddressesController:
app.config(function ($routeProvider) {
$routeProvider
.when('/address', {
templateUrl: 'partials/addresses.html',
controller: 'AddressesController'
})
.when('/address/:addressId', {
templateUrl: 'partials/addresses2.html',
controller: 'AddressesController'
})
});
The partial view is simple, I create 2 elements
<label> Sender </label>
<address address-data='{{senderAddress}}'></address> <!-- I tried all combinations of passing this as argument -->
<label> Receiver </label>
<address></address>
Now the directive is declared as:
app.directive("address", function () {
return {
restrict: "E",
templateUrl: "/directives/address.html",
scope: {addrData: '#senderAddress'},
link: function(scope, element, attributes) {
scope.adData = attributes["addressData"];
}
}
});
and template for it is:
<div>
<label> {{senderAddress}} </label> <!-- senderAddress from Addresses Controller is filled correctly -->
<div>
<label>Country</label>
<select>
<option value=""></option>
<option ng-repeat="country in countries.countries" value="{{country}}">{{country}}</option>
</select>
</div>
<div>
<label>City {{dto.adData.city}}</label>
<input type="text" data-ng-model="dto.adData.city" /> <!-- this I cannot pre-fill -->
</div>
<div>
<label>Street{{data.adData.city}}</label>
<input type="text" data-ng-model="dto.adData.street"> <!-- this I cannot pre-fill -->
</div>
</div>
It all works well outside of directive. But I miss something regarding how to handle the scope inside a directive with data being obtained from JSON service. Is it because JSON data is a promise object when the links to the directive are created? How to handle it?
PS
I also tried observing the attributes:
link: function(scope, element, attributes) {
//scope.dto.adData = attributes["addressData"];
attrs.$observe('addressData', function(data) {
if (!data)
return;
scope.dto.adData = data;
})
}
Even for statically defined data it doesn't work:
app.directive("address", function () {
return {
controller: function($scope) {
$scope.dto = {};
$scope.dto.data = {"id": 1, "country": "Poland", "city": "Warsaw", "street": "Nullowska 15"};
},
Passing in the JSON like this isn't how I'd do it as it's kind of hacking in the data binding and you probably don't get two-way binding. I'd use an isolate scope.
Your directive would be used without handlebars, to link up the scope variable:
<address address-data='senderAddress'></address>
And then you'd include a scope option in the directive definition:
app.directive("address", function () {
return {
restrict: "E",
templateUrl: "/directives/address.html",
scope: {
addressData: '='
}
}
});
The bare equals-sign '=' tells Angular to double-bind the parent scope variable referenced in the address-data attribute to the child scope variable addressData. This is done automatically by normalizing the name "address-data" into the JS-style "addressData." If you wanted to name the two scope variables differently, you could do innerAddressData: '=addressData' instead.
If you do it like this, you don't need a linking function at all and the binding still should work.
OK, I solved it, in case anyone has similar issues, it may help to check if the scope is set to true and to check if JSON is parsed from string ;-).
app.directive("address", function () {
return {
restrict: "E",
templateUrl: "/directives/address.html",
scope: true, // creates its own local scope
link: function(scope, element, attributes) {
attributes.$observe('addressData', function(data) {
if (!data)
return;
scope.dto = {};
// works almost fine but in 2nd case data is also filled
scope.dto.adData = angular.fromJson(data);
})
}
}
});
I am new to angularjs and i am stuck in accessing directive attributes in controller.
Directive
<rating max-stars="5" url="url.json"></rating>
app.directive('rating', [function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
link: function (scope, iElement, iAttrs) {
console.log(iAttrs.url); //works
}
controller
app.controller('ratingController', ['$scope', '$attrs' , '$http','$routeParams',function ($scope,$attrs,$http,$routeParams) {
console.log($attrs.url); //shows undefined
}]);
How do i access the url attribute in controller?
If you want to associate a controller with a directive, you can use the Directive Definition Object's controller property (either specifying the controller as a function or specifying the name of the controller (in which case it can be registered anywhere in your module)).
app.directive('rating', [function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
controller: 'ratingController'
};
});
// Meanwhile, in some other file
app.controller('ratingController', function ($scope, $element, $attrs) {
// Access $attrs.url
// Better yet, since you have put those values on the scope,
// access them like this: $scope.url
...
});
When using two-way data-binding via =, the corresponding attribute's value should not be a literal (because you can't have two-way data-binding to a literal), but a string specifying the name of a property in the current scope.
Using <rating max-stars="5"... together with scope: {maxStars: '='... is wrong.
You hould either use <rating max-stars="5"... and scope: {maxStars: '#'...
or <rating max-stars="someProp"... and scope: {maxStars: '='... while the enclosing scope has a property named someProp with a numeric value (e.g. $scope.someProp = 5;).
app.directive('myDirective',function(){
return{
controller: function($scope,$attrs){
console.dir($attrs);
}
}
});
That's it. If you want to access the elements attributes on a controller, you have to set up a controller for the directive.
(You could however, use a shared service to make those attributes available to another controller, if that's want you want to achieve)
http://jsbin.com/xapawoka/1/edit
Took your code and made a jsBin out of it. I can't see any problems whatsoever, so I'm assuming this is a simple typo somewhere in your code (could be the stray [ bracket at the top of your directive definition).
Here's the code:
var app = angular.module('app', []);
app.controller('ratingController',
function ($scope, $element, $attrs) {
console.log('ctrl.scope', $scope.url);
console.log('ctrl.attrs', $attrs.url);
});
app.directive('rating', function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
controller: 'ratingController',
link: function (scope, el, attrs) {
console.log('dir.scope', scope.url);
console.log('dir.attrs', attrs.url);
}
};
});
And here's the output:
http://cl.ly/image/031V3W0u2L2w
ratingController is not asociated with your directive. Thus, there is no element which can hold attributes bound to that controller.
If you need to access those attributes, the link function is what you need (as you already mentioned above)
What exactly do you want to achieve?