Pass parent scope value into ng-repeat loop in Angular - javascript

This should be an extremely simple question, but all of the workarounds I've found are complex. I'm looping through an array of objects in using ng-repeat in a template as follows:
<div class="row-fluid" ng-repeat="message in messages.current|filter:'draft'">
{{ message.subject }} ... {{ campaign.name }} ...
</div>
Since the ng-repeat creates a new scope, the 'campaign' object from the controller doesn't seem to be accessable. Is there any way (aside from adding the campaign object to every item in my array) of getting that value?
Thanks in advance.

You can access the parent scope by using $parent
<div class="row-fluid" ng-repeat="message in messages.current|filter:'draft'">
{{ message.subject }} ... {{ $parent.campaign.name }} ...
</div>

This is a way that works that doesn't use $parent. It searches upwards through the nested scopes to find the object you're using, however many scopes it has to go through.
In the scope that contains the list, you can define an object with the list as a property, like this:
$scope.obj = {};
$scope.obj.items = ['item1','item2','item3'];
Then have the ng-repeat look like this:
<div ng-repeat="item in obj.items | filter:'item3' track by $index">
{{obj.items[ obj.items.indexOf(item) ]}}
</div>
(you need to use obj.items[ obj.items.indexOf(item) ] rather than obj.items[ $index ] because $index is the index of the filtered array, not the original)
The reason this works is because while obj doesn't exist in the current scope, as you attempt to access its property, Angular will look above the current scope rather than give you an error (if you just tried {{obj}} it would be undefined, and Angular would be happy with giving you nothing instead of looking through higher scopes). This is a helpful link about nested scopes: http://www.angularjshub.com/examples/basics/nestedcontrollers/
In my case I needed the track by $index, because I had an input with ng-model bound to an item in the array, and whenever the model updated, the input would blur because I think the HTML was being re-rendered. A consequence of using track by $index is that items in the array with identical values will be repeated. If you modify one of those other than the 1st one, weird things will happen. Maybe you can filter for uniqueness to avoid that.
I'm relatively new to AngularJS, so please comment if there is anything big I'm missing. But this works, so I'm using it at least.

Another method might be to pass parent scope as a scope variable to the directive i.e.
<my-directive
md-parent-scope="this"
ng-repeat="item in items"></my-directive>
It's a bit messy, but you have more control over what the parent actually is and can pass anything in.

Related

AngularJs: how to do ng-repeat over multiple variables like ng-init can do?

With ng-init you can use multiple vars:
ng-init="var1=value1; var2=value2"
I tried something similar with ng-repeat but it doesn't work
ng-repeat= "var1 in var1s; var2 in var2s"
Is there a way to do so with a correct syntax ?
Update: I want to use var1 for header and var2 for content of an accordion http://www.w3schools.com/w3css/tryit.asp?filename=tryw3css_accordion
As others have said, ng-repeat only works over a single variable. But one workaround would be to use ng-repeat over a single object which has two keys, one for each variable you want to access. The view might look like this:
<div ng-repeat="data in dataObject">
{{data.var1}} {{data.var2}}
</div>
In your controller, the scoped variable dataObject would have two (or more) keys, one called var1 and the other var2.
No, There is no way you can use two ng repeats variable with a semi colon separation like ng-init.
ng-repeat only works with single variable which is an array. If you want to have ng repeat for two different arrays, you could use nested-ng-repeat.
Otherwise merge your two arrays as one using key,value. Then you will be able to do ng-repeat over key,value and access them.
DEMO USING NESTED NG-REPEAT

Sort an object in angularjs ng-repeat directive

I have an object
Object {1: "CMAR0002", 2: "CMAR0003", 4: "CMAR0001"}
I am using this object in ng-repeat directive but i want to sort it by values. But the problem is that angular orderBy filter requires array of objects. But i have only pretty simple object here ;)
Thanks in advance
By Angular ng-repeat documentation:
It is possible to get ngRepeat to iterate over the properties...
However, there are a limitations compared to array iteration, like "The built-in filters orderBy and filter do not work with objects, and will throw if used with one"
If you are hitting any of these limitations, the recommended workaround is to convert your object into an array that is sorted into the order that you prefer before providing it to ngRepeat. You could do this with a filter such as toArrayFilter or implement a $watch on the object yourself.
Anyway, you can try to implement you own filter for this, but it will be pretty ugly and it will require much effort (probably for nothing).
Hope, it will help you!
Do this:
<li ng-repeat="(key, value) in Object | orderBy: key">
{{ key }} - {{ value }}
</li>
Fiddle: http://jsfiddle.net/oo4cb9xf/1/

Updating parent's object data from child controller

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

JSrender: cannot access parent variable inside if condition

as the title says, the problem is clear:
within the construction:
{{if is_completed == 1}}
<div>
<p>{{:#parent.parent.data.myproperty}}</p>
</div>
{{/if}}
the parent properties are not visible!
I solved it by creating construction like:{{for movies ~myproperty=myproperty}} in parent loop, and ~myproperty is visible inside IF conditions, but what if I have several variables, what if I have many-level nesting data structure?
The {{if ...}} block adds another view, so means you need to add a .parent to step up through that view, as in: {{:#parent.parent.parent.data.myproperty}}
You can pass in variables as you said (~myproperty=...) and they will be visible to any depth of nesting.
Your variable can be an object too such as the current data object: ~myObj=#data:
{{sometag a=b ~myObj=#data}}
....
{{:~myObj.myproperty}}
...
{{/sometag}}
so you don't need a separate variable for each property.
You can also access the top-level data object and drill down from there:
{{:~root.foo...myproperty}}.
And finally you can use #get("item") to step up through any number of {{if}} blocks and get the nearest "item" view (i.e. the item view for a repeating {{for ...}} block).
So you would write:
{{:#get("item").data.myproperty}}

In AngularJS, how to make an isolated scope inherit from ng-repeat's scope

I'm trying to create a custom component that receives arguments in a ng-repeat loop.
So for example, say I have a component named "mycomp" that receives a custom argument "name" in a ng-repeat:
<mycomp name="{obj.name}" ng-repeat="obj in list" />
And in my directive the isolated scope is defined like this:
scope:{name:"#"}
That won't work because ng-repeat creates an isolated scope for each element it iterates. So I ended up having two levels of scopes.
How do I get around this issue? Am I doing something wrong?
Thanks.
As I stated in my comment of your original question, this has already been answered. Anyway, here it is, summed up:
In your template, state the model you want to have inherited, without {{}} (as using brackets results in the value being passed, and not the reference to the model itself):
<mycomp name="obj.name" ng-repeat="obj in list" />
And in your directive, establish a 2-way binding, like so:
scope:{name:"="}
EDIT:
I realize now (after your comment) that while this solves your problem, it doesn't fully answer the question. Here goes:
When you create a directive you have the choice of creating a scope that inherits from its parent (controller, typically, though not necessarily) ou an "isolated" scope, by specifying scope: true or scope: {...}, respectively.
So, by creating an unisolated scope, all the parent's models are available (you can access scope.obj - created via ng-repeat - but also scope.list). This is convenient, but also dangerous, of course (and doesn't really create reusable code).
If you create an isolated scope, you can specify the scope's models using '#', '=' or '&'.
'#' and '&' both produce a isolated, unbinded value, (that, if you change, changes only on the isolated scope - in your case, the object in the original list suffers no change at all), the only difference being that '#' reads a string value, and '&' reads an expression.
THIS IS IMPORTANT: the reason why I believe your code didn't work was (only) because you passed name="{obj.name}" and not name="{{obj.name}}", for with '#' the string value is read, and that string value can be the name of obj, but you must include it in {{}}!
If you use '=', you are declaring that you want that variable to be binded with the specified outside variable. So, if (in a fit of crazy, crazy rage!) you want to have 2 models in your directive that start up with the same value, but on is binded (i.e. changes are propagated to the outside scope), you could do something like this:
<mycomp binded-name="obj.name" unbinded-name="{{obj.name}}" ng-repeat="obj in list" />
and in your directive:
scope:{
bindedName: "=",
unbindedName: "#"
}

Categories