model is not refresh inside directive after changes - angularjs

I'a got a template with a directive using objects. That directive modifys an elements in the objects list. I can see the object modification in the template but not inside the directive.
I used angularjs 1.5.8
My template :
<div>
<ul>
<li data-ng-repeat="event in vm.events">
// some html code about event object
</li>
</ul>
<event-directive data-events="vm.events"/>
</div>
The directive template :
<div>
<div data-ng-repeat="event in events" class="event">
<a data-ng-click="edit(event)">Edit</a>
// some detail about event
</div>
</div>
The directive is declared liked that :
function eventDirective(/* dependencies */) {
return {
template: eventTemplate,
scope: {
events: '='
}
}
}
If I modified and event in my directive I can see changes in the main template ng-repeat loop but the changes are not "propagated" to the directive.
I'm sure I'm doing someting wrong or forgot something as there's similar code in my project that works as expected.
I search for advises or way to solve my problem.
I try to simplify my code but if it's not clear or there's missing information, just tell me.
Thanks in advance for any help.

Related

How to evaluate a template from a controller?

I've got this template:
<div class="container-fluid">
<div class="row">
<div class="col-xs-12" ng-repeat="product in ad.products">
<a href="{{product.link}}">
<h1>{{product.title}}</h1>
<img src="{{product.src}}">
<p>{{product.description}}</p>
<h5>{{product.price}}</h5>
</a>
</div>
</div>
</div>
From my controller I need to evaluate this template so that it checks how many products that have been selected and then it interpolates each product's values into the template. After that is done I also need to remove the ng-repeat so it doesn't fire an error in the external pages that will use this where angular is not present. However I'd figure that I'd just use a regex to look up the ng-repeat and everything in the expression and then remove it.
I've been looking at $interpolate and $compile but I can't figure out how to work with them from my controller so that it does what I want. This is because when I use these on my template and then console log the template value it's a function with a whole lot of nonsense in it.
So doing this:
ad.html = $compile(res.data, $scope);
Generates something like this:
function(b,c,d){rb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var h=d.parentBoundTranscludeFn,k=d.transcludeControllers;d=d.futureParentElement;h&&h.$$boundTransclude&&(h=h.$$boundTr…
Can someone shed some light on how to achieve what I want?
Your are using $compile function in wrong way, you should call $compile(html) function by passing $scope parameter like below.
var compiledDOM = $compile(res.data)($scope);//then do append this DOM to wherever you want
ad.html = compiledDOM.html(); //but this HTML would not make angular binding working.

AngularJS and ¤compiler

I have started to study angularJs directives. I have an example that I can't understand
The full source code is in the following plunker
var detailTemplate = $compile(element.find("div.detail")
.replaceWith(replacementMarkup));;
return a function.
Here the scope is global, I mean, is the scope of controller and I can see
the collection images (the recipies), I think.....
what I can't understand is how the old div is retrieved
<div class="detail">
<img ng-src="{{recipy.img}}" />
</div>
and how the right image is shown; ng-repeat is called again?
and when I do this:
detailTemplate(scope, function(detail) {
target.replaceWith(detail);
});
Thanks in advance

Hide/show an element in Angularjs using custom directive, ng-show and $scope

When a link is clicked in the app navigation a dropdown with ui-view content shows below each respective link.
The HTML:
<div class="sc-dash-header">
<ul>
<li>
<a class="navbar-brand" show-nav-popup href="">download</a>
<div id="nav-download-progress" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-download-progress"></div>
</div>
</li>
<li>
<a class="navbar-brand" show-nav-popup href="">add</a>
<div id="nav-add" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-add-content"></div>
</div>
</li>
<li>
<a class="navbar-brand" show-nav-popup href="">enter pin</a>
<div id="nav-unlock" class="dash-hdr-popup" ng-show="showPopup">
<div ui-view="hdr-unlock"></div>
</div>
</li>
</ul>
</div>
I've included an ng-show attribute to open the dropdown when $scope.showPopup is set to true.
To achieve this I've created a custom directive with an on click called show-nav-popup.
The JS:
.directive('showNavPopup', function () {
return {
restrict: 'A',
// scope: {},
link: function(scope, el, attrs) {
el.on('click', function(){
scope.$apply(function () {
scope.showPopup = true;
});
console.log(scope);
});
}
};
});
The above works, but the dropdown opens on each element.
Question: I need to isolate the scope, so on each click, only the respective dropdown appears. I uncomment the line // scope: {} - but this doesn't work..
Angularjs n00b here - any help would be much appreciated
Having an isolate scope in this situation wouldn't fix the problem. There are a ton of ways to achieve what you want though. One of which is to assign each show-popup-nav an id, turn $scope.showPopup into an array, and keep an individual true/false for each id. Then for each ng-show, you look at the index corresponding to each id for the true/false value.
I coded it up on that guy's Plunker, working as you expect: http://plnkr.co/edit/CSikLIiuPNT9dfsfZfLk
EDIT: I should say, you COULD use an isolate scope to fix this, but that would require a lot of changes to your DOM, as the ng-show directive is a sibling to your show-popup-nav, and not a child.
When you create the isolate scope, the scope applies to the element that your directive is applied to, and it's child elements. In this case that's just the anchor tag:
<a class="navbar-brand" show-nav-popup href="">download</a>
You are using an ng-show on a tag that is a sibling to the anchor tag:
<div id="nav-download-progress" class="dash-hdr-popup" ng-show="showPopup">
The sibling is not part of the isolate scope, and so it never notices that the value of showPopup has changed.
The ng-show would work if it were applied to a DOM element that was a child of the anchor tag.
EDIT
One way to make this work would be to wrap your two siblings in a parent tag, and use the directive on the parent:
<div show-nav-popup>
Download
<div ng-show="showPopup"></div>
</div>
Then you'd need to modify your directive's code to find the anchor tag and apply the click handler.
You might instead try a completely different approach as suggest in the other answer by #Bill Bergquist

ng-repeat with ng-include not working

I am trying to use an ng-repeat that includes an ng-include. The problem is that the first element in the ng-repeat is just the ng-include template with none of the data from the ng-repeat filled in. Is there a way I can somehow bind the template from the ng-include so it works on the first ng-repeat?
<div ng-repeat="item in items">
<div ng-include src="'views/template.html'"></div>
</div>
For example, if my ng-repeat contains 10 items, then the first item that is rendered will just be the empty template. Items 2-10 WILL be rendered as they should be. What am I doing wrong?
First make sure that the data that is contained in the first index of items actually has the data that you want.
One possible solution to your problem would be to simply not show the first index of the ng-repeat:
<div ng-repeat="item in items" ng-show="!$first">
<div ng-include src="'views/template.html'"></div>
</div>
This may not actually tackle the root of your problem, but it may still get your application working a bit more like what you expect.
Another possible solution:
<div ng-repeat="item in items" ng-include="'views/template.html'"></div>
see example here:
http://plnkr.co/edit/Yvd73HiFS8dXvpvpEeFu?p=preview
One more possible fix just for good measure:
Use a component:
html:
<div ng-repeat="item in items">
<my-include></my-include>
</div>
js:
angular.module("app").directive("myInclude", function() {
return {
restrict: "E",
templateUrl: "/views/template.html"
}
})
I ran into the same problem, and finally figured out that the first element has not been fetched and compiled in time for the first ng-repeat iteration. Using $templateCache will fix the problem.
You can cache your template in a script tag:
<script type="text/ng-template" id="templateId.html">
<p>This is the content of the template</p>
</script>
Or in your app's run function:
angular.module("app").run(function($http, $templateCache) {
$http.get("/views/template.html", { cache: $templateCache });
});
You can also use $templateCache inside your directive, although it's a bit harder to setup. If your templates are dynamic, I would recommend creating a template cache service. This SO question has some good examples of template caching inside a directive and a service:
Using $http and $templateCache from within a directive doesn't return results
Using a directive worked for me: https://stackoverflow.com/a/24673257/188926
In your case:
1) define a directive:
angular.module('myApp')
.directive('mytemplate', function() {
return {
templateUrl: 'views/template.html'
};
});
2) use your new directive:
<mytemplate />
... or if you're concerned about HTML validation:
<div mytemplate></div>

Duplicate attributes when using directive compile with transclude

JsFiddle of the issue: http://jsfiddle.net/UYf7U/
When using the angularJs transclude within a directives compile, it will duplicate any
attribute properties. I.e.
<a class="myClass">my link</a>
Will become
<a class="myClass myClass">my link</a>
Similarly, when using an ngClick
<a ng-click="myFunction()"> my link</a>
Will become
<a ng-click="myFunction() myFunction()"> my link</a>
The fiddle demonstrates this, and unfortunately it crashes. It's a stripped down version of what I'm trying to implement.
Is there a way around this? I've posted the issue to github to: https://github.com/angular/angular.js/issues/2576
When clicking on Hello the word "clicked" should appear in an alert.
This happens because myDirective is being initialized twice - first as part of your markup:
<div class="transcludeMe">
<div data-transclude-this="here">
<div class="myDirective"></div>
</div>
</div>
Second in the transcludeMe directive - since you do this in the compile stage of the directive initialization:
transcludeHere[0].innerHTML = clone[x].innerHTML
Since you use replace:true all attributes of the original element will get copied to the template element. If you remove this your example works, but you still be aware that myDirective is getting initialized two times: http://jsfiddle.net/tkzgG/
How important is it to you to specify the directive name as a class? This issue does not occur when the directives are used as elements directly.
See http://jsfiddle.net/smmccrohan/cfP3U/
Like thus, plus replacing the restrict: 'C' with restrict: 'E' in the directive definitions (and making some case changes to avoid an issue there):
<div ng-app="app">
<div ng-controller="ParentCtrl">
<transcludeme>
<div data-transclude-this="here">
<mydirective />
</div>
</transcludeme>
</div>
</div>
I found a different way to do multi-transclusion and that fixed my problem entirely, here's the updated fiddle for my problem being fixed: http://jsfiddle.net/UYf7U/1/
The code came from my previous question here: Multiple transclusions of separate html in an update that I did not see.
The fiddle will be out of date, but this is my final multi transclusion function. I've mode the logic into compile instead of the controller so that it can transclude dom that needs to have things like ng-repeat
.directive('multiTranscludeTo', function($rootScope){
return {
compile: function(tElement, tAttributes, transclude){
var baseScope = this;
transclude($rootScope, function(clone){
for (var x = 0; x < clone.length; x++){
var child = angular.element(clone[x]);
var viewName = child.attr('data-multi-transclude-from') || child.attr('multi-transclude-from');
if (viewName && viewName.split(" ")[0] == tAttributes["multiTranscludeTo"]){
tElement.html(clone[x].innerHTML);
}
}
});
}
}
})

Resources