I'm building an app with Aurelia and really liking the framework so far, but I've stumbled upon an issue where I'm trying to display a list of checkboxes whose values are numbers (ID:s in reality) and Aurelia seems to convert them to strings and thus comparison fails.
I basically have something like:
export class MyVm {
constructor () {
this.items = [
{name: 'Foo', id: 0},
{name: 'Bar', id: 1},
{name: 'Baz', id: 2}
];
this.selectedItems = [0, 2];
}
}
And in my view:
<ul>
<li repeat.for="item of items">
<input type="checkbox" value.bind="item.id" checked.bind="selectedItems">
${item.name}
</li>
</ul>
For this to work I actually have to do this.selectedItems = ["0", "2"] which just leads to a bunch of other comparison problems in my own code. I also don't want to send the selected item as a string to the server later on when saving the data.
I've tried using a simple value converter that converts toString toView and parseInt fromView, but I can't run this converter on the array of selectedItems:
export class IntValueConverter {
toView (val) {
return val.toString();
}
fromView (val) {
return parseInt(val);
}
}
How would you go about solving this?
You almost have it. There's one small problem with this part:
<input type="checkbox" value="${item.id}" checked.bind="selectedItems">
The <input> element's value attribute coerces everything it's assigned to a string. Not only that, a string interpolation binding (eg ${...}) also coerces everything to a string. You need to preserve the numeric item id. Replace value="${item.id}" with model.bind="item.id" and it will work. No need for a converter.
Coincidentally I just pushed a set of exhaustive docs on checkbox, radio and select binding to the Aurelia binding repo. They haven't been published to the official Aurelia docs app yet but they should be there on Tuesday.
Also- if you see anything weird with items whose id is zero- there's a fix going out on Tuesday for that as well.
Finally, I know this is not your question, but for others that land here looking for binding numbers in forms, here's a couple basic examples using a custom element and custom attribute:
https://gist.run/?id=d9d8dd9df7be2dd2f59077bad3bfb399
Related
I've been struggling with this issue so I thought I'd come here to find the fix. I'm trying to filter my objects that are in an array to only show the ones that have for example "h" in them.
This is how my array of objects is being constructed:
Object.keys(data.jello).forEach(function (id, SomeSearchValue) {
var jello = data.jello[id];
array.push({
id: jello.id,
name: jello.name,
type: 'foo',
});
}
For example, let's say my array consists of this for names.
array[blue, red, green, apple, abigail, alien]
So, I have an array of objects. In a search bar I type "a" and my array results are then all objects that have a letter 'a' in the name. so...
array[abigail, alien]
Then i type a "b" so my search is now "ab" and my array is ...
array[abigail]
So what would my code to implement this using jQuery?
The part I'm stuck on most is the actual searching as the user types without using ajax.
I'm using jQuery. Ive tried things like jQuery.grep(), inArray() but cant seem to search includes. I've also tried array.includes(). Thanks for the help!
Use Array#filter().
jQuery $.grep() is basically the same thing but also works on jQuery objects.
Following assumes you only want to search within name property and will accept the term found in any part of the name rather than just at beginning
const data = [{name:'blue'}, {name:'red'}, {name:'green'}, {name:'apple'}, {name:'abigail'}, {name:'alien'}];
const term ='ab';
const filtered = data.filter(({name}) => name.toLowerCase().includes(term.toLowerCase()))
// or use startsWith() to only match beginning of string
console.log(filtered)
I've looked around to see if anyone has answered this and it appears they have not. I want to use an object rather than a string or integer as the value of the radio button. Is this possible? Doesn't seem so because I'm having trouble with the tags md-radio-button recognizing an object rather than string or integer value as having been selected. I can see that it works after the page loads and I select something but I don't know how to check the radio button if the value already exists. You can see a very simple demonstration here: https://codepen.io/anon/pen/ZmjrLN
I've tried class="md-checked" to see if that works, it does not. I've tried ng-checked="selectedStatus.Name == status.Name", it doesn't work either. In fact ng-checked="true" also does not work.
I would think md-radio-button could work with an object!
--- EDIT FOR CLARIFICATION ---
From an answer below, referencing the code from codepen, if I use the same object in $scope.statuses to populate $scope.selectedStatus, it indeed selects the correct radio button on load. HOWEVER, in the real world $scope.selectedStatus is populated with the actual status from the server and $scope.statuses is also populated from that same call. They are the same but 2 different objects.
In a nutshell, I still want to check off the correct one, even though the objects aren't exactly the same, they should be treated like they are, because they are the same.
When comparing objects, ng-checked looks to see if checked is the same object or a reference and does not check the contend of the object. So to set the selected / checked radio button, make the value refer to the object. Here is an example of the Javascript updates for your codepen:
angular
.module('BlankApp')
.controller('AppController', function($scope) {
$scope.statuses = [
{id: 1, Name: 'Active'},
{id: 2, Name: 'Dormant'}
];
$scope.selectedStatus = $scope.statuses[0]; // reference the object directly
});
Or if the value is set outside of the scope and we have to compare, then we can compare manually and then set the selectedStatus by finding the correct object and assigning it.
angular
.module('BlankApp')
.controller('AppController', function($scope) {
$scope.statuses = [
{id: 1, Name: 'Active'},
{id: 2, Name: 'Dormant'}
];
$scope.selectedStatus = {id: 1, Name: 'Active'};
// Find the index of the selected that matches on id
let i = $scope.statuses.findIndex((val) => {
return val.id = $scope.selectedStatus.id;
});
$scope.selectedStatus = $scope.statuses[i]
});
I am trying to use ng-repeat to create list of checkboxes for the keys in a nested object.
My object loosk like:
$scope.kids = [
{
"name": "Will",
"age": 6,
"skills": {
"dancing": false,
"coloring": true
}
},{
"name": "Sally",
"age": 7,
"skills": {
"dancing": false,
"coloring": true,
"runnning": true
}
}
];
and I would like a unique list of the keys in the "skills" object with each skill listed only once, not once for each kid. (i.e. "dancing", "coloring", "running")
This was helpful, but I still can't get a unique list after trying the nested repeats
Here's my current attempt on JSFiddle
Thanks!
Modify Your HTML
I think you need to change your html, remove your ng-repeats with this
<span ng-repeat="kid in kids">
<span ng-repeat="(key,skill) in kid.skills">
<input type="checkbox" ng-modle="{{skill}}"> {{key}}
<br>
</span>
</span>
This should give you a list of boxes connected to the correct $scope and titles.
Here is a link to the modified jsfiddle
Edit
I've modified the code to display a list of all the kids and then within that list we have a list of all of the kids individual skills. Its basically about understanding how loops work.
If I got your question correctly, the best solution for you will be to build a filter that extracts the keys, sorts them and pull unique values. Then you can use it in a single repeat. I forked your jsfiddle and added the filter.
I chose to use lodash to quickly pull out the values, flatten, sort and remove dups. Lodash is a huge playground and you could make the filter even more shorthand.
Since you have a repeating access to this filter, I'd suggest you use memoize to avoid redundant evaluation of the filter. Once it is computed, it is good enough. The basic memoization uses a single key and will not be able to account for other parameters but you can override it and take all params into account and have an accurate and efficient memoization.
The filter is defined like so:
mymodule.filter('uniqueKeys', function(){
return function(input, keyPath){
return _.unique(_.sortBy(_.flatten( _.map(input,
function(item){return _.keys(_.get(item, keyPath));}))));
}
});
and the repeater would be:
<div ng-repeat="skill in kids | uniqueKeys:'skills'">
Note that I pass in the key to find your values, if you had hobbies field that'd you'd like to extract, you just have to write:
<div ng-repeat="skill in kids | uniqueKeys:'hobbies'">
I'm curious as to whether Mustache has a syntax feature that is the opposite of the "Inverted Selection" syntax {{^foo}}.
Let's say we have the data:
{
people: [
{ name: "Alice" },
{ name: "Bob" },
{ name: "Mallory" }
]
}
And the template:
<h3>People</h3>
<ul>
{{#people}}
<li>{{name}</li>
{{/people}}
</ul>
If we wanted to add an item if there are no people, we just add something like:
{{^people}}<li>No people</li>{{/people}}
But in my case I don't want to show the <h3> and <ul> if there are no people.
A syntax like {{?people}} (representing the opposite / boolean negation of {{^people}}) would be perfect for this situation, but does one exist?
Another alternative would be to preprocess the data, adding a value
has_people = Boolean(people.length)
and using {{#has_people}}, but this seems counter-intuitive and against the logic-less (and I use that term lightly) paradigm.
Simplest solution seemed to be to add the functionality myself.
I've forked the Mustache.js repository with the changes required to introduce the {{? data }} selector. In English it could be read as 'When true, or there are items within data.'
Repository | Diff 1 | Diff 2
Given a data structure that contains an array of JavaScript objects, how can I bind a certain entry from that array to an input field using Angular?
The data structure looks like this:
$scope.data = {
name: 'Foo Bar',
fields: [
{field: "F1", value: "1F"},
{field: "F2", value: "2F"},
{field: "F3", value: "3F"}
]
};
The fields array contains several instances of the given structure, with each entry having both a field attribute and a value attribute.
How can I bind an input control to the value field attribute of the array entry with the field F1?
<input ng-model="???"/>
I know that I could bind all fields using an ng-repeat, but that's not what I want. The above data is just an example from a much larger list of fields, where I only want to bind a pre-defined subset of fields to controls on the screen. The subset is not based on the attributes in the array entries, but is known at design time of the page.
So for the above example, I would try to bind F1 to one input on the page, and F2 to another one. F3 would not be bound to a control.
I've seen examples where a function was used in the ng-model, but it doesn't seem to work with Angular 1.1.0.
Is there another clever way to bind the input field to a specific array entry?
Here's a fiddle that has an example, but does not work since it's trying to use function in the ng-model attribute: http://jsfiddle.net/nwinkler/cbnAU/4/
Update
Based on the recommendation below, this is what it should look like: http://jsfiddle.net/nwinkler/cbnAU/7/
I personally would reorganize the array in a way that field property of an entry of the array become the identifier of the object. Mhhh that sentence may sound strange. What I mean is the following:
$scope.data = {
name: 'F1',
fields: {
F1: {
value: "1F"
},
F2: {
value: "2F"
}
}
};
If you want to bind a the value dynamically and it's an easy and quick way to achieve it.
Here is your fiddle modified so that it words. http://jsfiddle.net/RZFm6/
I hope that helps
You can use an array of objects, just not an array of strings.
HTML:
<div ng-repeat="field in data.fields">
<input ng-model="field.val"/>
</div>
JS:
$scope.data = {
name: 'F1',
fields: [
{ val: "v1" },
{ val: "v2" }
]
};
I've updated #Flek's fiddle here: http://jsfiddle.net/RZFm6/6/
Edit: Sorry just read your question properly, you can still use an array with:
<label>Bound to F1:</label>
<input ng-model="data.fields[0].value"/>
though maybe stop and think. Is there going to be variable number of fields ? or are you making a predetermined number of fields ? Use an array in the former and an object for the latter.
One way to do it is to simply add the necessary references to the scope, like this:
$scope.fieldF1 = fieldValue('F1');
$scope.fieldF2 = fieldValue('F2');
And then use those references:
<input ng-model="fieldF1.value"/>
<input ng-model="fieldF2.value"/>
Fiddle: http://jsfiddle.net/cbnAU/5/
Note: I'm assuming that $scope.data is static, but if it happens to be dynamic you can always watch for changes on it and recalculate the references...