I've developed AngularJS directives to add rectangle into SVG document:
<svg version="1.1" width="200" height="100">
<g>
<!-- Works -->
<g dir-rect1="" viz-settings="settings.rect1" />
<!-- Doesn't work -->
<g dir-rect2="" viz-settings="settings.rect2" />
</g>
The only difference is between the directives is that the dir-rect1 directive doesn't use replace and the dir-rect2 directive use:
app.directive('dirRect2', function($compile) {
return {
restrict: 'AE',
scope: {
vizSettings: '=',
},
replace:true, // <--- DOESN'T WORK
template: '<rect fill="{{vizSettings.fill}}" stroke="{{vizSettings.fill}}" width="{{vizSettings.width}}" height="{{vizSettings.height}}" />',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
});
Why when I use replacing in my dirRect2 directive, I cannot see a rect?
It seems that generated code is right in both cases.
You can see example on plunker.
Two things:
replace is deprecated so, I don't recommend you to use it
replace copy the attributes to the children, and that is your problem.
The second directive generates this:
<rect fill="#111111" stroke="#111111" width="10" height="20" dir-rect2="" viz-settings="settings.rect2" class="ng-isolate-scope"></rect>
And it seems that dir-rect2 and viz-settings is breaking the svg in some way... if you remove this attributes with the same code... svg seems to work:
<rect fill="#111111" stroke="#111111" width="10" height="20" ></rect>
in dirRect1 replace is false (default value) and in dirRect2 is true, so you see a different behavior between the two directives.
The rectangle is not displayed since "rect" tag is required to render the shape in svg! (as I just realized since I never played with svg)
(when you define "replace: true" you are telling Angular to remove the original tag on which the directive is applied)
Related
Basing on nice tutorial I've created an overlay with Google-style spinner. HTML in my case is minimum and looks like following:
<div class="spinner-wrapper" data-bind="visible: spinnerVisible">
<svg class="spinner" viewBox="25 25 50 50">
<circle cx="50" cy="50" r="20" fill="none" stroke-width="4" stroke-miterlimit="10" />
</svg>
</div>
Note the data-bind="visible: spinnerVisible".
I'd like to reuse this among different places in my application to avoid DRY problems. What approach may I take? Keep in mind, that visibility of the spinner wrapper will be controlled by different viewmodels (although I may guarantee each one to have spinnerVisible property).
I'd use a (template-only) knockout component with the new web component-like syntax.
Two steps to get it to work:
1. Register a component and define its template paramaters:
Here, you define your template and the parameters it needs to render. In your case, only a named observable that controls the visible binding.
ko.components.register('spinner', {
template:
'<div data-bind="visible: spinnerVisible">A spinner<hr/></div>'
});
2. Use it with the correct parameters in your HTML
Now, whatever your viewmodel's spinner controlling property may be, you can reuse the same component to render it. For example:
<spinner params="spinnerVisible: loading"></spinner>
<spinner params="spinnerVisible: pendingRequests().length > 0"></spinner>
Note that if you use the regular component binding, you'll lose some of the clean syntax.
Here's an example showing the code above in action:
ko.components.register('spinner', {
template:
'<div data-bind="visible: visible">A spinner<hr/></div>'
});
ko.applyBindings({
someProp: ko.observable(true)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<p>Regular syntax</p>
<div data-bind="component: {
name: 'spinner',
params: { visible: someProp }
}"></div>
<p>Web component</p>
<spinner params='visible: someProp'></spinner>
<button data-bind="click: someProp.bind(null, !someProp())">toggle</button>
I've been looking all over the internet for something like this and I still can't find the answer.
I have a directive that is reused throughout my application, it allows the user to sort and search through lists of items. I have multiple kinds of items that can be passed in to the directive (html templates that I pass in) as well as multiple uses for those templates. I.e, sometimes I want a certain button on that template, but sometimes I want another. (This will make more sense in a minute).
Therefore I have created multiple directives with transclusion in order to achieve this. However, I'm having serious issues with scoping and I can't seem to figure out exactly how to pass the isolated scope to the child directive.
Below is my code:
Item List Directive
var app = angular.module('main');
app.directive('itemList', function(){
var linkFunction = function (scope, element, attributes, ctrl, transclude) {
//Things that modify the scope here. This scope is what I want to pass down to the child directives
//NOTE: I do not currently have a working transclude function which is why I didn't include it here because I have no idea what to do with it
scope.pagedItems = groupItemsToPages(items);
scope.currentPage = 0;
};
return {
restrict: 'E',
replace: 'true',
transclude: true,
templateUrl: 'partials/directives/item-list.html',
link: linkFunction,
scope: {
searchPlaceholder: "#",
items: "=",
control: "="
}
};
});
item-list.html
<div class="form-group">
<!-- I won't put all of the html here, just some to show you what i'm going for -->
<div class="search-field">
<input type="text" ng-model="query.value" placeholder="{{searchPlaceholder}}/>
</div>
<table class="table table-hover">
<tbody>
<tr ng-repeat="item in pagedItems[currentPage]">
<td ng-transclude></td>
</tr>
</tbody>
</table>
</div>
Here's the directive that simply returns the URL of whatever template is passed to it. This is so that I can add in an extra html through further nested transclusions.
item-template.js
var app = angular.module('main');
app.directive('itemTemplate', function() {
return {
restrict: 'AE',
replace: 'true',
transclude: true,
templateUrl: function(tElement, tAttrs){
return tAttrs.templateUrl;
}
};
});
Here's an example template (extremely simplified again, just to show you the layout)
profile-template.html
<div>
<p>item.name</p>
<p>item.description</p>
</div>
<div ng-transclude></div>
Here's an example of the HTML that calls this code
tab.html
<div class="tab">
<div class="available-items">
<item-list control="profileControl" search-placeholder="Search Profiles" items="profileControl.profiles">
<item-template template-url="partials/profile-template.html">
<button type="button" ng-click="launchProfile(item.id)">Launch Profile</button>
</item-template>
</item-list>
</div>
</div>
So now that you've seen the code. The issue I'm having is that my profile-template.html inclusion isn't getting the scope from the directive above it even though I've tried cloning the scope to it. All the examples I've seen require you to remove the template key from the directive and assume you're only returning the code you have in your transclusion. In my case, I have other html that I want to display in the template.
Any help would be appreciated
Instead of trying to pass the scope between your directives, you can make use of the $parent attribute and get access to higher scopes.
For instance, in your item-template controller you could gain access to the higher scope from item-list with a code like this:
if ($parent.searchPlaceholder === '') {
...
}
I'm trying to dynamically build an encoded query in an ng-repeat that will then be passed into a typeahead directive.
I know I can pass in the entire ng-repeat item similar to this jsfiddle:
http://jsfiddle.net/yaroslavya/8YEkh/
And I know you can easily pass a string into a directive attribute.
Is there a way to do both?
Basically in the fiddle above i'd want to do something like:
<div ng-repeat="image in images">
<typeahead-directive image='"encoded_query=" + image' ></div>
</div>
I tried doing this and it works in the console, but the ng-repeat property isn't actually stored inside the directive:
<div ng-repeat="image in images">
<typeahead-directive image='encoded_query={{image}}' ></div>
</div>
any suggestions?
Is there a way to do both?
In a your directive you can get the string value of the your image attribute with $attrs.
You can use $eval to get the object from the your image attribute.
Your HTML
<div ng-repeat="image in images">
<div myimg image='image' ></div>
</div>
The directive
myModule.directive('myimg', function(){
return{
restrict: 'A',
scope: false,
template: '<p>{{image.title}}</p><img src="{{image.url}}" />',
controller: ['$scope','$attrs', function($scope, $attrs) {
console.log($attrs.image);
console.log($scope.$eval($attrs.image));
}]
/*link:function(scope, elem, attr){
console.log(attr.image);
console.log(scope.$eval(attr.image));
}*/
};
});
Controller local attributes are documented in AngularJS $compile API Reference -- controllers
The $eval function is documented in AngularJS scope API Reference
Also notice that I set scope: false because your directive doesn't really need a scope.
Let's say i have a <lightbox> directive in AngularJS. The lighbox has two params: title and image_source, which in turn are referenced in its HTML with {{title}} and {{image_source}}.
Because I use Bootstrap's modal, the directive must say on the outmost scope of the page (if I insert it in the inner DOM elements it can have display problems).
I understand that if the directive is in a "parent" controller, there are many ways to interact:
<div ng-controller="ParentCtrl">
<lightbox src='mySrc' ... >
</div>
but I am forced to pull it out:
<div ng-controller="ParentCtrl">
</div>
<lightbox ...>
(Edit: to clarify: i am forced to put the modal OUT of the controller (which is a template called by a route) because it interacts badly if it is not. Another way would be to wrap all the page in a Main controller, but I'm not sure it's elegant.)
But how do I access the internal directive scope and change dynamically the two values?
(Edit: thought of using a service, not sure if it is the right way, though)
this is method to call rootscope to directive
angular.module("directiveAPP", [])
.controller("directiveController", function ($scope, $rootScope) {
$rootScope.src = "http://picbook.in/wp-content/uploads/2014/07/image_123.jpg";
$rootScope.title = "hai";
}).directive("lightBox", function () {
return {
restrict: 'E',
template: '<div><img src="{{imageSrc}}" title="{{imageTitle}}" width="100px" style="border:solid #ddd 1px;" /></div>',
scope: {
imageSrc: '=',
imageTitle: '='
}
};
});
this one use of directive
<div ng-app="directiveAPP">
<div ng-controller="directiveController">
<light-box image-src="src" image-title="title"></light-box>
</div>
<light-box image-src="src" image-title="title"></light-box>
</div>
FIDDLE LINK
I have a simple directive that just displays some text for now:
app.directive("exampleText", function() {
return {
restrict: "E",
template: '<div>hello!</div>'
}
});
In my index.html I have this:
<div class="container" ng-app="customerPortalApp">
<div ui-view></div>
<exampleText></exampleText>
</div>
exampleText is outside my ui-view as thats to do with my routes and works correctly. But its my understanding the directive template should render as is. Have I missed something?
With a directive named:
app.directive("exampleText", ...
HTML should be:
<example-text></example-text>
From documentation:
Angular normalizes an element's tag and attribute name to determine
which elements match which directives. We typically refer to
directives by their case-sensitive camelCase normalized name (e.g.
ngModel). However, since HTML is case-insensitive, we refer to
directives in the DOM by lower-case forms, typically using
dash-delimited attributes on DOM elements (e.g. ng-model).
As tasseKATT noted the directive name should stay as "exampleText"
and the html element should be <example-text>
I thought a demo may help
demo
the template:
<div ng-app="myApp">
<sample-text></sample-text>
</div>
the directive:
var app = angular.module('myApp', []);
app.directive('sampleText', function () {
return {
restrict: "E",
template: '<div>Some sample text here.</div>'
};
});