I'm integrating Angular into a web application and I noticed that the wrapper is messing with some of the CSS on the page. After researching into directives, I saw that custom directives can have a property called 'replace' to be set to true so that the templateUrl directly replaces instead of being wrapped in the directive's tags.
Is there a way to do the same with ng-view, or more generally any Angular directive? Thanks for the help!
I think your best bet is to decorate the original ng-view directive and add the replace:true switch to it. Note, replace is going to be - hah - replaced in the next major version of angularjs, but for now, this will work:
app.config(function($provide) {
$provide.decorator('ngViewDirective', function($delegate) {
var directive = $delegate[0];
directive.replace = true;
return $delegate;
});
});
And of course, the jsFiddle: Here
Is there a way to do the same with ng-view
I can't think of a way of doing that without editing the source of Angular or something. replace is for custom directives, not (necessarily) the built in ones.
or more generally any Angular directive? Thanks for the help!
Same thing. But you wouldn't want to do it with any directive, seeing how you could have multiple directives in a single element. I suppose you may want to do it with all template'd directives (since I think you can only have one per element), but, again, without editing the source of the directive I can't think of a way to do that.
Also, note that Angular has stated that replace "will be removed in next major release", so it's probably not best to rely on it.
You can, however, if you wish, create you own ngInclude/ngView-esque directive that renders templates without a wrapping tag.
A naive and probably problematic one (or at least inefficient) might look like:
app.directive('myRender', function ($compile, $http) {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var newScope;
var newElement;
attrs.$observe('url', function (value) {
if (value) {
$http.get(value).then(function (result) {
if (newElement !== undefined) {
newElement.remove();
}
newElement = angular.element(result.data);
if (newScope !== undefined) {
newScope.$destroy();
}
newScope = scope.$new();
$compile(newElement)(newScope);
element.parent().append(newElement);
});
}
})
}
}
});
http://plnkr.co/edit/Vm96f2rQsT2PX1C3rgK8?p=preview
My first advice would be to adapt your css to angular if you want to use angular.
My other advice would to do your entire application in angular, and not add it after the fact.
Related
I have a component, and i would like to inject it dynamically into my html.
I have a component like this:
angular.module('test1', []);
angular.module('test1').component('test1', {
templateUrl: 'components/test1/test1.template.html',
controller: function test1Controller($scope) {
}
});
the test1.template.html file looks like this:
<p>TEST 1</p>
on my controller i have this:
angular.module('myApp')
.controller('ctrlCtrl', function ($scope, $sce) {
$scope.tag = "<test1/>";
});
on my index.html, i have this:
<ng-bind-html ng-bind-html="tag"></ng-bind-html>
but the tag will not show up. I have tried writing literaly "'<p>hi!</p>'" on the ng-bind-html field, and the text "hi!" shows up on a paragraph, so i don't think this error is because of a typo.
I also tried to use $sce.trustAsHtml, but it didn't work neither :(
$scope.tag = $sce.trustAsHtml("<test1/>");
when i insert an input field, the trustAsHtml method does work, but when i try to inject my components dynamically, it just won't let me, please help D:
Why ng-include won't work?
Components need to be compiled before you can use them on the markup. Try editing the html of the app with the developer tools from your browser, by artificially injecting your component on the markup: it won't work.
How to dynamically include components?
you'll need to use directives, this tutorial (thanks to #Artem K.) is friendly to follow, but you can also read the angular's official documentation, it is a little hard to understand though.
Following the logic of the final example of the angular's official documentation, you can create a directive that compiles everything that is passed to it, like this:
// source: https://docs.angularjs.org/api/ng/service/$compile
angular.module('myApp')
.directive('my-compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
});
and then, on your index.html, you'll have to invoke the directive, sending the the string containing the component's tag as an argument.
<div compile="tag"></div>
As #charlietfl and #Artem K. said, you have to understand the angular's $compile so, thanks guys for pointing me in the right direction :)
Here is the minimal code to describe the issue. On the page, I have:
<div ng-controller='AController as a'>
<div a-directive></div>
</div>
In js, I have:
app.directive("aDirective", function($compile){
return {
scope: true,
link: function(scope, element, attrs) {
var template = "<h1>{{a.label}}</h1>";
element.append($compile(template)(scope));
}
}
});
app.controller("AController", function($scope){
self = this;
self.label = "some text";
});
That works, but the issue is that {{a.label}}, which made the view and controller/model tightly coupled. Is there any way to get rid of that a., and not to mention the controllerAs-name in the directive code at all? (just like what I did in the controller code)
To improve this you can pass the text to display as a parameter to the directive. Something like this is the initial idea:
<div a-directive="a.label"></div>
However, I DO recommend using an alias for the controller, so I made a Plunker where you can see all of this working together with some improvements.
Check it out here: http://plnkr.co/edit/1hBSBxwSEPXoj9TULzRQ?p=preview
I would also recommend to use template instead of link and restricting the directive to an element instead of using it as attribute, since it is modifying the DOM. But yeah, you could keep improving it till the end of the times :)
We are using bootstrap datepicker for our project needs. What we need to do is that whenever user selects the today's date, date has to be shown appended with "(TODAY)" like "May 12, 2008 (TODAY)" in the textbox.
What can be the best approach here? As we are using this datepicker at multiple places, I think having a general approach like creating a directive would be helpful. Was trying to bind change event with the datepickerPopup directive, but have not been able to make it work.
Here is what I have tried so far:
Have created one decorator. This seems to be working. However one issue, how do I access the parent directive methods in this decorator (such as dateFilter, parseDate here)? (sorry if you find my questions naïve, as I am very new to angularjs). I have attached the code.
app.config(function($provide) {
$provide.decorator('datepickerPopupDirective', function($delegate) {
var directive = $delegate[0],
link = directive.link;
//closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
//angular.extend(directive.scope, { 'monthChanged': '&' });
directive.compile = function() {
return function(scope, element, attrs, ngModel) {
link.apply(this, arguments);
ngModel.$render = function() {
var date = ngModel.$viewValue ? dateFilter.apply(ngModel.$viewValue, dateFormat) : '';
var currentDate = new Date();
currentDate.setHours(0,0,0,0);
if(date.getTime() == currentDate.getTime()){
element.val(date + "(TODAY)");
}else{
ngModel.$setViewValue(scope.date);
}
scope.date = parseDate( ngModel.$modelValue );
};
};
};
return $delegate;
});
});
You've got a couple of options. The quick and dirty way would be to extract the component from the third party library into your own directive and template, and modify as needed. The disadvantage to this is that you'll no longer be up to date with the component. Future versions would require you to manually update your directive, which you might not care about... yet.
The second option, is to take advantage of angular's $provide.decorator
This post gives you an initial idea
What are "decorators" and how are they used?
Here's a basic example of decorating a directive definition object
app.directive("foo", function() {
return {
replace: true,
template: '<div>This is foo directive</div>'
};
});
app.config(function($provide) {
$provide.decorator('fooDirective', function($delegate) {
var directive = $delegate[0];
directive.restrict = "AM";
return $delegate;
});
});
In your case, you'll want to override what value is being referenced on the template. You could decorate the entire directive if you want to completely modify it.
I would recommend this as the best approach when looking to tackle modification of a third party library.
Here's an example of decorating a directive to override a scope level function, and use an existing scope variable within the directive while overriding it.
https://jsfiddle.net/wvty8rpc/3/
I have raw HTML in my model.question.text I was looking at the following:
app.directive('ngHtml', function() {
return function(scope, element, attrs) {
scope.$watch(attrs.ngHtml, function(value) {
element[0].innerHTML = value;
});
}
});
Can someone tell me if there is a simple way for me to include that in my page.
I think you're looking for ngBindHtmlUnsafe:
Creates a binding that will innerHTML the result of evaluating the expression into the current element. The innerHTML-ed content will not be sanitized! You should use this directive only if ngBindHtml directive is too restrictive and when you absolutely trust the source of the content you are binding to.
See $sanitize docs for examples.
I'm trying to learn AngularJS and I'm trying to dynamically compile some DOM elements...
I've tried the demo:
try {
var templateHTML = angular.element('<p>{{total}}</p>'),
scope = ....;
var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
//attach the clone to DOM document at the right place
});
//now we have reference to the cloned DOM via `clone`
} catch (ex) {
alert(ex.message);
}
but all I get back is a "$compile is not defined"
HELP!
A sample code for using $compile in a directive. Basically go ahead & first append the element to DOM (might want to keep it invisible), then run the compile by using a finder.. as mentioned by rtcherry, $compile should be injected.
//
componentModule.directive('getCompilerWk', function($compile) {
return {
restrict: 'A',
link: function(scope, elm, attr) {
elm.click(function(){
$(body).append(templateHTML);
$compile($(body).find('p'))(scope);
})
}
};
});
Where are you calling this code from? Is it safe to assume it is outside of the Angular framework by your use of angular.element(...)?
If so, you can use this:
// Split across two lines for readability...
angular.element(<something within Angular's scope>)
.injector().get('$compile')(...)
If not, you may simply need to inject $compile into the controller/directive/service.