I am using ng-repeat of angularjs directive to load the array which have JSON value store in it. and value has subarray also.
<div ng-repeat="data in MENULIST" > //MENULIST have array(Data)
after checking some conditions like (if:condition).
<div ng-if="(data.SubMenu.length >0)" ng-init = "MENULIST = data.SubMenu"></div>
but that assignment or initialization is not been done globally to MENULIST it limited to this div Only.
ng-repeat stop after printing two main array elements.
not printing Sub Element of Array Element.
actually, I am trying to make and tree structure in the sidebar that has menu and sub menus also.
Besides two-way binding should work for you I believe calling some function is more straightforward way(especially since you can create this function in scope of particular controller - so it would be much easier to get the point)
<div ng-if="(data.SubMenu.length >0)"
ng-init = "someController.setMenuList(data.SubMenu)">
</div>
Another "angular-way" is adding explicit watcher to data.Submenu.length in component's JS code
$scope.$watch('data.Submenu.length', function (value, oldValue) {
if (!value && oldValue) {
// do your init
}
});
Related
Within a view I'm using ng-repeat inside a directive to loop over an array of objects from my controller. During this phase the objects inside the array suffer value changes. When I change the view and create a new instance of the same directive, I want to loop over the array of objects yet again but this time loop over the updated array of objects.
If I modify the objects in the first view and pass the same array of objects in the second view, the directive uses the initial array, not the updated one. What I tried is watching the array for changes, it sees the changes but still during the second instance of the directive it uses the initial array.
Can someone explain whats going on and how could I solve this ?
You can have a simple parent child relationship directive, in which directive have access to scope of controller o the view in which it's added. so you can have ng-repeat in directive & array in scope variable of controller. Then automatically the values will be sync by two way data binding. You can follow following plunk I've created.
It depends upon how are you sharing data between controller & directive. Two data binding helps make it sync else you've to handle it over event or using $watch in directive (if you're using jquery code inside directive).
https://plnkr.co/edit/m47TPgcX7lY8nL1LswCv?p=preview
app.directive('helloWorld', function ($compile, CarCompany) {
return {
restrict: 'E',
template: '<div>\
<h2>HelloWorld directive</h2>\
<p ng-repeat="car in carc">{{car.name}}</p>\
<h3>Liste des marques:</h3>\
</div>',
link: function (scope, elem, attrs) {
var ul = document.createElement('ul');
var lis = '';
angular.forEach(CarCompany, function(item) {
lis += '<li>' + item.name + '</li>';
});
ul.innerHTML = lis;
elem.append(ul);
$compile(ul)(scope);
},
};
});
This's simple directive showing shared scope between controller & directive.
https://www.infragistics.com/community/blogs/dhananjay_kumar/archive/2015/06/11/understanding-scopes-in-angularjs-custom-directives.aspx
I have a object $scope.object in parentCtrl, and I have a isolated scope directive. Inside directive, I have a functionality wherein I need to make an API call and fetch one of the property of $scope.object (this property is an array of object) and replace this property in original $scope.object.
I have tried various ways of doing it but somehow I am not exactly getting at it. I have tried updating it using $scope.$parent.object from directive's controller.
After this I tried sending the fetched array in to the parent's controller and replacing it there.
Once the $scope.object gets updated, the directive should run as the directive's template is binded with properties of $scope.object.
The interesting thing is that the view gets updated with new data but the data in original object somehow again becomes null.
I guess, there is some issue with the way my directive is called as the same directive is called nestedly. As shown below:
<li ng-repeat="t in A" ng-if="t.selected">
<div directive> <div>
</li>
where, A is the fetched property and t.selected is true. Now in the fetched data t.selected is true for two elements of array, while the directive is called 4 times, twice for each element. In the first iteration the data remains intact in parent object, while in 2nd iteration, data becomes null
I want to know, how do I update the data of parent object permanently
Directive:
<li ng-repeat="(key, t) in fetch.data>
<div layout="row" layout-wrap directive target="t" ></div>
</li>
and this target is double way binded in directive's scope with parent's scope
I have made a new directive that is basically a new carousel that extends the angular UI carousel bootstrap. This new carousel will display multiple divs in one frame. My new directive accepts any data in an array and a custom html template for each data.
However, if I use my carousel with a directive, there is a weird behaviour that I am seeing with watch inside the directive. Everything works fine, but the watch inside my directive is always getting the same value for newVal and oldVal. What I mean is this, here's my carousel code:
<slide ng-repeat="frame in ctrl.frames">
<div ng-repeat="divs in frame.divs">
<custom-directive data="divs"></custom-directive>
</div>
</slide>
and inside my customDirective controller, I watch the change of data like this:
$scope.$watch("ctrl.data", function(newVal, oldVal){
if (newVal !== oldVal) {
// data is updated, redraw the directive in my case
// however newVal is always the same as oldVal
}
})
newVal and oldVal is always the same.. I expected the initial state to be oldVal = undefined and newVal will be my new data. However, this is never the case. Data is passed as a two-way binding to carousel and to custom directive (using '=' operator inside the scope of each directive).
Why is this happening? I have investigated this for long and here's my findings:
If I don't use ng-repeat inside my carousel, this will work. oldVal will be undefined and newVal will be my data during the initial state. But why is ng-repeat causing this? I have read lots of article regarding golden rule of prototypical inheritance, that says ng-repeat will create new childScope that hides/shadows the parent, but that only happens to primitive object and I am passing an array to my data.
I need to use ng-repeat in my carousel directive.. so I need to know why ng-repeat is causing this.. any suggestions?
UPDATE:
Reproduced the problem in Plunkr here. As you can see, oldValue is always the same as newValue (I expected the oldValue to be undefined in the beginning)
When you register the $watch in your link function, Angular has already processed the bindings during the preLink phase, hence you will never see undefined the first time your watcher is executed (that initialiation call is the only moment on which oldVal and newVal are potentially the same. If the watcher was registered before the bindings resolution, the oldValue would be undefined)
If you really want to see it, you could override the compile phase and add a custom preLink method (the default link being the postLink).
But I really doubt that you want to do that. Why is it a problem to not have undefined the first time? You should try to explain the real problem you are facing.
Also, note that if divs that you pass to your directive is an array, you should use scope.$watchColleciton instead of scope.$watch in order to detect changes in the array elements instead of change of the whole array pointer.
I think the problem you are having is just a misunderstanding of how $watch works.
$watch is expected to initialize with equal values. See the documentation here. Specifically:
After a watcher is registered with the scope, the listener fn is
called asynchronously (via $evalAsync) to initialize the watcher. In
rare cases, this is undesirable because the listener is called when
the result of watchExpression didn't change. To detect this scenario
within the listener fn, you can compare the newVal and oldVal. If
these two values are identical (===) then the listener was called due
to initialization
So in other words, your check for if they are equal is so you don't detect the initial call
In your provided Plunker, if you need to do some initialization code, you can do two things:
You can check if they are equal in the $watch function, and if they are then that is the initial call with their initial values
Or, outside of that function in the link function, the values are their initial values there (since the link function is equivalent to post-link, which means scope values have already been linked) so you can put your code there
Forked your Plunker here. Notice I moved the alert outside of the $watch, and the value is still valid
EDIT:
The reason you see a difference when it is not in the ng-repeat and it is set up like your commented out code in the Plunkr is due to you adding data in a $timeout. When the page initially loads, below are what the two types render as:
<a1 prop="data[0]"></a1>
HTML looks as it was written. data=[]. directive element exists, calling link with data[0]=undefined. $watch called with prop=undefined
<!-- ngRepeat: element in data track by $index -->
HTML is simply a comment. waiting for data to be populated. No directive element exists, which means link is not called
When you add items to data after the timeout, they look like this:
<a1 prop="data[0]"></a1>
Same as above. data[0] is now defined so prop is defined
<div ng-repeat="element in data track by $index" class="ng-scope">
<a1 prop="element" class="ng-isolate-scope"></a1>
</div> (x3)
Page now has directive elements. calling link function on each with data now filled. $watch called with prop values linked
I created a custom <select> dropdown element using a directive, which has a selecting attribute that is used in the directive to pull the correct data from the controller to populate the list of options. I'd like to know if this is an appropriate use of AngularJS's different pieces. I'm new to Angular, but this is how it occurred to me to accomplish this task.
In the HTML partial for a page needing my version of select, this is all that needs to be written:
<sliding-select selecting="areas"></sliding-select>
In the controller there is a corresponding variable on the main object called areas which contains within it an array called options, whose members should be the options of the area select.
And here is the part of the directive that takes that data and creates an option element for each member of the areas array :
.directive('slidingSelect', function($timeout) {
return {
restrict: 'E',
compile: function(element, attrs) {
var selecting = attrs.selecting;
// ng-repeated options come from selecting attr
var dropDownList = $('<div class="sliding-select-list"></div>');
dropDownList.append('<div class="sliding-select-option" ng-repeat="item'+
' in home.'+selecting+'.options">{{item.name}}</div>');
element.append(dropDownList);
}
};
I'm just a bit worried that it seems hacky to create an element whose ng-repeat phrase uses the variable name by creating a string from it. Maybe there's a totally different approach to this problem that's preferred by the Angular community?
I have an array of object that I use in a ng-repeat loop with an attribute referring to an another object by it's id.
{Description: 'something', OtherObjectId: 1}...
I'm using a directive to display information about the OtherObject in that ng-repeat loop.
<li ng-repeat="object in objects">
<div>{{object.Description}}</div>
<div other-object-info="date" other-object-id="{{object.OtherObjectId}}"></div>
</li>
The otherObjectInfo directive use innerHtml to put the requested info in that html tag
Now is the part that doesn't obviously work, I need to be able to filter that list with all the displayed informations, including the one inserted by the directives.
How would you resolve it? I guess the best way would be to include the OtherObject inside the objects array before rendering the view, but I like the simple way of using that directive because I need it in multiple views.
Thank you very much for your output!
Consider a custom filter for the ng-repeat. You would keep the other objects separate from the array and pass whatever you need to the custom filter function:
<li ng-repeat="object in objects | filter:customFunct(otherObjectCollection)">
$scope.customFunct = function(otherCollection) {
return function(object) {
// locate the target object within otherCollection using object.OtherObjectId
// eg you can use this if you're using lodash:
var targetObj = _.find(otherCollection, {id : object.OtherObjectId});
// make the comparison you need
// use 'return' to return true based on the comparison
return targetObj.value > 40;
}
}
In this way, you can define and use a custom filter with a passed argument.
You can read more discussion on this solution here:
https://stackoverflow.com/a/17813797/1220172
https://stackoverflow.com/a/17811582/1220172