How to set many dropdown fields using array in Angular. Also, when choose an option from one dropdown field must not effect another field - arrays

How to set many dropdown fields using array in Angular. Also, when choose an option from one dropdown field will not another field and the selected option to be shown on it's input box.
I tried a lot to achieve it, but not luck. I have also attached the code. Please review it & fix it if you have better understanding.
https://stackblitz.com/edit/angular-qfpxmy?file=src%2Fapp%2Fapp.component.ts

you has errors in your code, the idea is show/change filter.formField[ind]. So you should use [ngModel]="filter.formFiend[ind] instead [(ngModel)]="filter.formField" (it's innecesary use the "bannana notation" or (NgModelChange) because you change the value when click the options.
<div class="row" *ngFor="let item of dropdownsArr; let ind = index;">
...
<!--the [(NgModel)]="filter.formField" becomes like-->
<input ...[ngModel]="filter.formField[ind]"
(click)="dropdownFieldStatus(ind, $event)">
<ul class="dropdown filter-priority open" *ngIf="showDropDown[ind]">
...
</ul>
</div>
Futhermore, your function onSelectOption should give value to this.filter.formField[ind]
onSelectOption(ind: number, index: number, bugClass: any): void {
this.selectedOptionIndex[ind] = index;
this.filter.formField[ind] = bugClass.title;
}

Related

Items disappear when editing inside an Angular ngRepeat

I have an array of items that each display as a directive inside an ng-repeat like this:
<div ng-repeat="item in ctrl.data | filter:ctrl.query" class="ubi-box container-fluid">
<user-item item="item" . . .></user-item>
</div>
As you can see, there is a filter on the ng-repeat. Inside the directives, users can edit items inside forms that show when the user clicks on one of the items.
Trouble occurs when the user edits a field that affects the filter. If the user has found the item using the filter (ctrl.query), and if the edited text means the item no longer matches the filter, that item suddenly vanishes before the user can hit save or anything. Poof!
What is the most elegant solution? I don't want to turn the filter off when editing commences, because then all the items will reappear. I want the filter to still work, but I want the item being edited to keep showing even if it would be filtered out.
Do I set an "isOpen" flag in each item, and add an "or isOpen" clause to the filter? Adding flags like that always feels kludgy to me, and I'm not even sure how to do that in the filter syntax.
Do I add an "or isFormVisible" clause to the filter? That might be neater, but again, how do I even do that in the ngRepeat filter syntax?
Thoughts?
Thanks in advance
John
There might be more elegant solutions out there, but I would probably follow the example from the Angular documentation.
https://docs.angularjs.org/guide/forms#binding-to-form-and-control-state
When editing commences, copy the item being edited to a new object that populates the edit controls (the view controls behind the scenes are still the master object). Once you save, you copy the updated object into the master object which will at that point revert back to the view mode and it'll be hidden by the filter.
Something along these lines:
$scope.edit = function (item){
$scope.editItem = angular.copy(item);
}
$scope.saveTo = function(item){
for (var i = 0; i < $scope.items.length; i++){
if ($scope.items[i].id === item.id){
$scope.items[i] = angular.copy($scope.editItem);
break;
}
}
$scope.editItem = {};
}
I compare the ID in the item being edited to the ids of each item in the list to ensure only one can be ng-if'd at a time. Here's a sample plunk.
Update: Here's another plunk that uses a different query filter and all you do is set a flag when you go into edit mode to an id. Similar to the one before, but none of the master object code. If you already have an edit mode, this seems like it should be pretty quick to apply.
I would suggest using ng-model-options along with $render option, here is a working example : http://plnkr.co/edit/IFpXBYeJx1wrbKhhzMZg?p=preview
<form name="userForm">
<label>Name:
<input type="text" name="name" ng-model="name" ng-model-options="{ updateOn: 'click' }" />
</label>
<button ng-click="userForm.userName.$render();">Update Now</button>
<br />
</form>

How can I accomplish this task in AngularJS?

I am attempting to accomplish a task where I am using AngularJS so I am not sure how to implement potentially needed Javascript or if there is a way with using AngularJS.
I have this input:
<input type="number" ng-model="myCount" ng-disabled="button" min="1" value="1" id="myValue">
This code takes the value input by the user and places it in a variable:
$scope.myCount = document.getElementById("myValue").value;
Now, what I want is based on the number input, I want an array/object created for the number from the variable. For example:
for(i=0;i<myCount.length;i++){
$scope.trackObject=[
{name:i + "Objects"}
]
}
Here is what I am trying to accomplish with this:
<ul class="nav nav-tabs" id="pizza_tabs" role="tablist" ng-repeat="x in countPizzas">
<li class="active">{{x.name}}</li>
</ul>
Sorry, I don't think I very well explained what I am trying to accomplish!
I want there to be a tab that contains the text "# Object", for each object created which the number of objects created is based on the user input. Does that make sense? So, if the user inputs 3, then 3 objects will be created, which I will be able to assign a name and whatever other properties to. 3 tabs are thusly created for the user to toggle through that will contain these properties. I hope that helps!
"what I want is based on the number input, I want an array/object created for the number from the variable"
for(i=0;i<myCount.length;i++){
$scope.trackObject=[{name:i + "Objects"}]
}
//Try this
var name;
for(i=0;i<myCount;i++){ //because myCount is a number
name = i+"Objects";
$scope.trackObject.push({name:name}); //add to the array
}
Now for your last snippet, I didn't understand what you want to accomplish there.
I am confused by your question.
You have already set the ngModel to myCount so whatever you have typed into the input box would have been set to the ngModel - 'myCount'
There is no need to use jquery to get the value as such
"$scope.myCount = document.getElementById("myValue").value;"
As for the second part, i believe the user "I_Wrote_That" has already replied you on how to create an array of objects based on the input.
At first, angular supports two way data binding. So you need not get the value of the element manually using document.getElementById etc.
$scope.myCount = document.getElementById("myValue").value;
Instead the value will be available in myCount by default.
Now to generate the objects based on count:
<ul class="nav nav-tabs" id="pizza_tabs" role="tablist">
<li class="active">{{x.name}}</li>
</ul>
Now in your controller:
$scope.generateCounts = function(myCount) {
$scope.trackObject = [];
for(var i=0; i < myCount; i++) {
$scope.trackObject.push({name : i+"objects"});
}
return $scope.trackObject;
}
But this will not work still, it will give you duplicate key error.
So in your HTML, use track by $index
<li class="active">{{x.name}}</li>

How to set a boolean flag to collapse/expand a row with ng-repeat

I have this plunker code.
What I'm trying to do, is to display the gray box one time per row.
To achieve this, I thought to modify the partition filter in order to return a JSON to add it a new property by row to know if the gray box is expanded or not.
But, I could Not successfully return a JSON.
Do you know how to modify the filter to return a JSON or a better way to show the gray box by row?
Related questions:
Push down a series of divs when another div is shown
Update 1
The issue could be easily resolved by using the correct scope for the ng-repeat for the row without modifying the filter, thanks to #m59.
http://plnkr.co/edit/eEMfI1lv6z1MlG7sND6g?p=preview
Update 2
Live Demo
If I try to modify the item, it seems the ng-repeat would be called again losing the props values.
<div ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}">
<div ng-repeat="item in friendRow"
ng-click="collapse(item)"
ng-class="{myArrow: showArrow}">
{{item.name}} {{item.age}} years old.
<div>{{item.name}}</div>
</div>
<div collapse="!props.isExpanded">
some content
<br/>
<input type="text" ng-model="currentItem.name">
</div>
</div>
js
$scope.collapse = function(item){
this.props.isExpanded = !this.props.isExpanded;
this.showArrow = !this.showArrow;
$scope.currentItem = item;
};
This causes the gray box to collapse each time the item is modified. Any clue?
I've updated my code/answer regarding partitioning data. It's important to fully understand all of that before deciding on an approach to your project.
The problem you have in your plnkr demo is that you're modifying the parent $scope and not the scope of the ng-repeat for that row.
Just set a flag on the row and toggle it when clicked:
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="isExpanded = false"
ng-click="isExpanded = !isExpanded"
>
<div ng-repeat="item in friendRow">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!isExpanded">
some content
</div>
</div>
To access the correct scope within a function in the controller, you can use the this keyword instead of $scope. this will refer to the scope the function is called from, whereas $scope refers to the scope attached to the element with ng-controller (a parent of the ng-repeat scopes you want to target).
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-click="collapse()"
>
JS:
$scope.collapse = function() {
this.isExpanded = !this.isExpanded;
};
If you want to keep the ng-click directive on the item element instead of putting it on the row element as I have done, then you're dealing with another child scope because of that inner ng-repeat. Therefore, you will need to follow the "dot" rule so that the child scope can update the parent scope where the collapse directive is. This means you need to nest isExpanded in an object. In this example, I use ng-init="props = {}", and then use props.isExpanded. The dot rule works because the children share the same object reference to props, so the properties are shared rather than just copied, just like in normal JavaScript object references.
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}"
>
<div ng-repeat="item in friendRow" ng-click="collapse()">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!props.isExpanded">
some content
</div>
</div>
JS:
$scope.collapse = function(){
this.props.isExpanded = !this.props.isExpanded;
};
Update
We keep going through more and more issues with your project. You really just need to experiment/research and understand everything that's going on on a deeper level, or it will just be one question after another. I'll give it one last effort to get you on the right track, but you need to try in the basic concepts and go from there.
You could get past the issue of props reinitializing by putting $scope.expandedStates and then passing the $index of the current ng-repeat to your function (or just using it in the view) and setting a property of expandedStates like $scope.expandedStates[$index] = !$scope.expandedStates[$index]. With the nested ng-repeat as it is, you'll need to do $parent.$index so that you're associating the state with the row rather than the item.
However, you'll then have another problem with the filter: Using my old partition code, the inputs inside the partitions are going to lose focus every time you type a character. Using the new code, the view updates, but the underlying model will not. You could use the partition filter from this answer to solve this, but from my understanding of that code, it could have some unexpected behavior down the road and it also requires passing in this as an argument to the filter. I don't recommend you do this.
Filters are meant to be idempotent, so stabilizing them via some kind of memoization is technically a hack. Some argue you should never do this at all, but I think it's fine. However, you definitely should ONLY do this when it is for display purposes and not for user input! Because you are accepting user input within the partitioned view, I suggest partitioning the data in the controller, then joining it back together either with a watch (continuous) or when you need to submit it.
$scope.partitionedFriends = partitionFilter($scope.friends, 2);
$scope.$watch('partitionedFriends', function(val) {
$scope.friends = [].concat.apply([], val);
}, true); // deep watch

dependent ng-models inputbox and select Angular js

I am having one text box and one dropdown.
I want filtering in ng-repeat section based on both of the above control.
Means if i select name in dropdown and in text box as I typing the filtering should work based on name
and if in dropdown if i select EployeeID then filtering should be work based on Employee.
<input id="txtSearchText" ng-model="searchText"/>
<select>
<option>name</option>
<option>EmployeeID</option>
</select>
<div ng-repeat="u in users">
name : {{u.name}} || EID: {{u.eid}} :
<!-- here i want result -->
</div>
The link that domakas directed you to above should help, but the example is slightly different since it filters using 3 different text input boxes.
You are missing a few things here. The first thing is you need to apply a filter to in the ng-repeat for the users div.
<div ng-repeat="u in users | searchText">
The problem that you run into here, which I am sure you are aware of is that it will filter on all of the values in the users array. You seem to want to only filter on the value in the users array that the dropdown has specified. This means we will have to tie the filter to the dropdown in some fashion to make it dynamic.
The way I would do this is to make the filter an object and not just pure text.
$scope.searchText = {name: '', eid: ''};
Now you need to have the model of your input box tied to this object, but where it stores the value needs to be dynamic based on what the value of the dropdown
<input id="txtSearchText" ng-model="searchText[filter]" />
<select ng-model="filter">
<option value="name" selected>name</option>
<option value="eid">EmployeeID</option>
</select>
The above code will store the value of the dropdown in the correct value of the searchText object. This means that the filter will now use this object to filter out the results in the users div instead of just a string, which it would compare to the full JSON object.
Edit:
Added a watch on the 'searchText' object to clear the other value if the dropdown is switched. Without this the old filter value was still in the 'searchText' object, which caused it to filter on both values.
$scope.$watch('filter', function() {
if($scope.filter == "eid") {
$scope.searchText.name = '';
} else {
$scope.searchText.eid = '';
}
});
Here is a JSFiddle with a working example:
http://jsfiddle.net/glandrum101/NM5A5/1/

Ambiguity in the use of ngRepeat

I have following problem with using of AngularJS ngRepeat.
The issue can be viewed in this jsFiddle.
http://jsfiddle.net/zono/9rmEs/2/
The user can choose character and after this get all combination
of chosen characters in alphabet. Eg:
A - A-B, A-C, A-D and etc.
B - B-A, B-C, B-D and etc.
Everithing works properly but when user change value of selected
character the combination does not get updated. I solved this problem
with adding following code.
<span style="display: none;">
{{item.combionations = getCombinations(item)}}
</span>
And "hack" it. But there must be normal solution.
I would be very grateful for any ideas and recommendations.
Best regards.
Update
In case you plan to do more complex calculations based on the selection this simplified approach would not work. In general it is also better to encapsulate state in some data structure. In your case you could design a structure like this:
{ letter: "A", combinations: ["A-B", "A-C", ... ] }
To update the combinations array you can use ng-change="updateItem(item)" and some update function. Whenever you change the selection the array combination gets updated:
$scope.updateItem = function(item) {
item.combinations = getCombinations(item.letter);
}
I put this in a new fiddle.
You can easily solve this issue by using the model you bound to ng-select in the ng-repeat.
In the select you used item.model. Angular will update its value in the scope whenever you change the selection.
<select data-ng-model="item.model" ng-init="item.model=allLetters[0]" ng-options="value for value in allLetters">
</select>
When you use the same scope variable in ng-repeat you should get the desired behavior.
<div ng-repeat="letter in allLetters">
{{item.model}}-{{letter}}
</div>
Take a look an the updated fiddle.
The problem is that you compute combionations once at the begenning (ng-init="item.combionations=getCombinations(item)"). After that it never gets updated when you change item.model.
You could solve this problem (and also make sure created[...].combionations is kept up-to-date) like this:
<div data-ng-repeat="item in created">
<div ng-repeat="combination in item.combionations = getCombinations(item)">
{{combination}}
</div>
...
See, also, this short demo.

Resources