I am new to angular so forgive me if I use the incorrect terminology! I would also prefer any solutions using the latest Angular version if possible :-) I have some fairly complex use cases.
One of these is a customer edit screen. I have already built the list page and customer details forms, this works well. This also posts back some JSON. I have removed this from my example.
Something that a user must set is a customers stages which can be multiple. Therefore i will use checkboxes.
What I do is load the current user into the scope, then modify its values. then save to a web service. However i have some complex properties and figuring out how to bind these is problematic.
i found this example here which i can get to work if i put the options on my controller directly (shown in code)
http://plnkr.co/edit/cqsADe8lKegsBMgWMyB8?p=preview
however I cannot bind the check boxes on the currentUser.pipe properties. Any help would be greatly appreciated!
kind regards
jim
//our object definitions are here
function User(Firstname, Lastname, Id) {
this.Firstname = Firstname;
this.Lastname = Lastname;
this.PersonId = Id;
this.uuid = "OG6FSDHG6DF86G89DSHGDF8G6";
//hold the customers source
this.source = 2;
//these are used to populate our selection boxes
this.pipe = new Object();
//PROBLEM CODE IS HERE
//I WOULD LIKE TO OUTPUT A CHECKBOX FOR EACH ITEM AND THEN UPDATE THE SELECTED VALUE WHEN A USER CLICK IT
this.pipe.stages = [
{ id: 1, text: 'STAGE 1', selected: true },
{ id: 2, text: 'STAGE 2', selected: false },
{ id: 3, text: 'STAGE 3', selected: true },
{ id: 4, text: 'STAGE 4', selected: false }
];
this.getFullName = function () {
return this.Firstname + " " + this.Lastname + " " + Id;
};
}
function UserController($scope) {
//called to populate the customers list
$scope.populateCustomers = function () {
//this will be populated form the server. I have extra code which allows th euser ot select the customer and edit it and this works fine.
//I have removed the superflous code
$scope.userList = [
new User("John", "Doe", 1),
new User("Henri", "de Bourbon", 2),
new User("Marguerite", "de Valois", 3),
new User("Gabrielle", "d'Estrées", 4)
];
};
$scope.populateCustomers();
// the currentUser pobject is loaded by the user and modified. This works fine
$scope.currentUser = $scope.userList[0];
//if i add the stages here i can get them to update however these are different for each
//customer and would like the state to be held accordingly
$scope.stages = [
{ id: 1, text: 'STAGE 1', selected: true },
{ id: 2, text: 'STAGE 2', selected: true },
{ id: 3, text: 'STAGE 3', selected: true },
{ id: 4, text: 'STAGE 4', selected: true }
];
}
Here are the templates i have used. This one works off scope.stages
stages from scope.stages<br />
<label ng-repeat="stage in stages">
<input type="checkbox" value="{{stage.name}}" ng-model="stage.selected">{{stage.name}}
</label>
<p>stages: {{stages}}</p>
And this is what i would like to do however it shows the check boxes but doesnt bind correctly.
stages from currentUser.pipe.stages<br />
<label ng-repeat="stage in currentUser.pipe.stages">
<input type="checkbox" value="{{stage.name}}" ng-model="stage.selected">{{stage.name}}
</label>
<p>stages: {{stages}}</p>
Everything works perfectly. I think you have print the wrong variable in the currentUser.pipe.stages template. It will be <p>stages: {{currentUser.pipe.stages}}</p>.
stages from currentUser.pipe.stages<br />
<label ng-repeat="stage in currentUser.pipe.stages">
<input type="checkbox" value="{{stage.name}}" ng-model="stage.selected">{{stage.name}}
</label>
<!-- Print currentUser.pipe.stages -->
<p>stages: {{currentUser.pipe.stages}}</p>
See this PLUNKER. Its binding properly.
Related
I'm using AngularJS on a project and I need to implement a select box with a filter to a nested property. My object (I have an array of those and I'm iterating through them via ng-repeat) has a structure similar to this:
{
id: 1,
name: 'Example',
groups: [
{ id: 1, name: 'Group 1' },
{ id: 2, name: 'Group 2' }
]
}
I need to filter the group ID of the elements, and after searching I've come up with two ways to do it:
1.
| filter: { $: { id: search.item_id } }
Which has these problems:
Apparently it searches for any nested properties named ID, so if I have more than one object inside my main object, with a property called ID too, it would add it to the filter. Example:
{
id: 2,
name: 'Example 2',
groups: [
{ id: 1, name: 'Group 1' },
{ id: 2, name: 'Group 2' }
],
categories: [
{ id: 1, name: 'Cat 1' },
{ id: 2, name: 'Cat 2' }
]
}
Filtering for ID 1 would select not only group with ID 1, but category with ID 1 too.
Also, with this method, even before setting the filter (search.item_id model is null), objects without groups are being filtered and not appearing in the list. These objects are like this:
{
id: 3,
name: 'Example 3',
groups: []
}
and the other way is:
2.
| filter: { groups: [{ id: search.item_id }] }
In this case, the problem is that it simply doesn't work, it filters everything, leaving the list blank, no matter if it's set or which option is selected.
How can I make it work? I've been searching and couldn't find anything about this. Filtering nested properties is (or should be) a very basic thing.
Update:
So, xtx first solution kinda did it, but only if I use input text or number, but I need to use a select box (more specifically uib-dropdown, but working in a regular select is the next step). My select box is looking like this:
<select name="filter_classes_groups_test" ng-model="search.group_id">
<option val="group.id" ng-repeat="group in classesGroups">{{ group.name }}</option>
</select>
When I interact with it, nothing happens.
If creating a custom filter works for you, here is how you can do that:
app.filter('groupsFilter', function() {
return function(input, groupId) {
var out = [];
if (!groupId || isNaN(parseInt(groupId))) {
return input;
}
angular.forEach(input, function(item) {
if (item.groups && angular.isArray(item.groups)) {
angular.forEach(item.groups, function (group) {
if (group.id === parseInt(groupId)) {
out.push(item);
}
});
}
});
return out;
}
});
As you can see, the custom filter has name groupsFilter, and takes group id to search for as a parameter. The filter can be applied like this:
<div ng-repeat="item in data | groupsFilter:search.item_id">
...
</div>
UPDATE:
Instead of creating a custom filter, you can just create a function that implements filtering logic, in scope like this:
$scope.groupsFilterLocal = function(value) {
if (!$scope.search.item_id || isNaN(parseInt($scope.search.item_id))) {
return true;
}
if (!value || !value.groups || !angular.isArray(value.groups)) {
return false;
}
for (var i = 0; i < value.groups.length; i++) {
if (value.groups[i].id === parseInt($scope.search.item_id)) {
return true;
}
}
return false;
};
and then apply it using build-in filter like this:
<div ng-repeat="item in data | filter:groupsFilterLocal ">
...
</div>
Notice that in this case you can't pass the value to search for (search.item_id) into your function groupsFilterLocal like it is done for the custom filter groupsFilter above. So groupsFilterLocal has to access search.item_id directly
UPDATE 2: How to create a select properly
The reason why the filter is not applied when you pick a group in your dropdown, is in the way how you defined the select. Instead of the id of the selected group, search.group_id gets assigned group's name.
Try defining the select like shown below:
<select name="filter_classes_groups_test" ng-model="search.item_id" ng-options="group.id as group.name for group in classesGroups">
<option value="">-- Choose Group --</option>
</select>
To ensure that search.item_id gets id of the group that is selected (and not its name), try temporarily adding {{ search.item_id }} somewhere in your template.
I am trying to get the of checkbox selected and store its result in an array . when the checkboxes are selected by default its not getting its value but after toggling if if a particular checkbox is selected its
working correctly.Please tell me what i am doing wrong and thanks in advance.
here my html code:
<div ng-repeat="album in albums" ng-disabled="checked">
<input type="checkbox" ng-model="album.selected" value={{album.value}} ng-checked = "true"/> {{album.name}}
</div>
<button ng-click = "setAlbums()" type = "submit" class = "col-sm-3 btn btn-primary" style = "margin-left:3%;"> Save </button>
here my js code:
$scope.albums = [{
value: 3,
name: 'a'
},
{
value: 4,
name: 'b'
},
{
value: 5,
name: 'c'
},
{
value: 6,
name: 'd'
},
{
value: 7,
name: 'd'
},
{
value: 8,
name: 'e'
},
{
value: 9,
name: 'f'
}];
$scope.setAlbums = function () {
$scope.albumNameArray = [];
angular.forEach($scope.albums, function(album){
if (album.selected) $scope.albumNameArray.push(album.value);
});
console.log("$scope.albumNameArray",$scope.albumNameArray)
}
Initially while ng-repeat is rendering the array it will look for the album.selected value in the array itself, so if its not able to find it, it will be virtually checked due to your ng-checked = true attribute but it will not be set in the ng-model, so
try it like this, use a another property called ng-init = "album.selected = true" in,
<input type="checkbox" ng-model="album.selected" value="{{album.value}}" ng-checked="true" ng-init="album.selected = true"/>
then try with the save button click.
PLUNKER WITH YOUR CODE
I'm searching for a solution to edit in-line table rows. Pretty much like this fiddle, but with populated comboboxes too.
function Ctrl($scope) {
$scope.model = {
contacts: [{
id: 1,
name: "Ben",
age: 28
}, {
id: 2,
name: "Sally",
age: 24
}, {
id: 3,
name: "John",
age: 32
}, {
id: 4,
name: "Jane",
age: 40
}],
selected: {}
};
// gets the template to ng-include for a table row / item
$scope.getTemplate = function (contact) {
if (contact.id === $scope.model.selected.id) return 'edit';
else return 'display';
};
$scope.editContact = function (contact) {
$scope.model.selected = angular.copy(contact);
};
$scope.saveContact = function (idx) {
console.log("Saving contact");
$scope.model.contacts[idx] = angular.copy($scope.model.selected);
$scope.reset();
};
$scope.reset = function () {
$scope.model.selected = {};
};
});
How can I make inline editable comboboxes ? Each line should have a name, age and a group of options.
It would be easier, and I would venture to say nicer, if you use ng-grid instead of html tables. The grid has built-in editing, and you can customize the kind of editor, such that your cell could display as plain text, but use a combo box for editing.
Here is a plunker that shows an editable combo box on the Gender column:
http://plnkr.co/edit/zsxqZNQCnpFySjSWcf1D?p=preview
{field:'gender', displayName: 'Gender', enableCellEdit: true,editableCellTemplate: '<span id="gender"><select ng-class="\'colt\' + col.index" + ng-input="COL_FIELD" ng-model="COL_FIELD" ng-options="c.value as c.name for c in genders"></select></span>'}]
And here is the documentation for ng-grid:
http://angular-ui.github.io/ng-grid/
$scope.opts =
{
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"}
]
}
The above is my options list array and now I set my default option.
$scope.user.unit = $scope.opts.unit[0];
The above creates the following in my html
<select class="unit ng-pristine ng-valid" data-ng-options="a.name for a in opts.unit" data-ng-model="user.unit">
<option value="0" selected="selected">px</option>
<option value="1">%</option>
</select>
When I use the below I am pulling the data that was stored in a db from the options selected in the above example.
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
console.log($scope.user.unit);
});
This console.logs me the following Object { id=1, val="px", name="px"}
I may be wrong but the <select> box is binded to $scope.opts
How would I be able to link the retrieved data from $scope.user.unit to $scope.opts.unit so that when the data is retrieved it will then mark the correct option as :selected?
I'm not 100% sure but you can try this (or create JSFiddle):
JS:
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
$scope.selected = {};
angular.forEach($scope.opts.unit, function (value)
{
if (value.val == $scope.user.unit.val) {
$scope.selected = value
}
});
console.log($scope.user.unit);
});
and in View:
<select class="unit ng-pristine ng-valid" data-ng-options="a.name for a in opts.unit" data-ng-model="user.unit">
<option value="{{selected.val}}">{{selected.name}}</option>
</select>
Your ng-model for the select element is an object, and not a primitive type, which is fine, but then you reassign $scope.user to a brand new object (returned from $http.get), so user.unit is a new object too, so it's not identical to any of your ng-options. I can think of two ways which should fix the problem:
bind the select to the 'id' property of the unit object:
<select ng-options="a.id as a.name for a in opts.unit" ng-model="user.unit.id">
or leave the select bound to user.unit, but use the track by feature of ng-options:
<select ng-options="a.name for a in opts.unit track by a.id" ng-model="user.unit">
One of the things in Angular is that you rarely need to do is explicitly create <option> elements manually as the framework will generate this for you. Therefore, the following will work: (Working jsfiddle at http://jsfiddle.net/LMHLq/12/)
HTML:
<select data-ng-model='user.unit' data-ng-options="o.id as o.name for o in opts.unit"/>
JavaScript:
$scope.opts ={
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"},
{ id: 3, val: "pt", name: "pt"}
]
}
$http.get('/assets/inc/file.php?id='+thisPage).success(function(response) {
var userData = response.userData;
var locationData = response.locationData;
$scope.user = userData;
$scope.locations = locationData;
console.log($scope.user.unit);
});
$scope.opts ={
unit: [
{ id: 1, val: "px", name: "px"},
{ id: 2, val: "%", name: "%"},
{ id: 3, val: "pt", name: "pt"}
]
}
I noticed that the $scope.opts builds my select element and populates it but when the data is retrieved via db it needs to go into $scope.user.unit but this is binded to $scope.opts so what I have done is sought out the ID for the item that was retrieved and then added -1 to it so it will select from the array of $scope.opts.unit
var testUnit = $scope.user.unit.id-1; //gets the ID of the unit thats been retrieved
$scope.user.unit = $scope.opts.unit[testUnit]; //sets the selected option in the dom
I use select2 from angular-ui, everything works ok, but it passes to ng-model object and I need to get plain value
this is in controller
$scope.units = {
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term) === 0; }).length===0) {
return {id:term, text:term};
}
},
multiple: false,
data: [{id: 0, text: 'kg'},{id: 1, text: 'ks'},{id: 2, text: 'm'}, {id: 3, text: 'h'}]
};
and this in view
<input type="hidden" class="form-control" id="unit_name" ui-select2="units" ng-model="newItem.unit">
but result is {id: 0, text: 'kg'} and I would need only text from that object. I know that is is possible to get it with .val(), but I not able to use with angular... So how to format output? Is it possible?
Thanks
I also have this problem, and i got solution by remove some line of code from the select2-ui.js.
Commnet following code in select2-ui.js
elm.bind("change", function(e) {
e.stopImmediatePropagation();
if (scope.$$phase || scope.$root.$$phase) {
return;
}
scope.$apply(function() {
controller.$setViewValue(
convertToAngularModel(elm.select2('data')));
});
});