Let's say I have a block
<div class="myclass" mydirective="mycontrol.param">
</div>
that is repeatable (used) several times (but different by different parameters) in another directive.
I need to add the conditional to mydirective so it is persists in the block based on kind of ng-if true or false values for it.
What's the way to make it done?
P.S. What actually mydirective does is adds some DOM elemenets into the div it belongs to.
Related
I'm using Fire base to retrieve the user object, inside the object there is type which is admin or normal user.
$scope.User=$firebaseObject(fireBaseData.refUser().child(user.uid));
now i'm trying to use ng-if to change the view of each user
<div ng-if="{{User.type}}==admin">
i have tried another approach by changing the field to admin either true or false
<div ng-if="!{{User.admin}}">
but still not working in both cases, the problem is i think in using {{}} inside the ng-if directive
You dont usally use {{}} in directives. Just the following code would be sufficient:
<div ng-if="!User.admin">
The ng-if directive removes the HTML element if the expression evaluates to false.
If the if statement evaluates to true, a copy of the Element is added in the DOM.
The ng-if directive is different from the ng-hide, which hides the display of the element, where the ng-if directive completely removes the element from the DOM.
Hence, both of given is correct:
<div ng-if="User.type==admin">
<div ng-if="!User.admin">
I have a loading view that's set as the html inside the directive. Something like the following.
<my-directive>
<div class="loader"></div>
</my-directive>
I'd like the HTML inside there to be active until some property of my directive has finished processing.
The closest thing I've found is transclude but I'm not sure that's really what I want.
Is there a way to avoid rendering until some arbitrary point in time?
you can hide a html element until a property return true
<div ng-if="taskFinished"></div>
or
<div ng-show="taskFinished"></div>
both do the same think but with ng-show the hidden content are not recalculated on each unhide
What Alainlb explained is also how I would approach the issue, show in the example below:
<my-directive>
<div ng-if="loading">
<div class="loader"></div>
</div>
<div ng-if="!loading">
<span>Do everything in here...</span>
</div>
</my-directive>
ng-if and ng-show/ng-hide
I personally will always use ng-if unless there is a very good reason not to. Since ng-if will only render the HTML when the check has passed. ng-hide on the other hand will make the HTML code invisible but it is still there! When the directive is very complex, then the browser is doing pointless work calculating and rendering things the user can't even see. To me, the only time it makes sense is when you want to collapse items that you know for sure the user will open very often. Even in this case I would still consider ng-if, unless it takes a long time to render each time.
Hi please explain reason for following three scenarios as I am unable to know why is this happening -
1)
<div ng-controller="myctrl">
<p>something for DOM manipulation</p>
</div>
2)in route I write
('someroute',{
templateUrl : "mytemplate",
controller : "myctrl"
});
mytemplate:
<div>
<p>something for dom manipulation</p>
</div>
3)
<div ng-include="mytemplate" ng-controller="myctrl"></div>
with template being same as above
The controllers in all the above scenarios are same, and in all of them I am just trying to select p tag of DOM by writing angular.element('p'). But this seems inconsistent. It works very well in 2nd scenario, it never works in 3rd scenario and I am not sure about 1st sccenario. Can someone explain which method is best for dom selection/manipulation, as I have to add a class to this 'p' tag on hover.
I am not understanding which gets initialized first- controller or partial?
Manipulating DOM inside controllers is discouraged. Quote from Best Practice - Dom Manipulations:
Dom Manipulations should not exist in controllers, services or anywhere else but in directives.
If you only need to style elements on hover, using the p:hover CSS selector would be enough without touching the DOM. ng-class and ng-mouseover can help you if you really want the class.
For more complex scenarios, you may want to write your own directive. You can check the article above for a guide to do that.
Load order from the first case: HTML first. Directives like ngController are loaded after parsing the HTML. Therefore the HTML already exists when the controller is loaded.
Load order for the second case: I'm not sure about it. You may check documentation for ngRoute or uiRouter depending on the router you are using.
Execution order for the third case: Controller first. The directive ngController have higher priority than the ngInclude directive. Therefore, the controller is loaded first.
Quote from ngController documentation :
This directive executes at priority level 500.
Quote from ngInclude documentation :
This directive executes at priority level 400.
I'm creating a custom Angular directive for a slide in menu which needs to watch a couple of attributes and one of those attributes needs to be two way bound to the main controller scope (sometimes). However, sometimes the attribute will not be added by the developer so it needs to be added automatically and set to the default (false). So, the directive can be used like this.
<slide-menu position="right" is-open="menuIsOpen"></slide-menu>
or like this:
<slide-menu></slide-menu>
When used the first way the main controller will be able to open and close the menu by changing the value of the boolean $scope.menuIsOpen.
When used without supplying the is-open attribute it should default to false and is obviously used internally and by a child toggle directive.
An additional complication is that whether the attribute is supplied by the developer or not it should exist in the DOM. so in the second example above the directive would set itself to false by default and add the attribute is-open="false" to the DOM?
The reason for requiring is-open="false/true" in the DOM at all times is that the menu is actually operated using CSS tansitions which use the following selector:
slide-menu[is-active="true"]{
// Slide the menu in using transforms/transitions
}
There is a jsfiddle here which shows how far I have got.
http://jsfiddle.net/jonhobbs/gEPvE/
Obviously it doesn't work, but it shows how I have tried to set a default and how I have tried to use # and & on the isolated scope for a one time binding (the menu position) and a 2 way bound expression for the is-open variable.
I'm clearly a long way from achieving what I need but any advice would really be appreciated.
Have a look at this fiddle http://jsfiddle.net/gEPvE/38/
I took the one you started and updated it to act like you specified.
You can make a two way binding value optional by adding a ? on the scope definition.
Like this
{
scope: {
'isOpen':'=?'
}
}
Now the is-open attribute is optional.
Then you can set the default value in the directive controller, like you had started to do.
Next, in order to synchronize the DOM attribute with the scope value you can use $watch.
$scope.$watch('isOpen', function(val) {
$element.attr('is-open', val);
});
Finally, I changed the second 'slideMenuToggle' directive to wrap/transclude its element in order to add an ng-click handler. This is mainly to avoid any nastiness with calling $scope.$apply yourself.
Let me know if that works for you.
EDIT
Answering your question in the comment, you can pass a value directly without having it be bound to the scope, you just need to wrap the value in quotes.
For example
<div ng-controller='ctrl'>
<hello world='imOnScope'></hello>
</div>
Assuming 'hello' is a directive with a scope of 'world': '=?' then angular will assign a reference to the parent scope's 'imOnScope' object to the directive's $scope.world member, allowing a two way binding scenario.
To just provide a value directly you may do something like this
<div ng-controller="ctrl">
<hello world="'directValue'"></hello>
</div>
In this scenario angular will just assign 'directValue' to the directive's $scope.world member.
You need to add ngTouch to your module.
var app = angular.module('app', ['ngTouch']);
And add this script:
http://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular-touch.js
The reason for requiring is-open="false/true" in the DOM at all times
is that the menu is actually operated using CSS tansitions which use
the following selector
Forcing directive attributes to be appropriate for css selectors is terrible idea. As you correctly stated, they are for developers. So add a class to the element dynamically.
It seems that you're misusing &, it would be ok to set up a callback, but since you don't do this, in its current state you can end up with one-way # with confidence.
I guess it can be something like this (just added ngTouch and ng-controller for parent scope).
You could replace
$scope.watch('isOpen', function () {
$element.toggleClass('opened', $scope.isOpen);
});
with
$scope.watch('isOpen', function () {
$attrs.isOpen = !!$scope.isOpen;
});
and get the behaviour you're asking for, easy as that. Ok, it is boolean now, and it reflects the scope, and you can use [is-open=true] selector. But guess what will happen with your binding? Broken. Fortunately, you can do
$scope.watch('isOpen', function () {
$element.attr('is-open', !!$scope.isOpen);
});
instead. Voila, we tricked Angular because it doesn't look after jqlite. But what will will happen with the binding when the directive will be re-compiled for any reason? Again, isOpen's binding is non-existing 'true' or 'false' scope variable. Broken.
What is the proper way to dynamically add or remove directive from compiled and linked element?
I have a page that has bunch of inputs there (the list is pretty long, so i want to come up with a general solution). What i want to do is to disable all the inputs if specific flag set. I can do this by using jQuery's element.prop('disabled', true).
The problem of such approach is that if any of inputs have ng-disabled or ng-enabled directives attached, then on any their expression modification they will override previously set 'disabled' property. But I want them to not override my global flag.
I came up with the solution to add another bunch of watchers for ng-disabled or ng-enabled expression, but it seems to be not the best approach.
What I want to do, is to remove most of directives attached to the element and set appropriate attributes myself. But if I recompile and relink the element, and then replace it in the document, then I will get a memory leak, as the old element will be de-attached from the DOM document tree, and will remain in memory. I cannot destroy element's scope either, because those elements basically use whole page's main scope.
You can try something like
<div ng-show="someBoolean" >Some text or nested element</div>
or instead of "someBoolean" you can attach a function that resolves to a boolean. To set your boolean you could attach a ng-click to your input that updates your model/boolean value
<button type="button" ng-click="setBoolean()">Some text or nested element </button>
Because of angulars two way data binding the ng-show will be updated upon completion of the next digest cycle