I have a function that I use to handle pagination in my Angular app. It was working as expected - I subscribe to the url params and then use the router to navigate according to those params while taking in the page number as a passed in value. One param is a boolean indicating whether the filter is currently active, and the second param is the value(s) itself for the filter.
This is the working version:
public paginate(page) {
this.route.params.subscribe(
(params: any) => {
this.pn_location_e = params['pn_location_e'];
this.pn_location_v = params['pn_location_v'];
}
);
this.router.navigate(
['/clients', {
page: page,
pn_location_e: this.pn_location_e,
pn_location_v: this.pn_location_v,
}]);
let fn = resRecordsData => {
this.records = resRecordsData;
let data = resRecordsData.data;
};
this.dataService.filterByInput(
page - 1, this.pagesize, this.location, fn);
}
Everything above was working as expected.
However, recently a colleague changed the filter syntax from using an "_" to using a ".". So it went from this:
this.pn_location_e = params['pn_location_e'];
to this:
this.pn_location.e = params['pn_location.e'];
The problem is, in my Angular component I can't initialize the variable with that syntax. When I try and initialize like this:
pn_location.e
... I get a syntax error. I also tried doing this pn_location['.e'], but that also won't work (also causes a syntax error).
Is there a way around this? Or do we just need to go back to using the underscore syntax for our filters params?
Surrounding the property names with quotes will allow the assignments:
public paginate(page) {
this.route.params.subscribe(
(params: any) => {
this.pn_location_e = params['pn_location.e'];
this.pn_location_v = params['pn_location.v'];
}
);
this.router.navigate(
['/clients', {
page: page,
'pn_location.e': this.pn_location_e,
'pn_location.v': this.pn_location_v,
}]);
}
Related
I'm using react-hook-form package, which is neat.
One of the things you can do is watch changes on input fields by calling useForm() and using one of the events returned named watch like so:
const methods = useForm()
console.log('test watch', methods.watch("myrandominpuitname"))
which works great, however, if I would like to have autocomplete available on that watch method I have to pass some arguments to the useForm hook like so:
const methods = useForm<{
'myrandominpuitname': string,
}>()
console.log('test watch', methods.watch("myrandominpuitname"))
That will provide me with autocomplete when trying to call the methods.watch("...")
My question is, is it possible to generate an object from a nested dataset and use that as the type for the useForm?
Sample Looped Dataset:
function useFormInputs(arrDataset: any[]) {
let oInputs = {};
arrDataset.map(dataset => {
oInputs = Object.assign(oInputs, {[dataset.oField.oProps.name]: ''});
});
return useForm<typeof oInputs>();
}
Sample Object Result:
let test = {
'myrandominpuitname1': '',
'myrandominpuitname2': '',
'myrandominpuitname3': '',
}
Is it possible to make that object into a type looking like this:
type myInputs = {
'myrandominpuitname1': string,
'myrandominpuitname2': string,
'myrandominpuitname3': string,
};
I would like to generate it automatically,I have tried stuff like
type TestType<T> = {
[index in T as string]: string;
};
The above sample with not recognize the unique names and will not provide me with autocomplete.
Does anyone know how to do this? My feeling is that it is prob very easy for someone who is experienced with Typescript. :)
i am using object literal as an alternative to if else/switch statements. In doing so not knowing how to assign same value to different keys.
What i am trying to do?
Based on variable named "user" should redirect to different links.
if value of "user" is admin or manager should redirect to say "www.exampledomain.com". If value of "user" is "purchaser" should redirect to "https://stackoverflow.com".
To do so i have used object literal instead of if-else which is clumsy.
Below is the code,
get user() {
return ( {
'admin': 'www.exampledomain.com',
'manager': 'www.exampledomain.com',
'purchaser': 'https://stackoverflow.com',
} )[user];}
As you see from above code, admin and manager keys point to same url "www.exampledomain.com". Is there a way to assign it something like below.
get user() {
return ( {
'admin': 'manager': www.exampledomain.com',
'purchaser': 'https://stackoverflow.com',
} )[user];}
Could somebody help me solving this. Thanks.
Personally, I don't see any reason to use the second idea, furthermoe it is not valid JS code.
If you are trying to reduce code duplication you can just extract your urls into a constant variable like that.
static get REDIRECT_URLS() {
return {
"PRIMARY_SITE" : "www.exampledomain.com",
"SECONDARY_SITE" : "stackoverflow.com",
};
}
get user() {
return ( {
'manager' : FooClass.REDIRECT_URLS.PRIMARY_SITE,
'admin': FooClass.REDIRECT_URLS.PRIMARY_SITE,
'purchaser': FooClass.REDIRECT_URLS.SECONDARY_SITE,
} )[user];}
Of course there are other possible solutions, like having keys like 'admin|manager' : "url", but that doesn't seem to be a good choice and you need to add extra logic to iterate over the object keys and check if a key matched the regex.
If the problem were to be viewed in isolation, I would solve this issue by simply flipping the data structure around
const linkToTypeMapping = {
'www.exampledomain.com': ['admin', 'manager'],
'https://stackoverflow.com': ['purchaser'],
}
But that doesn't really fix your issue.
The way I would solve the actual use-case is to simply add a link property to your user object and just populate and later access userObject.link.
However for completeness's sake, here's how you would extract a user's link from the data structure I posted above.
const get_link = (type) => {
for (let key in linkToTypeMapping) {
if(linkToTypeMapping.includes(type)) {
return key;
}
}
}
This is obviously very complicated as far as the code goes, but if your linkToTypeMapping object is expected to become fairly large, this might actually be the right solution for you.
You can make default route like that:
function user() {
const defaultRoute = "www.exampledomain.com"
return ({
'admin': defaultRoute,
'manager': defaultRoute,
'purchaser': 'https://stackoverflow.com'
})[user];
}
or that
function user2() {
const defaultRoute = "www.exampledomain.com"
return ({
'someoneElse': "www.google.com",
'purchaser': 'https://stackoverflow.com'
})[user] || defaultRoute;
}
or mix both styles, depending on how complex your statement is;
or even make it from the other side
function user3(user) {
const routes = {
"www.google.com": ["admin", "manager"],
"www.exampledomain.com": ["purchaser"]
};
return Object.keys(routes).find(k => routes[k].includes(user));
}
console.log(user3('manager')); //www.google.com
I am a relative beginner in Angular, and I am struggling to understand some source I am reading from the ng-bootstrap project. The source code can be found here.
I am very confused by the code in ngOnInit:
ngOnInit(): void {
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
const results$ = letProto.call(inputValues$, this.ngbTypeahead);
const processedResults$ = _do.call(results$, () => {
if (!this.editable) {
this._onChange(undefined);
}
});
const userInput$ = switchMap.call(this._resubscribeTypeahead, () => processedResults$);
this._subscription = this._subscribeToUserInput(userInput$);
}
What is the point of calling .call(...) on these Observable functions? What kind of behaviour is this trying to achieve? Is this a normal pattern?
I've done a lot of reading/watching about Observables (no pun intended) as part of my Angular education but I have never come across anything like this. Any explanation would be appreciated
My personal opinion is that they were using this for RxJS prior 5.5 which introduced lettable operators. The same style is used internally by Angular. For example: https://github.com/angular/angular/blob/master/packages/router/src/router_preloader.ts#L91.
The reason for this is that by default they would have to patch the Observable class with rxjs/add/operators/XXX. The disadvantage of this is that some 3rd party library is modifying a global object that might unexpectedly cause problems somewhere else in your app. See https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#why.
You can see at the beginning of the file that they import each operator separately https://github.com/ng-bootstrap/ng-bootstrap/blob/master/src/typeahead/typeahead.ts#L22-L25.
So by using .call() they can use any operator and still avoid patching the Observable class.
To understand it, first you can have a look at the predefined JavaScript function method "call":
var person = {
firstName:"John",
lastName: "Doe",
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var myObject = {
firstName:"Mary",
lastName: "Doe",
}
person.fullName.call(myObject); // Will return "Mary Doe"
The reason of calling "call" is to invoke a function in object "person" and pass the context to it "myObject".
Similarly, the reason of this calling "call" below:
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is providing the context "this._valueChanges", but also provide the function to be called base on that context, that is the second parameter, the anonymous function
value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
}
In the example that you're using:
this._valueChanges is the Input Event Observerable
The _do.call is for doing some side affects whenever the event input happens, then it returns a mirrored Observable of the source Observable (the event observable)
UPDATED
Example code: https://plnkr.co/edit/dJNRNI?p=preview
About the do calling:
You can call it on an Observable like this:
const source = Rx.Observable.of(1,2,3,4,5);
const example = source
.do(val => console.log(`BEFORE MAP: ${val}`))
.map(val => val + 10)
.do(val => console.log(`AFTER MAP: ${val}`));
const subscribe = example.subscribe(val => console.log(val));
In this case you don't have to pass the first parameter as the context "Observable".
But when you call it from its own place like you said, you need to pass the first parameter as the "Observable" that you want to call on. That's the different.
as #Fan Cheung mentioned, if you don't want to call it from its own place, you can do it like:
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
I suppose
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is equivalent to
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
In my opinion it's not an usual pattern(I think it is the same pattern but written in different fashion) for working with observable. _do() in the code is being used as standalone function take a callback as argument and required to be binded to the scope of the source Observable
https://github.com/ReactiveX/rxjs/blob/master/src/operator/do.ts
I am learning Angular2, following the "Tour of Heroes" tutorial on Angular.io. Near the end of the tutorial, we set up routing to a detail page and pass a parameter indicating the hero to displace. This is handled using the params Observable in ActivatedRoute. We use switchMap to redirect from the params Observable to a Promise to return the data we actually want based on the parameter.
The syntax used in the tutorial is concise, so I tried to break it out into building blocks to get a better understanding of what is going on. Specifically, I have tried to replace right arrow notation with an actual function, that I think is identical. But my modification does not work.
Here is the code:
ngOnInit(): void {
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
//.switchMap(this.getHero)
.subscribe(hero => this.hero = hero);
}
getHero(params: Params) : Promise<Hero> {
return this.heroService.getHero(+params['id']);
}
What confuses me is why using the line that is currently commented out instead of the line above it, I get an error: "Cannot read property 'getHero' of undefined." The two versions of code look identical to me.
Fat-arrow function preserves the context of execution, allowing the this "variable" to be the same as in the parent scope. If you use .switchMap(this.getHero) then this will point to something else, not the component.
getHero(params: Params) : Promise<Hero> {
// "this" is not what you expect here
return this.heroService.getHero(+params['id']);
}
So this.heroService is undefined here.
You'd need to bind your getHero function.
.switchMap(this.getHero.bind(this))
Otherwise your change is identical. Using bind like this allows you to pass getHero as a standalone function to switchMap without losing what this means to it.
You can experiment with it:
'use strict';
const foo = {
bar: 'baz',
getBar: function() {
return this.bar;
}
};
foo.getBar();
// => 'baz'
const myGetBar = foo.getBar;
myGetBar();
// => TypeError: Cannot read property 'bar' of undefined
const boundGetBar = foo.getBar.bind(foo);
boundGetBar();
// => 'baz'
const otherObj = { bar: 'hi' };
const otherBoundGetBar = foo.getBar.bind(otherObj);
otherboundGetBar();
// => 'hi'
otherObj.getBar = myGetBar;
otherObj.getBar();
// => 'hi'
You cannot use this.getHero like in your snippet because
it's undefined (the service returns Observable that you have to subscribe before using its data)
it's not a property (doesn't have get modifyer).
I use the following code which is working great but I wonder if in JS there is a way to avoid the if and to do it inside the loop, I want to use also lodash if it helps
for (provider in config.providers[0]) {
if (provider === "save") {
....
You can chain calls together using _.chain, filter by a value, and then use each to call a function for each filtered result. However, you have to add a final .value() call at the end for it to evaluate the expression you just built.
I'd argue that for short, simple conditional blocks, an if statement is easier and more readable. I'd use lodash- and more specifically chaining- if you are combining multiple operations or performing sophisticated filtering, sorting, etc. over an object or collection.
var providers = ['hello', 'world', 'save'];
_.chain(providers)
.filter(function(provider) {
return provider === 'save';
}).each(function(p) {
document.write(p); // your code here
}).value();
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.8.0/lodash.js"></script>
Edit: My mistake; filter does not have an overload where you can just supply a literal value. If you want to do literal value checking you have to supply a function as in my amended answer above.
I'd argue that what you have there is pretty good, clean and readable, but since you mentioned lodash, I will give it a try.
_.each(_.filter(config.providers[0], p => p === 'save'), p => {
// Do something with p
...
});
Note that the arrow function/lambda of ECMAScript 6 doesn't come to Chrome until version 45.
Basically, you are testing to see if config.providers[0], which is an object, contains a property called save (or some other dynamic value, I'm using a variable called provider to store that value in my example code below).
You can use this instead of using a for .. in .. loop:
var provider = 'save';
if (config.providers[0][provider] !== undefined) {
...
}
Or using #initialxy's (better!) suggestion:
if (provider in config.providers[0]) {
...
}
How about:
for (provider in config.providers[0].filter(function(a) {return a === "save"}) {
...
}
Strategy, you are looking for some kind of strategy pattern as,
Currenlty the save is hardcoded but what will you do if its coming from other varible – Al Bundy
var actions = {
save: function() {
alert('saved with args: ' + JSON.stringify(arguments))
},
delete: function() {
alert('deleted')
},
default: function() {
alert('action not supported')
}
}
var config = {
providers: [{
'save': function() {
return {
action: 'save',
args: 'some arguments'
}
},
notSupported: function() {}
}]
}
for (provider in config.providers[0]) {
(actions[provider] || actions['default'])(config.providers[0][provider]())
}
Push „Run code snippet” button will shows two pop-ups - be carefull
It is not clearly stated by the original poster whether the desired output
should be a single save - or an array containing all occurrences of
save.
This answer shows a solution to the latter case.
const providers = ['save', 'hello', 'world', 'save'];
const saves = [];
_.forEach(_.filter(providers, elem => { return elem==='save' }),
provider => { saves.push(provider); });
console.log(saves);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.js"></script>