I want to create a directive like:
<div ng-refreshing-when="x">Some Content In Here</div>
so when x=true, it will do some css and fade a spinner div over top, then when x=false, the spinner will fade out. I want to append a div (which is hidden by css) to the parent div, NOT replace the content. My current code is here, but the ngRefreshingWhen isn't triggering the ng-if:
http://plnkr.co/edit/griUYR6RY46x5DlqVHWU?p=preview
I set:
$scope.refreshing = false;
so the "refreshing" text shouldn't be there...
How can I accomplish this?
You should be able to use transclude and put the HTML in the template:...
angular.module('ngRefreshingWhen',[])
.directive('ngRefreshingWhen', function ($compile) {
return {
restrict: 'A',
transclude: true,
template: '<div class="refresher" ng-if="ngRefreshingWhen"><p>Refreshing</p></div><div ng-if="!ngRefreshingWhen" ng-transclude></div>',
scope: {
ngRefreshingWhen: '='
}
};
});
If the HTML is more complicated, put it in a separate file and use templateUrl.
http://plnkr.co/edit/28Eq0HARKF0KXpYJwBTS?p=preview
<div> <!-- Parent Div -->
<div ng-refreshing-when="refreshing"></div>
<p>Some Stuff I Want to Keep</p>
</div>
&
angular.module('ngRefreshingWhen', [])
.directive('ngRefreshingWhen', function($compile) {
return {
restrict: 'A',
scope: {
ngRefreshingWhen: '='
},
template: '<div class="refresher" ng-if="ngRefreshingWhen"><i class="fa fa-spin fa-refresh"></i></div>',
link: function(scope, element, attrs) {
// add has-refresher class
element.parent().addClass('has-refresher');
}
};
});
http://plnkr.co/edit/xRgqqOfz9IuskL1kn057?p=preview
I did it differently, I added directive inside, then added the needed class to it's parent, same result, just added an element instead of trying to add an attribute to the parent.
Related
I have a lightbox directive that when it transcludes, it injects in the content. Technically, wherever I put the actual call to the directive. But essentially, I need it to transclude to the body so that the backdrop can cover the entire page viewport, and so that it centers to the viewport and not the container is transcluding to right now.
The directive is written this way:
(function() {
'use strict';
angular
.module('app.core')
.directive('lightboxDirective', lightboxDirective);
function lightboxDirective() {
return {
restrict: 'E',
transclude: true,
scope: {},
template: '<section class="md-lightbox" ng-transclude></section>',
};
}
})();
The lightbox is located in the page this way:
<div id="scala-media-media" class="page-layout">
<div class="center" flex>
<div class="header">
<img ng-src="{{vm.media.images[0].url}}" ng-click="showLightBox = true">
</div>
<div class="content">
... the page content here ...
</div>
</div>
</div>
<lightbox-directive class="angular-lightbox" ng-class="{ removed : !showLightBox }">
... the code for my lightbox content (which works fine by the way) ...
</lightbox-directive>
At first I thought I'd specify the parent as I would normally do in a $mdDialog (Angular Material) with parent: angular.element($document.body), but that didn't work out. parent is not a recognized callback I assume.
Notice in the image where it is injected. Right where I place it! What's the point of using a directive if I can just place the code there and will do the same thing without the directive? right?
Here is a screenshot of what is happening. Notice the backdrop and centering issue I am having on the left side, as opposed to what I desire in the right side with a dialog.
I am using this angular lightbox found in this CODEPEN
UPDATE
I moved the lightbox to its own template file, so as to not feel guilty for using the code directly on the page which makes the use of a directive redundant. And restructured my directive as it shows below. But the rendering on runtime is still a problem. The template injects where it is called. Now if only I could declare the body as the parent for it to append to! lol
New directive:
function lightboxDirective() {
return {
restrict: 'E',
templateUrl: function(elem,attrs) {
return attrs.templateUrl || 'app/main/pages/scala-media/views/lightbox-template.html'
}
};
}
Thanks in advance!
You can make the element reposition itself just before the closing body tag with
.directive("lightboxDirective", function() {
return {
restrict: 'E',
transclude: true,
scope: {},
template: '<section class="md-lightbox" ng-transclude></section>',
link: function(scope, elem) {
angular.element(document).find("body").append(elem);
}
};
});
I have an element which its whole content could be transcluded. the transclusion is optional, so I can let its inner parts stay but an inner element be transcluded. Let's see it in action:
<h4 class="modal-title" ng-transclude="title">
Are you sure you want to remove this <span ng-transclude="innerType">good</span>
</h4>
In directive definition I have:
myApp.directive('confirmDeleteModal',function(){
return {
restrict:'E',
transclude: {
title:'?modalTitle',
innerType:'?modalType',
},
templateUrl:'templates/confirm_delete_modal.html',
scope: {
obj:'=info',
},
}
});
The HTML code would be so:
<confirm-delete-modal info="goodToBeDeleted">
<modal-type>good</modal-type>
</confirm-delete-modal>
When I run my code, I get the following error: [ngTransclude:orphan].
What should I do?
I am using AngularJS v1.5.8
You use another directive ng-transclude in a fall back content of modal-title. But the ng-transclude become the "parent" of the span and doesn't provide transclusion function. I suggest to you to modify your directive and to use the method isSlotFilled to know if title is filled or not :
directive('confirmDeleteModal',function(){
return {
restrict:'E',
transclude: {
title:'?modalTitle',
innerType:'?modalType',
},
link: function(scope, elem, attrs, ctrl, trfn) {
scope.titleFilled = trfn.isSlotFilled('title');
},
template:'<h4 class="modal-title" ng-transclude="title" ng-if="titleFilled"></h4>' +
'<h4 class="modal-title" ng-if="!titleFilled">Are you sure you want to remove this <span ng-transclude="innerType">good</span></h4>',
scope:{
obj:'=info',
}
}
})
(https://plnkr.co/edit/k0RXLWbOvHdNc9WFpslz?p=preview)
I set up a directive as follows:
.directive('ogTakeATour', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '../scripts/directives/TakeATourTemplate.html',
scope: {
content: '#',
uid: '#'
},
link: function(scope) {
angular.element(scope.uid).css("top","250px");
}
};
});
Directive template looks like this:
<div id="{{uid}}" class="tourContainer">
{{content}}
</div>
And this is how I call my directive:
<og-take-a-tour content="Content goes here" uid="menuTour"></og-take-a-tour>
However for some reasons this does not apply the css to the applicable div.
angular.element(scope.uid).css("top","250px");
Why is this? Could it be that the directive does not know what the id of my element is at the time the link function is running? How would I get around this if that is the case?
Angular's jQlite does not support search by id or CSS selector. So change your code like this:
angular.element(document.querySelector('#' + scope.uid)).css("top", "250px");
I'm trying to assign a random string to ngModel. But I can't even seem to assign a regular string to it. In the code below, I'm trying to change the ngModel to "new", but in chrome, it's still showing me that the ngModel is "placeholder". What am I doing wrong?
app.directive("page", function(){
return {
restrict: 'E',
replace: true,
template: '<div ng-model="placeholder"></div>',
controller: function ($scope, $element) {
$scope.placeholder = "new";
}
}});
The plain div tag was just an example. What I actually have is a content editable div that I've bound to a textarea with a contenteditable directive. I made a button to allow me to add as many of these directives as I want, but when I add this directive, I'd like a new ng-model for each one, because that's what I'm using to save the content of each content editable div to a file.
This is the full example in case it might help someone else. I put the addPage directive to a button, which, when clicked, appends a new content editable div (which I'm calling a page). There is one more directive that I didn't include (contenteditable) because I got it from the bottom of the docs over here
app.directive("addPage", function($compile){
return function(scope, element, attrs){
element.bind("click", function(){ angular.element(document.getElementById('container')).append($compile('<page></page>')(scope));
});
};
});
app.directive("page", function(){
return {
restrict: 'E',
replace: true,
// template: '<div class="page" contenteditable strip-br="true" ng-model="chapter"></div>',
template: function (elem, attr) {
var test="chapter.two"; //in reality, I will generate a random string to keep these divs unique
return '<div class="page" contenteditable strip-br="true" ng-model=' + test +'></div>'
}
}});
In my controller, I have this object that is used to store all the ng-model content
$scope.chapter = {};
I think you are approaching this a bit backwards. Starting with the model, you'd need an array to keep the values from all of the inputs / contenteditables.
.controller("MainCtrl", function($scope){
$scope.data = [{v: ""}]; // first element
$scope.addContentEditable = function(){
$scope.data.push({v: ""});
};
})
Then, you can bind to each element of that array easily, and add elements at will:
<button ng-click="addContentEditable()">Add</button>
<div ng-repeat="item in data" ng-model="item.v" contenteditable></div>
I'm not sure exactly how the page directive needs to be used here, but one way or another, the approach is the same as above.
If you just want to pass a string in you could use attributes and template(fn)
HTML
<div page="new">
Directive
app.directive("page", function () {
return {
restrict: 'E',
replace: true,
template: function (elem, attr) {
return '<div ng-model="' + attr.page + '"></div>';
}
}
});
If I have something like this
<div ng-class="{'breakfast': meal == 'eggs'}"></div>
Now I need to create a directive that would add the same thing, so doing this:
restrict: 'C'
scope:
meal:"&"
link:(scope, element, attr)->
element.attr('ng-class', "{'breakfast': meal == 'eggs'}")
adds the attribute to the DOM, but then nothing happens, when meal == 'eggs' it doesn't add breakfast class to the element. What am I doing wrong?
ps. I can of course do it with element.addClass and element.removeClass but then the animation would only work when I add the class, but wouldn't for the removal (if I have css styles like .breakfast-add, .breakfast-remove etc.
Instead of modifying the element's attributes at link or compile time, you can use ng-class in the directive's template and use a scope variable with your classes. It works for me in the following example, and it seems to be cleaner:
HTML:
<div ng-app="app">
<div class="meal: eggs;">xxx</div>
</div>
JS:
angular.module('app', [])
.directive('meal', function () {
return {
template: '<div ng-class="classes" ng-transclude></div>',
transclude: true,
restrict: 'C',
scope: {
meal: '#'
},
link: function (scope) {
scope.classes = {
breakfast: (scope.meal == 'eggs')
};
}
};
});