Using the same directive in a directive [angularjs] - javascript

I have a need to use the same directive within a directive, depending on a conditional param. However, when ever i try to do it, it seems to go to an endless loop. As i understand, it tries to pre-load the templates and that causes an endless recursion and at the end, it just throws me the following error:"RangeError: Maximum call stack size exceeded".
I have created an example in fiddle.. as you can see in the example, when the param's value is "1", it creates the error (even when the second level param is valued as "2" so it shouldn't have real recursion issues in the controller/app).
https://jsfiddle.net/qh9nh1gx/
"custom-directive"'s template:
<div>
<div ng-if='info==1'><div custom-directive info='2'></div></div>
<div ng-if='info==2'>DONE,single.</div>
</div>
Thanks

I have found 2 options to deal with the issue, the first one, is exactly what Jju described - creating a new "compiler" method (it can be grabbed from the url he sent).
The second option - always using an additional template for the "recursive" parts of the directive. For example, in my directive, i had a "ng-repeat" part that depending on the items value, it could request to display the directive again. while i used "ng-include" to have the other directive, it worked.
<div ng-repeat="item in items" ng-include="'inline-possibly-recursive-template"'></div>
in that template, you can call the directive again without any issues..
I hope that it will anyone else that will stumble into that issue.

You can look into https://stackoverflow.com/a/19065910/1680674 that describe a common approach to create directive that use himself inside

Related

change an angularjs nested template at run time

I have a template which is nested inside another template which I want to load when i click on a button.
So the nested template is loaded dynamically. This is what I have done so far.
This is the main body.html (this loads when a url is provided in the browser e.g. http://url#/newtemplate)
<div ui-view> </div>
Other section of the code has been removed for brevity
This is the new_template.html which I expects it to show when I click a button.
When I put a template name directly like below i.e. when I hard code it
<div ui-view="number1"></div>
It loads the template fully.
This is the dynamic model
<button ng-model="template_name" ng-value="number1">Button1</button>
<div ui-view="{{template_name}}"></div>
{{template_name}}
The above does not load the template as I expected. but it shows the string number1 when
the button is clicked
What can I do for it to load the template....
This is my controller
.state('parent',{
url: '/newtemplate',
views:{
'':{
templateUrl: "parent.tpl",
contoller:"controller",
},
'number1#parent':{
templateUrl:"number1.tpl",
contoller:"formcontroller"
},
'number2#parent':{
templateUrl:"number2.tpl",
contoller:"formcontroller"
},
'number3#parent':{
templateUrl:"number3.tpl",
contoller:"formcontroller"
}
}
})
Strange enough when I used the dot notation it did not work so I have to use the absolute naming method.
I also noticed that when I added the nested views as shown above the time it takes before the template gets loaded take a very long time.
Please I would appreciate any help which can allow me to load a nested view at runtime (possibly very fast)
Expecting more answer
I still hope that the I can make use of ui-view/ui-router because of the ability to make use of controller.
I'm not sure you can use uiView to load html dynamically.
I would try another possible solutions:
Use directives
Using ngInclude
I'll leave you an example with ngInclude: https://next.plnkr.co/edit/M5hl71mXdAGth2TE?open=lib%2Fscript.js&deferRun=1&preview

Using templateUrl breaks my code, but using inline template instead works . Why?

Ok, I've seen similar questions on SO but still don't understand this issue.
I have a directive for date input, using ngModelController. I have another directive, to which you can pass the model controller, and it checks if the model has errors, and sets appropriate classes to the element.
So the markup looks like this:
<form name="myForm">
<div class-validation="myForm.date">
<date-input ng-model="date" name="date"></date-input>
</div>
</form>
Since we have date-input inside of our form, and it has the name attribute and ng-model, the ngModelController will be added to myForm under the name date, which is exactly what we pass to class-validation directive.
Now the problem is, if I use templateUrl for my date-input directive, the class-validation directive receives undefined. But when I use inline template instead, everything works as expected.
Here is a Plunker that demonstrates the problem exactly.
http://plnkr.co/edit/Cygawxjp4WN9xRbTEXWU?p=preview
Comment out line 41 and uncomment line 42 in script.js to see the problem. The validation doesn't work and if you open browser console, you'll see that class-validation parsed myForm.date as undefined.
Why is this happening? I am guessing that requiring template from an url is async operation, but it shouldn't make a difference to the developer. I wasted hours and hours trying to find out what's causing this. Thanks.

(Why) does having a directive that "calls itself" in a finite ng-repeat lead to a stack overflow from infinite recursion?

EDIT: See this plunker for a simplified version of the problem (an sscce).
The following code seems to cause an infinite recursion problem:
statement.directive.html
<textarea></textarea><br />
<button ng-click='statement.newStatement()'>New Statement</button>
<button ng-click='statement.newSubstatement()'>New Substatement</button>
<hr />
<statement
ng-repeat='statement in statement.curr.sections'
curr='statement'
parent='statement.curr'>
</statement>
I don't know why though. statement.curr.sections is zero (when I tested it). So wouldn't that directive not get "instantiated/implemented"?
<p ng-repeat='statement.curr.sections'>x</p>
Doesn't cause any problems.
Wrapping it in an ng-if doesn't fix the problem.
<div ng-if='statement.curr.sections > 0'>
<statement
ng-repeat='statement in statement.curr.sections'
curr='statement'
parent='statement.curr'>
</statement>
</div>
Edit: this doesn't work either (but I realize I chose bad variable names).
<textarea></textarea><br />
<hr />
<statement
ng-repeat='item in statementCtrl.curr.sections'>
</statement>
Code on GitHub.
I took a look at your github code but it's really confusing. The naming is definitely throwing my brain in circles, so I would start by clarifying that.
For a directive, you should use a prefix so that it's clear you're referencing a directive: ie. myStatements, elStatement, whatever.
For a service and controller, use a suffix: StatementService, StatementController.
What I don't understand:
<button ng-click='statement.newStatement()'>New Statement</button>
What is statement. referring to here? When does this become initialized/binded to the view? Edit: nevermind I see that's the controller.
OK, so you've got the template for the directive, referencing the directive itself...
Hmm, not sure if a directive can have a child directive that is itself. Seems like quite the infinite loop when the template is trying to render. Because even if the next layer/directive doesn't have data, it still needs to bootstrap the directive and its template, so that leads it to load the next directive... and it never finishes.
Seems like an anti-pattern. What is the use case? Are you trying to draw a fractal? lol
In terms of fractals, you need to pass a limit (ie. when a fractal becomes so small you can't see it, there's no longer a point to draw it). So by imposing some limit, you can stop it from infinitely trying to draw.
tl;dr: Even if the data is empty, the child directive is still being initialized/rendered.

Toggling between templates displayed by an angular directive

I have an angular app that shows a list of things
<div ng-repeat="thing in things" regular-thing>
implemented with an ng-repeat that shows a regularThing directive for each thing in my list.
It's actually a little bit trickier than that though:
<div ng-repeat-start="thing in things">
<div ng-if="$first || thing.isSpecial" special-thing></div>
<div ng-if="!$first && !thing.isSpecial" regular-thing></div>
</div>
<div ng-repeat-end></div>
I use a different directive for the first thing in my list, and also for any "special" things. For our purposes, a thing becomes "special" when the user clicks on it - so, as they're scrolling through the list, they can click on a thing to have it displayed in a different (more extensive) template.
The way I have it now feels wrong to me. For one thing, I really don't need to have two different directives - just different templates. The logic is identical, specialThing just has a bit more of it. For another thing, I'm toggling a property on the data (namely thing.isSpecial) for purely view-related reasons, which makes me die a little bit inside.
So my question: Don't I deserve to die a little bit inside for this? Isn't there a cleaner, more "angular" way to handle this (i.e. to toggle between the directive templates)?
First, no one deserves do die. So the answer to your question is "no".
But you do raise some interesting points.
First, it's ok to have "view model" information in your scope (or controller, depending on if you are using the ControllerAs syntax or not). However, you definitely don't want to add view model information to your data models. Here's how I might do it (using your click-toggles-something-special example).
<div ng-repeat-start="thing in things">
<div ng-if="$first || isSpecial" ng-include="/specialtemplate.html" ng-click="isSpecial = !isSpecial"></div>
<div ng-if="!$first && !isSpecial" ng-include="/regulartemplate.html" ng-click="isSpecial = !isSpecial"></div>
</div>
<div ng-repeat-end></div>
The key difference is I'm adding the isSpecial property to the scope, not to thing, and isSpecial will be specific to that particular ng-repeat item's scope.
Also, unless you're planning on doing DOM manipulation, you can replace them with ng-include + ng-controller in the template html.
Some people prefer that pattern (include + controller) instead of directives, and other prefer to go ahead and others prefer to go ahead and write directives because it's more "componenty" (I made that word up). I think either is a valid way to go.

Angularjs animate ngClick + ngShow with custom directive (mySlide)

I'm trying to get the same ultimate functionality as ng-click + ng-show, except that I want the show to slide in instead of suddenly appear by toggling display: block/none;. I've got the jQuery animate I need, and I've set up the ng-click. I've got 2 problems, but the second might be a result of the first:
Problem 1
ng-click does not change the value of aside_users. I saw SO#12202067 which seems to be a similar situation, but I don't understand how/why their custom directive works and the native ng-click doesn't.
I see the scope: { … } after restrict: 'A',, but that appears to make $scope values available within the newly-created DOM element (my elements already exist and show up just fine, but no triggers/events are happening).
infobox.html
<aside
class="users"
ng-include src="'views/users.html'"
my-slide={"direction":"left","condition":"aside_users"}
></aside>
<i
class="icon icon-user"
ng-click="aside_users=!aside_users"
ng-init="aside_users=false"
></i>
The above code is a $compile'd template and elsewhere within the template I print out the value of the $scope parameter aside_users (prints false).
Problem 2
my-slide doesn't seem to be initiated/triggered (the logging of 'elm: ', elm doesn't appear in Chrome's console). I verified that directives.js is linked in my index.html page.
EDIT I remembered to link directives.js in index.html, but I forgot to add it to the resources array in app.js.
Plunkr
P.S. I'm not sure if <aside attr={object}> is strictly valid, but legitimate browsers seem to accept it in test cases (didn't bother to check IE). My alternate plan is to use 2 attributes: <foo my-slide="direction" my-condition="boolean"></foo>

Categories

Resources