Display checkboxes in with AngularJS based on two object arrays - javascript

I know there are several similar topics already but I found none that really matches my problem.
When opening my AngularJS app/website I am loading two arrays of objects (both are of the same type). One list contains all possible values (called sources here) and the other list is a selection of elements from the first.
My goal is display all sources as checkboxes. The ones from the second list have to be preselected, the rest not. Now the user can select/deselect checkboxes. If he does so I need to inform the server (with the source.id).
What I got so far:
exampleApp.controller('testController', [ '$scope', '$http', function($scope, $http) {
$scope.sources = [];
$scope.selectedSources = [];
$scope.changeSource = function(source) {...};
})
and
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="??"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
What I can't figure out is how I can get ng-model to preselect the right checkboxes and how to get the new (and old) values to changeSource(). Is there an elegant way of doing that?
Example (Pseudo code only):
Sources = [{id=1, name=test1},{id=2, name=test2}, ...]
SelectedSources = [{id=2, name=test2}]
Now what I need are checkboxes like this:
[ ] test1 [x] test2
where all elements from sources are checkboxes and the ones from selectedsources are preselected. Changes of the selection can be stored in selected sources (as objects) and have to trigger my changeSource() function so that I can inform my server.

Set the selected/unselected state in a property inside each of the objects in Sources array(initialize it based on whats present in selectedArray)
$scope.sources.forEach(function(source) {
source.selected = isSelected(source);
})
function isSelected(selectedSource) {
return !!$scope.selectedSources.find(function(s) {
return s === selectedSource || s.id == selectedSource.id;
})
}
Here's a working plunker link

I didn't understood your question very well, but if i'm not mistaken, you want to fill the second collection only with the selected items from the first one, right? If it's the case, you could turn your second collection into a returning function with a filter of the first inside, as follows:
In your controller:
exampleApp.controller('testController', [ '$scope', '$http', function ($scope, $http) {
$scope.sources = [];
/* ... */
$scope.getSelectedSources = function () {
return $scope.sources.filter(function (val) {
return val.selected;
});
};
})
In your template:
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
<div ng-repeat="source in getSelectedSources()">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>

Hi this may be help you to get new & old value.
$scope.$watch('sources', function (oldval, newval) {
console.log(oldval + newval);
});

Related

All the checkboxes inside a ng-repeat are getting checked when I select just one

I have list of objects named rolePermissionList like this:
[{"id":1,"name":"createUser","type":"user","marked":1},{"id":2,"name":"deleteUser","type":"user","marked":1},{"id":3,"name":"editRole","type":"role","marked":0}]
and I use ng-repeat to repeat checkboxes using the values in that list like this
<div class="form-group">
<label>Role Permissions:</label>
<div class="checkbox" ng-repeat="permission in rolePermissionList">
<label>
<input type="checkbox" ng-model="idsPermission[permission .idPermission ]"
ng-checked="permission.checked">{{permission.name}}
</label>
</div>
</div>
the ng-model of the checkboxes is named idsPermission and it's a list of numbers, those numbers are the IDS of the objects.
When I load the page the checkboxes that are supposed to be checked are checked this part works fine, but when I check another checkbox all the checkboxes gets checked, and when I uncheck a checkbox the same thing happens all the checkboxes gets unchecked.
I use that list of numbers named idsPermission to get all the IDS of the checkboxes that are checked, this worked before I used the directive ng-checked="permission.checked", but now I need to use it since now I need to show the checkboxes that are already marked.
this is my controller
angular.module('MyApp')
.controller('RolCtrl', ['$scope', 'RolService',
function ($scope, RolService) {
$scope.idsPermission = {};
$scope.getListCheckBoxesEditRole = function (idRole) {
$scope.selectRol.descripcion;
RolService.getListCheckBoxesEditRole(idRole)
.then(
function (d) {
var userPermissionList = [];
for (var permission in d) {
if (d[permission ].type === 'user') {
if (d[permission ].marked === 1)
{
d[permission ].checked = true;
userPermissionList.push(d[permission ]);
} else {
userPermissionList.push(d[permission ]);
}
}
}
$scope.rolePermissionList = userPermissionList;
},
function (errResponse) {
console.error('ERROR');
}
);
};
}
$scope.getListCheckBoxesEditRole(3);
]);
The RolService.getListCheckBoxesEditRole(idRole) service returns this JSON [{"id":1,"name":"createUser","type":"user","marked":1},{"id":2,"name":"deleteUser","type":"user","marked":1},{"id":3,"name":"editRole","type":"role","marked":0}]
and what I do in the controller is iterate over that list and check if the marked field is 1 if it's 1 I do this d[permission ].checked = true; I what I think that I do in that line is setting the checked value to true so I could use this directive in the html view ng-checked="permission.checked"
I tried doing this ng-checked="idsPermission[permission.checked]" but when I do this the values that are marked=1 in the JSON that I paste above don't appear checked when I load the page, but if I put it like this ng-checked="permission.checked" they appear marked as they should, but when I click a checkbox all the checkboxes gets selected.
I came across too many issues to document but the main problem was how you are iterating through the array that is returned from the service. It should be something like this:
Controller
angular.forEach(d.data, function(permission) {
if (permission.type === 'user') {
if (permission.marked === 1) {
permission.checked = true;
userPermissionList.push(permission);
} else {
userPermissionList.push(permission);
}
}
});
Then you can simplify your html like this:
HTML
<input type="checkbox" ng-model="permission.checked" />
You can see all of the changes in this working plunk.
I can't see in your code that $scope.idsPermission is getting defined. In ng-repeat you only set the key for the object but the value is undefined. That's why the checkbox won't show the correct value.
You could use ng-init to initialize the model. Please have a look at the simplified demo below or this fiddle.
(Also defining the models in your controller would be possible.)
Only using ng-model should be enough for the checkbox to work. I think I've read somewhere that ng-checked and ng-model aren't working smoothly together.
angular.module('demoApp', [])
.controller('mainCtrl', MainCtrl);
function MainCtrl() {
var vm = this;
angular.extend(vm, {
data: [{
id: 0,
marked: 1
}, {
id: 1,
marked: 0
}, {
id: 2,
marked: 1
}]
})
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp" ng-controller="mainCtrl as ctrl">
<div ng-repeat="item in ctrl.data">
<input type="checkbox" ng-init="ctrl.idPermissions[item.id] = !!item.marked" ng-model="ctrl.idPermissions[item.id]"/>{{item.id}}
</div>
<pre>
permissions: {{ctrl.idPermissions | json: 2}}
data{{ctrl.data | json: 2}}</pre>
</div>

Semantic UI dropdown with angular not clear selected value

I've created a small sample of what is happening.
http://plnkr.co/edit/py9T0g2aGhTXFnjvlCLF
Basically, the HTML is:
<div data-ng-app="app" data-ng-controller="main">
<select class="ui dropdown" id="ddlState" data-ng-options="s.name for s in states track by s.id" data-ng-model="selectedState"></select>
<select class="ui dropdown" id="ddlCity" data-ng-options="c.name for c in cities track by c.id" data-ng-model="selectedCity"></select>
</div>
And the javascript is:
angular.module("app", [])
.controller("main", function($scope, $timeout) {
$scope.selectedState = {id:1,name:"A"};
$scope.selectedCity = {id:1,name:"A.1",stateId:1};
$scope.states = [{id:1,name:"A"},{id:2,name:"B"},{id:3,name:"C"}];
var fakeDataSource = [
{id:1,name:"A.1",stateId:1},
{id:2,name:"A.2",stateId:1},
{id:3,name:"A.3",stateId:1},
{id:4,name:"B.1",stateId:2},
{id:5,name:"B.2",stateId:2},
{id:6,name:"B.3",stateId:2},
{id:7,name:"C.1",stateId:3},
{id:8,name:"C.2",stateId:3},
{id:9,name:"C.3",stateId:3}
];
$scope.$watch("selectedState", function(n,o){
if (n !== o)
$scope.selectedCity = null;
$scope.cities = fakeDataSource.filter(function(x){
return n.id === x.stateId;
});
$timeout(function(){
$(".ui.dropdown").dropdown().dropdown("refresh");
});
})
$timeout(function(){
$(".ui.dropdown").dropdown();
})
})
The problem is when I change the first dropdown to value 'B' or 'C', the value of second dropdown does not change, even it is changed in angular model.
You guys can notice that I've the line $(".ui.dropdown").dropdown().dropdown("refresh") to refresh the values but does not work.
I tried destroy and recreate using $(".ui.dropdown").dropdown("destroy").dropdown() but still does not work.
Any help?
Simply using ngModel won't make the values change dynamically. Take a look at the documentation here: https://docs.angularjs.org/api/ng/directive/ngModel
You can bind the values using ngBind or what I have done is do an onChange to then check the value and change your second drop down accordingly. Something like:
$("#ddlState").on("change", function(e) {
//check $scope.selectedState for it's value, and change #ddlCity/$scope.selectedCity accordingly
});

collect filtered ng-repeat data to use in controller

I am trying to build a picture library in a responsive grid with data coming down from a Mongo DB. Data from db is in this form. name, image, keyword, bio, reference", searchable,
I start with an ng-repeat to display category selection checkboxes based on the picture keywords. This seems to work fine.
p CATEGORIES:
span.formgroup(data-ng-repeat='category in mongoController.catData')
input.checks( type='checkbox', ng-model='mongoController.filter[category]')
| {{category}}
Here is the factory that sorts and identifies the keyword checkboxes:
getCategories: function (cat) {
return (cat || []).map(function (pic) {
return pic.keyword;
}).filter(function (pic, idx, array) {
return array.indexOf(pic) === idx;
});
}
From there I filter an ng-repeat to display pictures based on the checkbox selection, and/or a search field which works well also.
input.form-control.input-med(placeholder="Search" type='text', ng-model="search.searchable")
br
div.light.imageItem(data-ng-repeat="picture in filtered=( mongoController.allData | filter:search | filter:mongoController.filterByCategory)")
a(data-toggle="modal", data-target="#myModal", data-backdrop="static", ng-click='mongoController.selectPicture(picture)')
img( ng-src='../images/{{picture.image}}_tn.jpg', alt='Photo of {{picture.image}}')
p Number of results: {{filtered.length}}
Functions to display picture lists:
//Returns pictures of checkboxes || calls the noFilter function to show all
mongoController.filterByCategory = function (picture) {
return mongoController.filter[picture.keyword] || noFilter(mongoController.filter);
};
function noFilter(filterObj) {
for (var key in filterObj) {
if (filterObj[key]) {
return false;
}
}
return true;
}
If you click one of the pictures a modal box pops up with all of the input fields where you can edit image specific fields.
The part I am really struggling with is how to gather the usable data from the filtered ng-repeat to use in the controller so when the modal box is up I can scroll right or left through the other pictures that met the criteria of the search.
Any suggestions would help, even why the hell are you doing it this way?
When you declare the ng-repeat that filters the pictures, your filtered variable belongs to the current $scope (which I cannot infer from the question, as it stands). You could associate the filtered variable to a specific controller by using Controller As syntax: (i.e. using <elem ng-repeat="picture in ctrl.filtered = (ctrl.data | filter1 | filterN)"/>)
Example: (also in jsfiddle)
var mod = angular.module('myapp', []);
mod.controller('ctrl', function() {
var vm = this;
vm.data = ['alice', 'bob', 'carol', 'david', 'ema'];
vm.onbutton = function() {
// access here the filtered data that mets the criteria of the search.
console.log(vm.filtered);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="ctrl as vm">
<input ng-model="search" type="text" />
<p data-ng-repeat="item in vm.filtered = (vm.data | filter:search)">
{{item}}
</p>
<p>Items: {{vm.filtered.length}}</p>
<button ng-click="vm.onbutton()">Show me in console</button>
</div>

How to create own angular service with XHR properly?

I am very new about AngularJS things. Need to do file upload with other datas in form, I found some scripts and angular plugins but I am using my own service calls $xhr. I was able to send file but i got error, bug(not real error-bug, i just named like that) or i can not use AngularJS properly. Here it is:
.
JS
var app = angular.module('ngnNews', []);
app.factory('posts', [function () {...}]); // I reduced the codes
app.factory('$xhr', function () {
var $xhr = { reqit: function (components) { ... //My Xml HTTP Request codes here }}
return $xhr;
});
app.controller('MainCtrl', ['$http','$scope','$xhr','posts',
function ($http, $scope, $xhr, posts) {
$scope.posts = posts.posts;
$scope.files = [];
var newPost = { title: 'post one', upvotes: 20, downvotes: 5 };
$scope.posts.push(newPost);
$scope.addPost = function () {
$xhr.reqit({
form: document.getElementById('postForm'),
callbacks: {
success: function (result) {
if (result.success) {
console.log($scope.posts); //[FIRST OUT]
$scope.posts.push(result.post);
$scope.title = '';
console.log($scope.posts); //[SECOND OUT]
}
}
},
values: { upvotes: 0, downvotes: 0 },
files: $scope.files
});
...
}
}]);
.
HTML
<form action="/Home/FileUp" id="postForm" method="post" enctype="multipart/form-data">
<div class="form-group input-group">
<span class="input-group-addon">Post Title</span>
<input name="title" class="form-control" type="text" data-ng-model="title" />
</div>
<ul>
<li ng-repeat="file in files">{{file.name}}</li>
</ul>
<button class="btn btn-primary" type="button" data-ng-click="addPost()">Add New</button>
</form>
SCREEN
Sample post displayed in list
.
PROBLEMS
When I click first time Add New button everything works well until $scope.posts.push(result.post);. In console, [SECOND OUT] is here:
First object has $$hashKey but second object which sent from server(added by $scope.posts.push(result.post); function) doesn't have. I want to know why is this happening? But it's not only weird thing, when I second time click Add New button, everything completed successfully (No new logs in console, adding new post to list shown screen image above).
MAIN PROPLEM
I pushed returned value from the server but post list(in screen) is not affected when first click.
QUESTIONS
- What is happening? or
- What am I doing wrong? Thanks for any explanation.
You are doing nothing wrong with respect to $$hashkey if that is your concern. When you use ng-repeat with array of objects angular by default attaches a unique key to the items which is with the property $$hashkey. This property is then used as a key to associated DOM elements with the corresponding item in the array by identity. Moving the same object in array would move the DOM element in the same way in the DOM. You can avoid this (addition of additional property on the object by angular) by using track by with ng-repeat by providing a unique key on the object or a mere $index. So with that instead of creating a unique key and attaching it to $$haskey property angular will use the unique identifier you have provided to associate the DOM element with the respective array item.
ng-repeat="post in posts track by $index"
or (id you have a unique id for each of the object in the array, say id then)
ng-repeat="post in posts track by post.id"
And since you say you are using my xml http request code here, i am assuming it is not within the angular context so you would need to manually perform the digest cycle by using $scope.$apply() is on of those ways.
$scope.addPost = function () {
$xhr.reqit({
form: document.getElementById('postForm'),
callbacks: {
success: function (result) {
if (result.success) {
$scope.posts.push(result.post);
$scope.title = '';
$scope.$apply();//<-- here
}
}
},
But ideally you could wrap your xhr implementation with a $q and if you pass $q promise from your api, you wont need to perform a manual $scope.$apply() everywhere. Because $q promise chaining will take care of digest cycle invocation.

delete row(s) from ng-grid table from button

I have a table with ng-grid, and the problem is that i'm not sure how to collect the selected row(s) id or variable to pass into my delete function.
here is a quick mockup of what i'm trying to do
http://plnkr.co/edit/zy653RrqHmBiRJ7xDHlV?p=preview
the following code is from my html, a clickable delete button that takes in 2 parameters, the array of checkbox ids and the index at the corresponding table. This delete method was obtained from this tutorial : http://alexpotrykus.com/blog/2013/12/07/angularjs-with-rails-4-part-1/
<div class="btn-group">
<button class="my-btn btn-default button-row-provider-medical-services" ng-click="deleteProviderMedicalService([], $index)">Delete</button>
</button>
</div>
<div class="gridStyle ngGridTable" ng-grid="gridOptions">
</div>
The following code grabs the json data from a url, queries it and returns it. It also contains the delete function that gets called from the controller in the html page.
app.factory('ProviderMedicalService', ['$resource', function($resource) {
function ProviderMedicalService() {
this.service = $resource('/api/provider_medical_services/:providerMedicalServiceId', {providerMedicalServiceId: '#id'});
};
ProviderMedicalService.prototype.all = function() {
return this.service.query();
};
ProviderMedicalService.prototype.delete = function(providerId) {
this.service.remove({providerMedicalServiceId: providerId});
};
return new ProviderMedicalService;
}]);
The following is my controller(not everything, just the most important bits). $scope.provider_medical_services gets the json data and puts it into the ng-grid gridOptions.
After reading the ng-grid docs, i must somehow pass the checkbox ids from the selectedItems array and pass it into html doc to the delete function. Or, i'm just doing this completely wrong, as i hacked this together. Solutions and suggestions are greatly appreciated
(function() {
app.controller('ModalDemoCtrl', ['$scope', 'ProviderMedicalService', '$resource', '$modal', function($scope, ProviderMedicalService, $resource, $modal) {
var checkBoxCellTemplate = '<div class="ngSelectionCell"><input tabindex="-1" class="ngSelectionCheckbox" type="checkbox" ng-checked="row.selected" /></div>';
$scope.provider_medical_services = ProviderMedicalService.all();
$scope.deleteProviderMedicalService = function(ids,idx) {
$scope.provider_medical_services.splice(idx, 1);
return ProviderMedicalService.delete(ids);
};
$scope.gridOptions = {
columnDefs: [
{
cellTemplate: checkBoxCellTemplate,
showSelectionCheckbox: true
},{
field: 'name',
displayName: 'CPT Code/Description'
},{
field: 'cash_price',
displayName: 'Cash Price'
},{
field: 'average_price',
displayName: 'Average Price'
},
],
data: 'provider_medical_services',
selectedItems: []
};
i think the easiest option is pass an (array index) as data-id to your dom, which u can pick it from there.
{{$index}} is a variable you can use in ng-repeat
======= ignore what i said above, since i normaly writes my own custom stuff ======
I just had a look at ng-grid, i took their example. i've added a delete all selected function, as well as someone elses delete current row function ( these is pure angular way ) to see the code, hover over the top right corner < edit in jsbin >
http://jsbin.com/huyodove/1/
Honestsly i don't like it this way, you would be better off use something like lodash to manage your arrays and do your own custom grid. Using foreach to find the row index isn't good performance.
In their doc, it says you can change the row template, and which you should, so you can add the {{index}} to that row, and filter your data through that index rather which is a little bit better. anyway beware of deleting cells after you have filter your table.
I don't quite get much your question, but you can access to selectedItems of ng-grid as following: $scope.gridOptions.$gridScope.selectedItems (see ng-grid API for more information, but technically this array holds the list of selected items in multiple mode - or only one item in single mode)
For your case, the deleteAll() function could be someething like this:
$scope.deleteAll = function() {
$scope.myData = [];
}
The delete() function which delete selected items can be:
$scope.delete = function() {
$.each($scope.gridOptions.$gridScope.selectedItems, function(index, selectedItem) {
//remove the selected item from 'myData' - it is 2-ways binding to any modification to myData will be reflected in ng-grid table
//you could check by either name or unique id of item
});
}

Categories

Resources