Angular 4 ng bootstrap typehead pass additional parameters - ng-bootstrap

with reference of Angular 2 ng bootstrap typehead pass additional parameter
I have address.component.html
<input name="city" type="text" id="city" formControlName="city" [ngbTypeahead]="getCities">
and address.component.ts as
getCities = (text$: Observable<string>) =>
text$
.debounceTime(300)
.distinctUntilChanged()
.switchMap(query =>
query.length < 2 ? [] : this.apiService.getCities(query).catch(() => {
return Observable.of([]);
});)
My requirement is I want to use getCities for two different text controls based on some parameter like [ngbTypeahead]="getCities('domestic', 'us')". Based on the response provided in thelink referenced above, $index can be passed as [nbgTypeahead]="branchSearchFactory($index: any)". But if I try to pass some variable I'm getting JavaScript compilation error.
So can we pass variables in [ngbTypeahead]?
Thanks

Related

knockout write binding without observable

In knockout, say we have something like this:
var person = {
name: "The H. Dude",
telecom: [
"mailto:dude#host.com",
"tel:+1-987-654-3210"
]
}
and I have a data binding elements like this:
<label>Name: <input type="text" data-bind="value: name"/></label>
<label>Phone:<input type="text" data-bind="value: telecom.find(url => url.startsWith('tel:'))"/></label>
<label>Email:<input type="text" data-bind="value: telecom.find(url => url.startsWith('mailto:'))"/></label>
This works alright. However, this would hit the user over the head with the URL scheme prefix.
So, what we would like to do is something like this:
data-bind="value: telecom.find(url => url.startsWith('tel:')).substring('tel:'.length)"
and
data-bind="value: telecom.find(url => url.startsWith('mailto:')).substing('mailto:'.length)"
respectively.
And that works just fine for a read-only property, which I might just display. But when I type a new phone number in, the expression ends in a function call, which can't be written to, and of course substring function doesn't know how to work backwards to prepend the "tel:" or "mailto:" prefix before the user-entered value gets written to the object.
I have worked deep down in an XForms engine and before that I had made my own UI framework with a path language similar to XPath, used precisely for two-way data-binding. There I had invented the notion of a "conversion" which was a pair of functions, in this case you might say:
telPrefixConversion = {
prefix: "tel:",
forward: function(value) { return value.substring(this.prefix.length); }
backward: function(value) { return prefix + value; }
}
And I'm thinking this would be super-useful in knockout too. Then on the binding I could just say
data-bind="{value: telecom.find(url => url.startsWith('mailto:')), conversion: telPrefixConversion}"
and now knockout-3.5.0.debug.js line 2842 could do this:
if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
// For two-way bindings, provide a write method in case the value
// isn't a writable observable.
var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key;
propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + writableVal + "=_z}");
}
that last line could change to
propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + conversion.backward(writableVal) + "=_z}");
Now I can already think of a way to do that by using a computed observable instead, but it seems heavy weight for something like that. This conversion principle is very powerful as it can also convert complex objects into UI string representations and on the way back it goes into the complex object again.
I am so tempted to implement that in knockout too, since I have done it twice already on other data binding UI tools. Should I go ahead? Or am I missing a simple feature like this?
ATTEMPT 1 - Computed Observable: I have since used computed observables to solve my immediate need, but I found out that this would work when you have more than one telephone number in some kind of repeated groups. For example, let's say you have a list of friends with name, email, and phone number adding computed scratchpad properties just to convert a value to and from string representation is not good.
The fist answer here also suggests computed observable, but both my initial attempt and what is suggested in that answer is too special. We want to have the ability to do it anywhere, regardless if it is a property of one or the other object no matter whether they are also repeated in an array.
So I am thinking of something like this:
class Conversion {
constructor(object, property) {
let self = this;
object[property] = ko.computed({
read: function() {
return self.m2v(ko.util.unwrapObservable(this[property]));
},
write: function(value) {
this[property](m2v(value)); // <<<< infinite recursion????
},
owner: object
});
}
model2view(modelValue) { throw new Error("undefined abstract function called"); }
view2model(viewValue) { throw new Error("undefined abstract function called"); }
}
These model2view and view2model functions can then be overridden to deal with my prefixes, or with date formats as in this other question, etc.
The problem I am stuck on is this:
we replaced the actual value with the observable
when assigning the view value to the observable we would be entering this cycle
We would still need some new property where we store the value for the view separate from the property that holds the actual model value. And that's what I'm trying to avoid by supplying this function pair instead to some other binding option somehow.
ATTEMPT 2 - Extender
I found the observable extenders might almost do the trick:
ko.extenders.prefixConversion = function(target, prefix) {
const read = function() { return target().substring(prefix.length); };
const result = ko.pureComputed({
read: read,
write: function(value) { target(prefix + value); }
});
result(read());
return result;
}
this is used together with the following initializer, and also with these telecom things being objects with url elements, not just the urls as plain strings, example:
o = { ...
telecom: [
{ url: "tel:+1-987-654-3210" },
{ url: "mailto:dude#host.com" }] ... }
then turning this into observables:
telecom
.filter(t => t.url.match('^(tel|mailto):'))
.forEach(t => {
const prefix = t.url.substring(0, value.indexOf(':')+1);
t.url = ko.observable(t.url).extend({prefixConversion: prefix});
});
nice and general works for different prefixes. And here is how we ultimately bind them:
<label>Email:<input data-bind="value: telecom.find(t => t.url.startsWith('mailto:')).url"/></label>
<label>Phone:<input data-bind="value: telecom.find(t => t.url().startsWith('tel:')).url"/></label>
Now this causes the telecom values to be wiped out, because it stores the values back with removed prefixes. It almost works, I can see how the read and write functions produce the correct value, write adds the prefix, and read removes it, but then somewhere, somehow, that value without the prefix gets written into the observable._state._latestValue and then we lose a hold of this altogether.
I don't know if this is a bug somewhere. I don't know even how that prefix-less value ever got written into the observable state.
But in the end, I find even this is too costly an approach anyway. I think the issue applies strictly to binding modes view and text and perhaps textInput, i.e., wherever the ultimate representation is a string. It only applies to the UI surface presentations, it is not about the represented object and its properties. No code should ever get the tel: and mailto: prefix removed, this is only for the user, therefore, this conversion could be bound to the binding handler.
MY OWN SOLUTION
So I am resolving this the same way that I did it with the XForms framework once. I added v2t and t2v functions: v2t means value to text, and t2v means text to value. For example:
<input ...
data-bind="..."
data-t2v="return t.indexOf('tel:') == 0 ? t : 'tel:' + t;"
data-v2t="var p = v.substring(0,4); return p == 'tel:' ? v.substring(4) : v;"
.../>
These attributes get converted to functions during first initialization (or lazily when needed):
if(!(element.t2v === null || element.t2v)) {
const att = element.getAttribute("t2v");
element.t2v = att ? new Function("t", att) : null;
}
if(!(element.v2t === null || element.v2t)) {
const att = element.getAttribute("v2t");
element.v2t = att ? new Function("v", att) : null;
}
then in a few select places, I check and use the element.t2v and .v2t properties respectively before reading and writing respectively. Example:
element.value = element.v2t ? element.v2t(element, value) : value;
This is a solution I have been using for a decade and I think it is right. It is not used when the UI text interactions are more involved. Anything that needs keystroke-event granularity or is in other ways complex, such as requiring inputs from other objects would be handled differently.
I have this implemented and working now. But it is a change to knockout and the question is not moot, because perhaps the knockout people might have a different solution for that.
For example, I note that there is also ko.utils.domData, which I don't understand yet, but which could potentially provide an avenue for this. But my solution is tested in a different data binding framework for a long time and now implemented in knockout, and I think it is the right solution as, again, the test to value and value to text conversions are about the widget and how it renders model data values, not about the data.
You could achieve this without having to modify Knockout, using the built-in writable computed functionality.
Suppose you had a list of people, like so:
vm.person = [
{
name: ko.observable("The H. Dude"),
telecom: [
"mailto:dude#host.com",
"tel:+1-987-654-3210"
]
},
{
name: ko.observable("The I. Gal"),
telecom: [
"mailto:gal#host.com",
"tel:+1-987-654-3211"
]
}
];
Then you could define on your viewmodel a constructor function that returns a writable computed:
vm.getTelecom = function(personIndex, telIndex, prefix) {
return ko.pureComputed({
read() {
return vm.person[personIndex].telecom[telIndex].substring(prefix.length);
},
write(newVal) {
vm.person[personIndex].telecom[telIndex] = prefix + newVal;
}
});
}
And bind that to your inputs:
<!-- ko foreach: person -->
<div>
<label>Name: <input type="text" data-bind="textInput: name"/></label>
<label>Phone:<input type="tel" data-bind="textInput: $parent.getTelecom($index(), 1, 'tel:')"/></label>
<label>Email:<input type="email" data-bind="textInput:$parent.getTelecom($index(), 0, 'mailto:')"/></label>
</div>
<!-- /ko -->
Working demo: https://jsfiddle.net/thebluenile/ujb50vwn/
It feels to me like you're trying to do too much in the view, where knockout would prefer you to add more logic to your viewmodel.
Here's an example of a simple Person View Model that does two things:
Handle the model's (not ideal) format for email and phone explicitly both on the way in and out
Separate the binding values from the model values. The model values have the prefixes, the binding values hide that from the user.
ko.extenders.prefixConversion = function(target, prefix) {
const newTarget = ko.pureComputed({
read: () => target().substring(prefix.length),
write: value => target(prefix + value)
});
return newTarget;
}
const Prefix = {
mail: "mailto:",
phone: "tel:"
};
const PersonVM = person => {
const name = ko.observable(person.name);
const email = ko.observable(
person.telecom.find(str => str.startsWith(Prefix.mail))
);
const phone = ko.observable(
person.telecom.find(str => str.startsWith(Prefix.phone))
);
const toJS = ko.pureComputed(() => ({
name: name(),
telecom: [
email(),
phone()
]
}));
return {
name,
email: email.extend({ prefixConversion: Prefix.mail }),
phone: phone.extend({ prefixConversion: Prefix.phone }),
toJS
};
}
const personVM = PersonVM({
name: "The H. Dude",
telecom: [
"mailto:dude#host.com",
"tel:+1-987-654-3210"
]
});
ko.applyBindings(personVM);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input data-bind="textInput: name" type="text">
<input data-bind="textInput: email" type="text">
<input data-bind="textInput: phone" type="text">
<pre data-bind="text: JSON.stringify(toJS(), null, 2)"></pre>

What is the best way to approach a JSON API output and a v-model within Vue3

I did build a form with Element Plus (https://element-plus.org/#/en-US/component/select). Specific: Basic Multiple Select.
The problem I have is that the Element Plus element () works with v-model. From the API I retrieve the following output in JSON: [1, 2] which is fieldData[field.field].
If I use v-model, it won't work as v-model does not accept JSON I think. The selected options aren't visible. How can I JSON parse in V-model or do I need to fix it otherwise?
<el-form label-position="top">
<el-form-item :label="field.label">
<el-select
v-model="fieldData[field.field]"
multiple
placeholder="Select"
#change="changed(field.field, value2, field.validation)"
style="width: 100%"
clearable
>
<el-option
v-for="item in getOptions(field.options.link, field.options.field)"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
If I change the code to: v-model="value2" and add data to the component (check below) it is working.
data() {
return {
value2: [1, 2]
}
}
JSON is not a type, it's a format, so I"m assuming you mean JSON-encoded object, which is a string
You can (and I believe should) parse the data before passing it to your template
The easiest way to do that is to pass the data through a computed that can then create an array that can be used by the template.
So if the fieldData is an array of JSON-encoded string, you could do this
computed:{
fieldDataformated(){
return this.fieldData.map(str => JSON.parse(str))
}
},
and then use it as v-model="fieldDataformated[field.field]" (assuming field.field` is an index).
If this.fieldData is an object, the computed would look a little different, but the same idea, iterate over the fields and convert the string to an object;
You should also consider resolving getOptions(field.options.link, field.options.field) as a computed as-well (options[field.options.link][field.options.field]🤷‍♂️), since method look-ups in the template will force the view renderer to recalculate the values every time anything changes on the component. Having the value cached by a computed will reduce the load on the browser/js.

Validate prop with function as input in redux form

i have a question about the validate prop.
Suppose we (.....)
In the component we define this:
<Field>
validate = {validate}
</Field>
Why cant we write validate={validate(value)} or why is not correct to write something like this: validate={()=> validate(value)}
Thanks!
The validate prop on Field takes a function (or an array of functions) which should be used to validate the incoming field value. So you can't write validate={ someCustomValidator(value) } unless the someCustomValidator function is a function you have defined that in turn returns a function.
Using validate={()=> someCustomValidator(value)} should work, but doesn't make much sense in the context of your example (where does value come from?). If you use validate={(value)=> someCustomValidator(value)} instead, that makes more sense, but this is problematic because this will create a new function each time the component with the Field re-renders. And according to the documentation:
Note: if the validate prop changes the field will be re-registered.
which is probably not what you want to happen.
So, using
// validation
const someCustomValidator = value => {
// give error roughly half of the time
return Math.random() < 0.5 ?
undefined : // validation is ok
'Validation failed'; // this will be available in meta.error
}
// somewhere else
<Field validate={someCustomValidator} />
is the correct way of using it. But note that Field doesn't internally know what to do to display potential validation errors. That you have to solve yourself. See https://redux-form.com/7.0.4/examples/fieldlevelvalidation/ for an example.

Unwanted data binding in vue js

I have a edit form in my application. When this component is created http request is sent and response data is saved in two different variables.
axios.get(this.initializeURL)
.then((res) => {
this.form = res.data.form
this.formInit = res.data.form
})
and "this.form" is binded with form inputs.
<input type="text" class="form-control" id="name" v-model="form.name">
My problem is : When the input fields are changed "this.formInit" object is also changed. but i haven't bind "this.formInit" with input fields.
Isn't the "this.formInit" supposed to contain the initial data from the response ?
Why is this unwanted binding happening ?
My goal is to compare "this.form" & "this.formInit" to check if the user changes some fields before tring to update the form.
You can create a new object using Object.assign:
axios.get(this.initializeURL)
.then((res) => {
this.form = Object.assign({}, res.data.form),
this.formInit = res.data.form
})

Passing arguments to an embedded controller in Symfony 2.8

I'm using Symfony 2.8.0 (as I find Symfony 3.x not very mature at the moment, but let's not go into that discussion right now).
According to the official documentation
(http://symfony.com/doc/2.8/book/templating.html#embedding-controllers)
it should be possible to pass arguments to an embedded controller invoked from within a view.
However, this doesn't seem to work. I always end up with the following exception:
"Controller "AppBundle\Controller\DefaultController::buildNavigationAction()" requires that you provide a value for the "$argument1" argument (because there is no default value or because there is a non optional argument after this one)."
Within my view I have the following bit of code:
{{ render(controller('AppBundle:Default:buildNavigation'), {
'argument1': 25,
'argument2': 50
}) }}
The controller looks like this:
public function buildNavigationAction($argument1, $argument2)
{
// ... some logic ...
return $this->render(
'navigation.html.twig', array(
'foo' => $argument1,
'bar' => $argument2
)
);
}
What gives? Is this a bug?
The use case described in the documentation (rendering dynamic content from within the base template and therefor on every page) is exactly what I'm using it for. Repeating the same logic in every single controller is an obvious sin against the DRY principle.
Your syntax is incorrect, as you are not passing values to the controller since you are closing the ) too early. It should instead be:
{{ render(controller('AppBundle:Default:buildNavigation', {
'argument1': 25,
'argument2': 50
})) }}

Resources