Comments (10)
Great question- here's a few options:
brute force
import {HttpClient} from 'aurelia-http-client';
import {employeeComparer} from './employees';
export class Job {
constructor() {
this.http = new HttpClient();
}
activate(params) {
return Promise.all([
this.http.get('/api/jobs/' + params.id).then(http => {
this.job = JSON.parse(http.response);
}),
this.http.get('/api/employees').then(http => {
this.employees = JSON.parse(http.response);
})
]).then(() => {
var assignee = this.job.assignee;
if (assignee) {
this.job.assignee = this.employees.filter(employee => employeeComparer(employee, assignee))[0];
}
});
}
}
converter
export class EmployeeToIDConverter {
toView(employee, employees) {
return employee ? employee.id : null;
}
fromView(id, employees) {
return id ? employees.filter(e => e.id === id)[0] : null;
}
}
<select value.bind="job.assignee | employeeToID:employees">
<option repeat.for="employee of employees" model.bind="employee | employeeToID:$parent.employees">${employee.name}</option>
</select>
enhance Aurelia's select binding logic
Maybe we should add support for an equality-comparer
attribute that would be used like this:
<select value.bind="job.assignee" equality-comparer.bind="employeeComparer">
<option repeat.for="employee of employees" model.bind="employee">${employee.name}</option>
</select>
What do you think?
from binding.
Thank you @jdanyow for your suggestions! After further investigation, I'm tempted to use the JSON.parse()
optional argument reviver
to return the same instance whenever the same object is parsed. That would require a map of retrieved objects (trivial) and a well-written reviver
function to handle seamlessly [nested-]arrays and nested-objects (definitely non-trivial).
from binding.
I came up to this reviver
:
var map = new Map();
var reviver = function(k, v) {
if (v === null || Array.isArray(v)) return v;
if (typeof v !== 'object') return v;
var key = JSON.stringify(v); // this is a simplified strategy to guess object identity
if (map.has(key)) {
return map.get(key);
}
map.set(key, v);
return v;
};
applied to the former use case:
import {HttpClient} from 'aurelia-http-client';
export class Job {
constructor() {
this.http = new HttpClient();
this.map = new Map();
var _map = this.map;
this.reviver = function(k, v) {
if (v === null || Array.isArray(v)) return v;
if (typeof v !== 'object') return v;
var key = JSON.stringify(v);
if (_map.has(key)) {
return _map.get(key);
};
_map.set(key, v);
return v;
};
}
activate(params) {
return Promise.all([
this.http.get('/api/jobs/' + params.id).then(http => {
this.job = JSON.parse(http.response, this.reviver);
}),
this.http.get('/api/employees').then(http => {
this.employees = JSON.parse(http.response, this.reviver);
})
]);
}
}
and the binding works like a charm (see this gist for a test in NodeJS).
Obviously here I'm guessing object identity based on its string representation (which may vary between the two API requests), but one can use other strategies, e.g. key properties; also, JSON schema may help identifying the type of each property.
from binding.
Cool stuff! One question about the reviver- why not use k
as the key instead of stringifying v
to produce the key? Apologies if this is a silly question, I don't know anything about revivers.
Edit
stopped being lazy and learned why 💤 Ignore this post...
from binding.
@jdanyow I'm for adding the third option you listed but with an easier name like matcher :) Even a simple object is having problems matching up.
from binding.
re-opening- @PWKad i like matcher
from binding.
I'm using this as a Reviver now, with slight modifications from the previous one:
- I pass a
pk
argument which is the /primary key/ (it must be a UUID, unique among collection of different objects); - I use
Object.assign()
to update the current instance of the object.
export class Reviver {
constructor() {
this.map = new Map();
}
revive(json, pk) {
var self = this;
return JSON.parse(json, function(k, v) {
if (v === null || Array.isArray(v)) return v;
if (typeof v !== 'object') return v;
var key = v[pk];
if (!self.map.has(key)) {
self.map.set(key, v);
return v;
};
return Object.assign(self.map.get(key), v);
});
}
}
I'm still looking for a stronger strategy to discriminate object identity…
P.S. I see Aurelia's http-client
uses a reviver
in JSON.parse()
. Is it configurable?
from binding.
@heruan The reviver in the http-client is definitely configurable, you can set a shared reviver using .reviver(revfunc)
during configuration or per request using .withReviver()
from binding.
I really like the matcher
idea. This scenario happens on nearly every page I write for our line-of-business app.
from binding.
http://stackoverflow.com/questions/33920610/binding-a-select-to-an-array-of-objects-in-aurelia
from binding.
Related Issues (20)
- path expressions containing 'in' no longer working, 'in' treated as keyword HOT 3
- Issue with aurelia/binding typings HOT 12
- Issue with interpolation binding inside HTML `<option>` element HOT 2
- Checkbox event arguments in repeaters not binding correctly HOT 5
- @behel33 Thanks for creating the issue, I couldn't recreate the behavior you described, can you help fix this codesandbox https://codesandbox.io/s/x761qq5p3q HOT 1
- Infinite loop with 100+ observers HOT 17
- click.delegate bindings stop working HOT 14
- click.delegate firing for all clicks on firefox rather than just left click HOT 5
- Binding on role attribute not working in Firefox HOT 4
- Delegate event bubbling does not match native behavior in shadow dom
- Infinite loop in aurelia-bindingd.ts OverrideContext HOT 13
- parsing of template issue: ${moment().toISOString()} does not work HOT 1
- Parser Error: Invalid start of expression at column 0 in expression []
- Optimizing getter access HOT 1
- Matcher ignored within repeater HOT 7
- Delegate does not support shadow dom for Firefox
- getContextFor() doesn't handle a non-object bindingContext HOT 3
- Can we use/make attached/detached method inside aurelia custom element? HOT 3
- Binding checkboxes: custom matcher does not work when using repeat.for HOT 3
- SetterObserver keeps old value retained HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from binding.