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
Related
What I want is a form that I can use for both creating and updating. So I pass before showing
$scope.form = {};
$scope.car = null;
$scope.getCar = function(hash) {
$http.get('/cars/'+hash).success(function(car) {
$scope.car = car;
$scope.form = car;
});
};
As you can see I add the result of the get to both car and form.
Now I'm opening the View:
<h1>{{ form.name }} <small>shows correctly</small></h1>
But a line after that I'm trying almost the same:
<form class="list" ng-submit="createOrUpdateForm(form)">
<label class="item">
<span class="input-label">Name</span>
<input type="text" ng-model="form.name">
Here it's not shown... But when I add the same line after it like this:
<input type="text" ng-model="car.name">
This does work, but then I can't use the ng-submit anymore, because that references to form.
Form some reason I can't set the form scope?
You should not manual assigning anything to form. A "form" is not the same as the data you manage using the form. Neither the empty object {} nor car make sense in that context.
Give the form a name, this will allow angular to assign it to a scope property.
<h1>{{ car.name }} <small>shows correctly</small></h1>
<form name="carForm" ng-submit="createOrUpdateForm(carForm)">
<label class="item">
<span class="input-label">Name</span>
<input type="text" ng-model="car.name">
$scope.createOrUpdateForm = function(form) {
if(form.$valid) {
console.log($scope.car.name);
// POST / PUT your data.
}
};
I'm getting this error:
Error: Firebase .push failed: first argument contains undefined property 'price'
This is my controller:
angular.module('starter')
.controller('EditPicsController', function($scope,$location,$state) {
var itemsRef = new Firebase('https://myapp.firebaseio.com/items');
var itemprice = this.price
$scope.createItem = function(itemprice){
console.log(itemprice);
var newItemRef = itemsRef.push({'price':itemprice});
};
});
And my template:
<form>
<div class="list">
<label ng-model="price" class="item item-input item-stacked-label">
<span class="input-label">Price</span>
<input type="text" placeholder="$200.00" >
</label>
</div>
<button ng-click="createItem(price)" class='button button-dark'>
test-additem
</button>
</form>
my console.log output is blank.
What's going on here?
You should apply ng-model to input rather label.
<input ng-model="price" type="text" placeholder="$200.00" >
instead of
<label ng-model="price" class="item item-input item-stacked-label">
And in your code you've created a object of a class i.e. itemsRef. Its an object not an array. So because of this line itemsRef.push({'price':itemprice}); you're getting error. push is for arrays not for object.
You don't seem to use angularJS in its entirety.
As mentioned in the other answer you should set ng-model on input element rather than label because then you can leverage the angularJS philosophy of two-way data binding.
This is because user can interact with your client-side application in angularJS and can only change the value of HTML elements which are editable.
So those values are ng-model and will be binded to your controller and exposed through the glue called $scope in your controller.
So your HTML will be:
<form>
<div class="list">
<label class="item item-input item-stacked-label">
<span class="input-label">Price</span>
<input ng-model="price" type="text" placeholder="$200.00" >
</label>
</div>
<button ng-click="createItem()" class='button button-dark'>
test-additem
</button>
And your controller code:
angular.module('starter')
.controller('EditPicsController', function($scope,$location,$state) {
$scope.itemsRef = new Firebase('https://myapp.firebaseio.com/items');
$scope.createItem = function(){
var newItemRef = $scope.itemsRef.push({'price': $scope.price});
};
});
Also if your $scope.price is undefined then Firebase will complain because it wont allow you to save values which are undefined so set some Form validation or initialise the ng-model ($scope.price) with some value to test.
UPDATE: Your code $scope.itemsRef.push({'price': $scope.price}); for saving to Firebase is absolutely correct its just that Firebase doesn't like undefined in javascript so your model $scope.price needs to have a value to get this working
Since Firebase does not like undefined attributes like this:
item = { 'name': 'hot dog', 'price': undefined };
itemsRef.push(item);
I do a quick and dirty clean up of my objects before I push
for( let attr in item){
try {
if( item[attr] == undefined ) delete item[attr];
} catch(e){}
}
itemsRef.push(item);
which will push
{ 'name': 'hot dog' }
and firebase is happy :-) ..
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
});
}
}
I have a group object that contains a list of video
And when you edit the object, i give the HTML a list of all videos, where you can search in.
But if you don't search I want it to show only the videos that are checked.
here is the HTML used
<div class="input-field">
<input type="text" id="filter" ng-model="query" ng-change="hasVideos()" />
<label for="filter"><i class="mdi-action-search"></i></label>
</div>
<ul class="collection">
<li class="collection-item" ng-repeat="video in filterdVideo = (videos | filter:query) ">
<p>
<input type="checkbox" id="video_{{video.Id}}" checklist-model="group.Keywords" checklist-value="video.Id">
<label for="video_{{video.Id}}">
{{video.Title}}
</label>
</p>
</li>
</ul>
So how can I make it that it only shows the video list where the checkbox is checked? and when the query isn't empty to ignore that and show all.
I will try to improve my answer later.
But for a quick guess you might be able to use ng-if and let the controller handle if something should be listet.
<li ng-if="functionthatreturnstrueorfalsebasedonyourneeds" class="collection-item" ng-repeat="video in filterdVideo = (videos | filter:query) ">
Checkbox example can be found in the docs:
https://docs.angularjs.org/api/ng/directive/ngIf
Fixed it by doing a filter:
filter('filterStack', function () {
return function (inputs, filterValues, showAll) {
var output = [];
if (showAll) return inputs;
angular.forEach(inputs, function (input) {
angular.forEach(filterValues, function (filterVal) {
if (filterVal.Id == input.Id) {
output.push(input);
}
});
});
return output;
};
})
but having an other issue now, when loading it in with data: https://stackoverflow.com/questions/29749006/angular-checklist-model-not-checking
I have four input fields which I'm using to add properties to a single object using ng-model= model.propertyName. I have a series of check boxes that I'm creating with ng-repeat that I could not figure out how to add unique propertyNames for each ng-model as they were created with the ng-repeat. As a work-around(or maybe this is correct, I'm not sure) I was able to write a function to add the checked items to an array. I was then trying to use a for-loop to iterate over the array and add each selected propertyName(string) to the ng-model object as a new property using a ng-click to call the function.
As-is when I click the "Add Technician" button I get the following error output:
TypeError: Cannot read property 'selection' of undefined
at Scope.$scope.addTechnician (..../scripts/controllers.js:
This occurs because $scope is undefined inside the conditional of my for loop in the addTechnician function. I can't understand why because when I pass $scope to the addTechnician function it recognizes the newTech inside the for loop. When I don't pass $scope to the addTechnician function it says newTech is undefined with the following error:
TypeError: Cannot set property 'cert1' of undefined
at Scope.$scope.addTechnician(.../scripts/controllers)
I'm pretty sure this has something to do with the way ng-repeat creates a new scope, which prototypically inherits from the parent scope. But again, I'm not sure.
Here is my controller
use strict';
angular.module('Carrepair2.controllers', [])
.controller('SetupCtrl', function($scope) {
$scope.certifications = [
{'name':'Engine Repair'},
{'name':'A/T & Transaxle'},
{'name':'Manual Drive Train & Axles'},
{'name':'Suspension & Steering'},
{'name':'Brakes'},
{'name':'Electrical & Electronic Systems'},
{'name':'Heating & Air Conditioning'},
{'name':'Engine Performance'},
{'name':'Light Vehicle Diesel Engines'}
];
// selected certifications
$scope.selection = [];
$scope.toggleCert = function(name) {
var idx = $scope.selection.indexOf(name);
//is currently selected
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
//is newly selected
else{
$scope.selection.push(name);
}
};
$scope.addTechnician = function($scope) {
for(var i=0; i < $scope.selection.length - 1; i++){
$scope.newTech['cert' + (i + 1).toString()] = $scope.selection[i].name;
}
};
})
Here is my template
<form class="col-md-6">
<div class="input_wrapper">
<input type="text" name="first-name" ng-model="newTech.firstName" required>
<label for="first-name">First Name</label>
</div>
<div class="input_wrapper">
<input type="text" name="last-name" ng-model="newTech.lastName" required>
<label for="last-name">Last Name</label>
</div>
<div class="input_wrapper">
<input type="email" name="email" ng-model="newTech.email" required>
<label for="email">Email</label>
</div>
<div class="input_wrapper">
<input type="tel" name="phone" ng-model="newTech.phone" required>
<label for="phone">Phone</label>
</div>
<h5>Check all held ASE certifications</h5>
<ul class="list">
<li class="item item-checkbox" ng-repeat="certification in certifications">
<label class="checkbox">
<input type="checkbox" value="{{certification.name}}" ng-checked="selection.indexOf(certification.name) > -1" ng-click="toggleCert(certification)">
</label>
{{certification.name}}
</li>
</ul>
<button class="button button-block" ng-click="addTechnician()">Add Technician</button>
</form>
Ideally after the "Add Technician" button is clicked I want to end up with an object, my newTech ng-model object, that has the input field data and the properties from the checked items. Here is a jsFiddle with simplified code replicating the problem
http://jsfiddle.net/aq93z/7/
I solved it. I had to format my loop using angular.forEach(values, function(value, index){here is the jsFiddle with the solution http://jsfiddle.net/aq93z/9/. If you look at the $scope.newTech object created in the console the checkbox selections are added as properties of the newTech ng-model object.