Detail section in row of `ng-repeat` - javascript

I am creating rows using ng-repeatand each row has expandable section in which details are shown. i fetch data on row click and populate detail area by two way binding. but here is the problem, all details sections are bidden to common scope variables.
Can someone guide me how to tackle this problem?
One solution in my mind is to maintain map structure for every detail. But do not know will it be a good idea or there is any better solution for this problem
Thanks in advance
EDIT
Angular.js version : 1.0.8

I think your problem is that you are binding the "ng-open" of your details tag to a boolean in the parent scope of your ng-repeat, instead of binding it to an attribute of the ng-repeat iteration itself (each loop of ng-repeat creates a child scope of your actual scope).
Here is an example:
<tr ng-repeat="thing in things">
<td ng-click="showContent = !showContent">
<details id="details" ng-open="showContent">
<summary><em>Details</em></summary>
<p ng-bind="thing.info"></p>
</details>
</td>
</tr>
On each loop of your repeat, it will create a child scope, and each of those scopes will contain a "private" showContent attribute that your ng-open directive will be attached to.
Hope this is your problem and this helps you.
--Edit--
(Of course that you have to be sure that the "showContent" attribute isn't declared on your scope controller)

Related

Using ng-model to display input value in p element

I am using a slider module called slick. I have a tab heading which tracks and displays the index of the slide so the user can see. Issue is that the tab heading slides with each slide as that tab is inside the carousel (obviously).
I moved the tab heading out of the slide, and planned to display to use ng-model and ng-value to bind the data to an element outside of the slide. Like so:
<div>
<p>Blue Renewal {{vm.slide.number}} of {{$ctrl.blueRenewals.length}}</p>
</div>
<slick data="$ctrl.phaseArr" >
<div ng-repeat="item in $ctrl.phaseArr">
<div>
<input type="text" ng-model="vm.slide.number" ng-value="{{$index + 1}}">
</div>
</div>
</slick>
And in the .js:
vm.slide = {
number: '1'
}
Question
Why isn't the value from the input binding to the p element outside of the slick carousel?
It's hard to say with so little code to go on, but I suspect you're encountering a scope issue.
There are at least two, probably at least three levels of scope in play here. Your heading is in your controller accessing properties of its scope. So when you bind to vm.slide.number it's looking on your scope. The 'slick' component likely creates a scope of its own, which may be where vm comes from. If that's the case, your view (on the parent of slick's scope) can't see properties on it.
(The ngRepeat also creates child scopes, but I don't think that's the problem.)

Angular ng-model not binding to textarea [duplicate]

Here is my plnkr: http://plnkr.co/edit/n8cRXwIpHJw3jUpL8PX5?p=preview You have to click on a li element and the form will appear. Enter a random string and hit 'add notice'. Instead of the textarea text you will get undefined.
Markup:
<ul>
<li ng-repeat="ticket in tickets" ng-click="select(ticket)">
{{ ticket.text }}
</li>
</ul>
<div ui-if="selectedTicket != null">
<form ng-submit="createNotice(selectedTicket)">
<textarea ng-model="noticeText"></textarea>
<button type="submit">add notice</button>
</form>
</div>
JS part:
$scope.createNotice = function(ticket){
alert($scope.noticeText);
}
returns 'undefined'. I noticed that this does not work when using ui-if of angular-ui. Any ideas why this does not work? How to fix it?
Your problem lies in the ui-if part. Angular-ui creates a new scope for anything within that directive so in order to access the parent scope, you must do something like this:
<textarea ng-model="$parent.noticeText"></textarea>
Instead of
<textarea ng-model="noticeText"></textarea>
This issue happened to me while not using the ng-if directive on elements surrounding the textarea element. While the solution of Mathew is correct, the reason seems to be another. Searching for that issue points to this post, so I decided to share this.
If you look at the AngularJS documentation here https://docs.angularjs.org/api/ng/directive/textarea , you can see that Angular adds its own directive called <textarea> that "overrides" the default HTML textarea element. This is the new scope that causes the whole mess.
If you have a variable like
$scope.myText = 'Dummy text';
in your controller and bind that to the textarea element like this
<textarea ng-model="myText"></textarea>
AngularJS will look for that variable in the scope of the directive. It is not there and thus he walks down to $parent. The variable is present there and the text is inserted into the textarea. When changing the text in the textarea, Angular does NOT change the parent's variable. Instead it creates a new variable in the directive's scope and thus the original variable is not updated. If you bind the textarea to the parent's variable, as suggested by Mathew, Angular will always bind to the correct variable and the issue is gone.
<textarea ng-model="$parent.myText"></textarea>
Hope this will clear things up for other people coming to this question and and think "WTF, I am not using ng-if or any other directive in my case!" like I did when I first landed here ;)
Update: Use controller-as syntax
Wanted to add this long before but didn't find time to do it. This is the modern style of building controllers and should be used instead of the $parent stuff above. Read on to find out how and why.
Since AngularJS 1.2 there is the ability to reference the controller object directly instead of using the $scope object. This may be achieved by using this syntax in HTML markup:
<div ng-controller="MyController as myc"> [...] </div>
Popular routing modules (i.e. UI Router) provide similar properties for their states. For UI Router you use the following in your state definition:
[...]
controller: "MyController",
controllerAs: "myc",
[...]
This helps us to circumvent the problem with nested or incorrectly addressed scopes. The above example would be constructed this way. First the JavaScript part. Straight forward, you simple do not use the $scope reference to set your text, just use this to attach the property directly to the controller object.
angular.module('myApp').controller('MyController', function () {
this.myText = 'Dummy text';
});
The markup for the textarea with controller-as syntax would look like this:
<textarea ng-model="myc.myText"></textarea>
This is the most efficient way to do things like this today, because it solves the problem with nested scopes making us count how many layers deep we are at a certain point. Using multiple nested directives inside elements with an ng-controller directive could have lead to something like this when using the old way of referencing scopes. And no one really wants to do that all day!
<textarea ng-model="$parent.$parent.$parent.$parent.myText"></textarea>
Bind the textarea to a scope variable's property rather than directly to a scope variable:
controller:
$scope.notice = {text: ""}
template:
<textarea ng-model="notice.text"></textarea>
It is, indeed, ui-if that creates the problem. Angular if directives destroy and recreate portions of the dom tree based on the expression. This is was creates the new scope and not the textarea directive as marandus suggested.
Here's a post on the differences between ngIf and ngShow that describes this well—what is the difference between ng-if and ng-show/ng-hide.

AngularJS, binding to a model in ng-repeat in a directive

So I am trying to bind inside a directive (to access outside) to a model inside of an ng-repeat. So in the outer controller I have a variable I would like to bind in like
//in the directive scope
filterArray: '='
Inside the directive that would be bound inside the directive to a model in an ng-repeat like so -
//inside the directive
<li ng-repeat="value in filter.values">
<input type="checkbox" ng-model="filterObject[filter.name][value]" ng-change="filterChange()">{{value}}
</li>
This worked fine until I changed the directive to have an isolate scope, now it is saying cannot set property of undefined. Is there any way to get this working as intended? The idea is the variable would build out when the user clicks the inputs so the outer controller would be able to see the built object.
Apologies if this is a bit confusing - I have made a fiddle to clarify : https://jsfiddle.net/vt1uasw7/42/ .
I want the outer controller to have access to the object built by binding the model - again this was working before I added the isolate scope. Thanks!
Edit: maybe the trick in this case is not to use the isolate scope? This one has me stumped, I've tried every combination of scope attributes :(.
If you can't pre-initialize your outter filterObject you can let the directive controller handle that for you:
$scope.filterArray[$scope.filter.name] = {};
And check your parameters as Claies stated, inside your directive you need to use filterArray and also the attribute name in the outter ng-repeat needs to be "filter-array and not "filterArray".
<div ng-repeat="filter in searchResults.filters" class="my-directive" filter="filter" filter-change="filterChange" filter-array="filterObject"> </div>
See this https://jsfiddle.net/vt1uasw7/164/

AngularJs Avoiding Scoping Issues

I recently spent over 4 hours before figuring out why my ng-model directive used in combination with ng-options was not correctly binding to the property within my controller. The <select> element was being properly initialized - receiving a value from the controller (parent) scope. But the child scope was not correctly updating the parent scope. After checking out the following questions and plunkers, I was able to develop a "work around" for this issue:
Helpful stackoverflow question 1
Helpful stackoverflow question 2
Basic Plunker
I found that the property I was binding to in my <select> element was binding to a property of the same name within a child scope of the controller - therefore not the value was not reflected as expected in the controller's scope. After changing
<select ng-options="asset as asset.Name for asset in allAssets" ng-model="selectedAsset" ng-change="lookupAssetPermissions()"></select>
to
<select ng-options="asset as asset.Name for asset in allAssets" ng-model="$parent.selectedAsset" ng-change="lookupAssetPermissions()"></select>
The value in selectedAsset was correctly binding to the property in the controller's scope (as seen in the ng-change event handler). The entire context of my element is the following:
<!---outer div has controller level scope----->
<div>
<!---inner div creates child scope with ng-if----->
<div ng-if="true condition here">
<!---select statement from above----->
<select ng-model="$parent.selectedAsset">...</select>
</div>
</div>
Do I have any other options in this scenario other than purposefully binding to the parent scope? If I had multiple child scopes (nested ng-if statements), would I need to alter the ng-model to bind to $parent.$parent.$parent....selectedAsset in order to update the value in my controllers scope? Are there any "best practices" on this topic?
Put all variables inside some object i.e.:
$scope.Model = {
selectedAsset : 'mySelectedAsset1',
selectedAsset2 : 'mySelectedAsset2',
selectedAsset3 : 'mySelectedAsset3'
}
Then you can:
<div ng-repeat> //new scope
<div ng-repeat> // new scope
<input ng-model="Model.selectedAsset">
This also lows your 'dependency' on $scope, defining such Model object will show everyone who is reading your code what model u have.

AngularJS: ngTransclude and access scope of ngRepeat

We are in the process of upgrading our application to Angular 1.3.0. In doing so, we ran into a few issues, most of which seem to boil down to the behavior of ngTransclude inside of ngRepeat.
We have a directive that repeats a bunch of items, with a container around them, but does not own the children of that container. For instance, here is a simplified example:
<div ng-controller="myController">
There are {{items.length}} items.
<div my-directive items="items">
This item's name is {{item.name}}
</div>
</div>
Internally, the directive contains <li ng-repeat="item in items" ng-transclude></li>, among other things.
Prior to the update, this worked fine. The repeated, transcluded elements are in a scope that inherits from the scope created by ngRepeat. As of the update, the items are in a scope that inherits from the controller, and as far as I can tell, there is no way to access the scope created by ngRepeat.
Here are two JS Bin examples:
Old (1.3.0-beta.1) behavior: http://jsbin.com/kalutu/1/edit
New (1.3.0) behavior: http://jsbin.com/gufunu/1/edit
How can I achieve the old behavior, or some semblance of it, in Angular 1.3.0? If this is the intended behavior of ngTransclude, how can I repeat a bunch of child nodes without knowing what they are?
https://github.com/angular/angular.js/issues/8182
It was decided for 1.3 that ng-trasclude would not pull scope from the directive. There is a work-around on the linked pages,
https://github.com/angular/angular.js/issues/7874
https://github.com/angular/angular.js/issues/7874#issuecomment-47647528
This is the expected behavior.

Categories

Resources