Custom directive to populate dynamic select box AngularJS - angularjs

I've got the following controller that basically calls a http get service and assigns the data to a show_data scope variable
.controller('forms_controller', function($scope, Form) {
Form.get().then(function(data){
$scope.show_data = data;
});
});
The scope.show_data is then pushed to this view ..
<div ng-repeat="(key, value) in show_data | orderBy:'key' " >
<div ng-switch on="value.Type" >
<div ng-switch-when="attributes" >
<div ng-repeat="(key2, value2) in value | orderBy:'key' ">
<div ng-switch-when="Date" >
<label class="item item-input">
<span class="input-label">{{value2.Label}}</span>
<input identifier="{{value2.Identifier}}" type="date">
</label>
</div>
<div ng-switch-when="Select" >
<label class="item item-input item-select">
<div class="input-label">
{{value2.Label}}
</div>
<select codelist="{{value2.CodeList}}" identifier="{{value2.Identifier}}" >
</select>
</label>
</div>
</div>
</div>
</div>
Which basically checks the data based on the input type and spits out the different form elements based on the data. My problem is I am faced with is with the select box... Each select box has an id [codelist in this case] to the ng-options that should be displayed however, I'd first need to make another http get call to fetch this data before populating the ng-options ...
Please also note that there might be more then one select box per form.
I was thinking of using some kind've custom directive to achieve this?
Any help would be highly appreciated :)
Thank You

Your problem is that the server sends you an "incomplete" object. You need to do additional get requests to "complete" your object.
So this is what you should do: in your controller, iterate over your object and do the additional get-requests and store the results in something that you can call in your html with value2.items.
You don't have to wait with setting $scope.show_data untill all the data is fetched. Angular's two-way binding will synchronize the html as soon as the additional info is available.
Something like this should work.
var n = 0;
for (;n < data.length; n += 1) {
var values = data[n].value
var i = 0;
for (;i < values.length; i += 1) {
// GET based on values[i].CodeList
$http.get(...)
.success(function (data2) {
values[i].items = data2
// Retrieveable in HTML as value2.items
});
}
}

Related

Refresh ng-repeat data

I have a big list containing all of my data, and I have another shorter list containing only the selected data.
Originally, all the data is selected so both lists are identical, and the user must uncheck the checkbox of the unwanted data, and then the unchecked data must disappear from the selected_data_list.
When the page is loaded, the two lists are being filled correctly. But when I uncheck the unwanted data, the selected_data_list is not updating.
I tried to use $scope.$apply(), but it gave me this error:
Error: $rootScope:inprog
Action Already In Progress
Here is my code:
Complete list:
<div ng-controller="BrainRegionsCtrl">
<div ng-repeat="region in brain_regions">
<label>
<input type="checkbox"
name="checkbox_brain_region__included"
id="checkbox_brain_region_{/region.id/}"
value="{/region.id/}"
ng-model="region.show"
ng-change="update_selected_Brain_regions(region)">
<b>{/region.name/}</b>
</label>
</div>
</div>
Selected regions list:
<div ng-controller="BrainRegionsCtrl">
<label ng-repeat="region in selected_brain_regions">
<input type="radio" name="radio_brain_regions_graphical_search_soma" value="{/region.id/}">
<b>{/region.name/}</b>
</label>
</div>
Controller:
ngApp.controller('BrainRegionsCtrl', function ($scope, $http) {
$scope.brain_regions = [];
$scope.selected_brain_regions = [];
$scope.update_selected_Brain_regions = function (region) {
for (var i = 0; i < $scope.brain_regions.length; i++) {
var current_region = $scope.brain_regions[i];
if (current_region.id === region.id) {
if (region.show) {
$scope.selected_brain_regions.push(current_region);
}
else {
$scope.selected_brain_regions.splice(i,1);
}
}
}
console.log($scope.selected_brain_regions);
};
});
Here is a jsfiddle
Thank you.
Just try to use $rootScope.
Because you give the ng-controller="BrainRegionsCtrl" for two div that's why the $scope.selected_brain_regions will go to the initial state in the second div.
OR
you should give the ng-controller="BrainRegionsCtrl" to the root div which contains the two parts of your code..
Please check with this code and let me know whether it solves your issue. Also please check this working plnkr of your example scenario.
Template:
<div ng-repeat="region in brain_regions">
<label>
<input type="checkbox"
name="checkbox_brain_region[$index]"
id="checkbox_brain_region[region.id]"
value="region.id"
ng-model="region.show"
ng-change="update_selected_Brain_regions(region)">
<b>{{region.name}}</b>
</label>
</div>
<br>
<label ng-repeat="region in selected_brain_regions">
<input type="radio" name="radio_brain_regions_graphical_search_soma[$index]"
ng-model="region.show" value="region.id">
<b>{{region.name}}</b>
</label>
Controller:
$scope.update_selected_Brain_regions = function (region) {
for (var i = 0; i < $scope.brain_regions.length; i++) {
var current_region = $scope.brain_regions[i];
if (current_region.id === region.id) {
if (region.show) {
$scope.selected_brain_regions.push(current_region);
} else {
var index = $scope.selected_brain_regions.map(function(x){ return x.id; }).indexOf(current_region.id);
$scope.selected_brain_regions.splice(index,1);
}
}
}
};
AngularJS promises already call .apply() on their callbacks. You cannot call .apply again inside the callbacks since they're already being .applied, and the error "In Progress" tells exactly that. Just remove the .apply call and it will work, unless you have further errors.
Your issue because of index update, so try track by $index, should work.
<label ng-repeat="region in selected_brain_regions track by $index">
<input type="radio" name="radio_brain_regions_graphical_search_soma" value="{{region.id}}">
<b>{{region.name}}</b>
</label>
Check out jsFiddle

AngularJS protractor test in nested ng-repeat

I have nested ng-repeat like this. I am the doing protractor test. So I want to access the first index of parent repeater.
<div ng-repeat="pChild in mainParent | orderBy : 'order'" ng-show="checkfor.qm">
<div ng-repeat="cChild in Child ">
<input id="checkbox-01" type="checkbox" class="ng-valid ng-dirty ng-valid-parse ng-touched ng-not-empty">
</div>
</div>
So, I want to select first child of mainParent and inside it, I want to calculate length of Child.
I am new to protractor test. Please help me. Thanks in Advance
I am assuming that you want to calculate the length of cChild of first child of mainParent. You can use below code.
<div ng-repeat="pChild in mainParent| orderBy : 'order'" ng-show="checkfor.qm" ng-init="parentIndex = $index">
<div ng-repeat="cChild in pChild| orderBy:'iseeit__Priority__c' ">
<span ng-if="parentIndex === 0">cChild.length<span>
</div>
</div>
instantiate the parent index with some local variable, and check if it's the first element.
Let me know, if you are looking for something else.
var elm = element(by.repeater("pChild in mainParent").row(0)).all(by.repeater("cChild in Child")).map(function (elm) {
return elm.element(by.css("input#checkbox-01")).getAttribute("class");
}).then(function (links) {
console.log(links.length);
})
links.length have provided the length.

How to update scope on form Submit, when ng-model value changes?

I am creating an edit customer form and passing an object of 30 key value pairs. I am then using ng-repeat to populate input fields of a form. All form is displayed nicely and with passed key, values. I can also change the value of any input field.But when I submit the form, it takes the old scope object which I passed initially in the form, leaving behind my changes. I need to submit my changed object Now. How To do that? I searched much but can't find the solution to this.
var app = angular.module("customerModule", []);
app.controller('crudController', function($scope, $http) {
$scope.Customers = //object from Ap with almost 30 key,values
$scope.edit = function() {
console.log($scope.Customers);
//the above line prints the API called object where as I am editing the values of ng-model in my form. and I need the form submitted values
}
});
<div ng-app="customerModule" ng-controller="crudController">
<form name="as" ng-submit="edit()">
<ul>
<li ng-repeat="(key, val) in Customers " ng-hide="(key=='total' || key=='paid' || key=='customfields' || key=='owing')" ng-if="key!='customfields'">
<label class="label"> {{key}}</label> <input type="text" ng-model="val" />
</li>
<li ng-repeat="(key, val) in Customers.customfields">
<label class="label"> {{key}}</label> <input type="text" ng-model="val" />
</li>
<button type="submit"><i class="fa fa-plus-circle" aria-hidden="true"></i><span> Edit Customer</span></button>
<ul>
</form>
</div>
Use ng-model="Customer[key]".
You're using the local variable val referencing the value. That's basically aquivalent to doing
var val = Custom['foo'];
val = 'newValue';
That won't change the value of Custom['foo'], right?
But the following will:
Custom['foo'] = 'newValue';

Cannot save/push array of objects to parse backend

I was attempting to add an array to the parse.com backend. I created an array:
var addHoles = function () {
$scope.holeArray = [];
for (var i = 0; i < 18; i++) {
$scope.holeArray[i] = {
number: i + 1,
par: '',
distance: '',
handicap: ''
};
}
console.log($scope.holeArray);
return $scope.holeArray;
};
and then used ng-repeat to display the array and ng-model to allow the user to change the array.
<div ng-repeat="hole in holeArray" class="row">
<div class="form-group">
<div class="col-md-2">
<div class="form-group form-md-line-input">
<input type="text" class="form-control" id="form_control_1" ng-model="hole.number" readonly>
<label for="form_control_1">Hole Number</label>
</div>
</div>
I was getting an unusual error from parse. and could not get parse to save the array.
It turns out the issue was that when you use ng-repeat angularjs adds a $$hashKey. This $$hasKey conflicts with parse's backend formatting requirements. So, to fix the issue all I had to do was use "track by" in my ng-repeat.
<div ng-repeat="hole in holeArray track by hole.number" class="row">
This took me hours to figure out. Hopefully it saves someone else time and headache.

AngularJS - dynamic naming using $index in expression

I have a directive in which i create many ng-form's and i name each form based on the $index (from the ng-repeat). My problem is that i want to show an error container (that contains the error message) when the form is invalid but i cant find how to properly reference the form.
Here is my code:
<ng-form name="innerForm{{$index}}">
<label ... ><input name="input" ... />
<div class="error-container" ng-show="'innerForm'+$index.input.$invalid">
// show the error message
</div>
</ng-form>
I want 'innerForm'+$index.input.$invalid to be evaluated as innerForm5.input.$invalid for example.
I have made many attempts but i can't get it to work. What is the correct way to reference my dynamically named form?
please see here : http://jsbin.com/jocane/4/edit
<div ng-controller="firstCtrl">
<div ng-repeat="object in allMyObjects">
<ng-form name="innerForm{{$index}}">
<input type="text" ng-model="object.name" required name="userName">
<div ng-show="{{'innerForm'+$index}}.userName.$invalid">
error
</div>
</form>
</div>
First thing is that, ng-form creates an isolated scope. So if you give a ng-form static name, it will be ok. No need to append $index or anything.
ng-show="innerForm.input.$invalid"
Code snippet to find form controller using element
var elmParent = $(domEle).parents('form, ng-form, [ng-form]');
var formCtrl = null;
var elemCtrl = null;
if (elmParent.length) {
var elm = angular.element(domEle);
formCtrl = elm.scope().$parent[elmParent.attr('name') || elmParent.attr('ng-form')];
}
Hope this will help

Resources