Create objects on Ionic - angularjs

Good morning!
I've been working with Ionic for a few weeks and I thought I actually understood how typescript and angular works, but I've found myself with a weird trouble that I think it will be very silly...
I'm trying to create an object called pendingWaybills with some properties named waybills, clients and clientWaybills. The thing is that I'm creating it this way:
pendingWaybills: {
waybills: any
clients: any,
clientWaybills: any,
};
I've also tried
pendingWaybills: {
"waybills": any,
"clients": any,
"clientWaybills": any,
};
And some other ways, but when I try to assign a value to this properties I'm getting the following error: TypeError: Cannot set property 'waybills' of undefined
I've also tried to assign some string or integers just to see if it was about the data that I was trying to assign like this.pendingWaybills.waybills = "Hi"; but I'm still getting the same error...
Would be glad to get some help as I think it's all about the way to create the object (and I also think it will be very silly) but I'm so stuck here.
Thank you!
Edit:
Here is where I try to assign the data to the object. (The variable data is a json)
loadPendingWaybills(){
this.loadClients(2)
.then(data => {
this.pendingWaybills.waybills = data;
var preClients = this.pendingWaybills.waybills;
this.clients = [];
for(let i = 0;i < preClients.length; i++){
if(this.pendingWaybills.clients.indexOf(preClients[i].descr1_sped) == -1){
this.pendingWaybills.clients.push(preClients[i].descr1_sped)
}
}
this.pendingWaybills.clientWaybills = [];
for(let i = 0; i < this.pendingWaybills.clients.length; i++){
this.getWaybills(this.pendingWaybills.clients[i], 2)
.then(data => {
if(this.pendingWaybills.clientWaybills[i] != data){
this.pendingWaybills.clientWaybills[i] = data;
}
});
}
});
}

You need to create an empty instance of the object, declaring the properties doesn't create the variable, it only tells your ide which properties it has:
public pendingWaybills = {
waybills: []
clients: [],
clientWaybills: [],
};

In Typescript, doing :
pendingWaybills: {
waybills: any;
clients: any;
clientWaybills: any;
};
will only set pendingWaybills variable type. To declare and assign value to the variable, you must do something like :
pendingWaybills = { // note the "="
waybills: something,
clients: something,
clientWaybills: something,
};

Put this in constructor
constructor() {
this.pendingWaybills = {
waybills: [],
clients: [],
clientWaybills: [],
};
}
Some explanation
It is nested object you can not create right away when you declare it.
For example var xxx = 'hi'; is fine. but if you do var xxx.yyy = 'hi' is not fine, as xxx is not defined before so yyy of xxx will cause error.
You can do
var xxx = {
yyy: 'hi'
};
or you can do
var xxx = {};
xxx.yyy = 'hi';

Related

I am trying to take data from "blog" and push it to "blogs", yet an error occurs

I'm getting the following error:
**Error1:** Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables, such as Arrays. Did you mean to use the keyvalue pipe?
**Error2:** this.blogs.push is not a function
My code looks like this:
export class BlogComponent {
blogs: Array<blogType>;
constructor() {
this.blogs = new Array<blogType>();
}
ngOnInit() {
this.blogs = JSON.parse(localStorage.getItem("blogs")!);
}
addBlog(title: any, content: any) {
let blog = new blogType(title.value, content.value);
if (localStorage.getItem('blogs')) {
this.blogs = JSON.parse(localStorage.getItem('blogs')!);
}
this.blogs.push(blog); //error occurs because of that line. Runtime error
localStorage.setItem('blogs', JSON.stringify(this.blogs));
title.value = '';
content.value = '';
alert('Blog Added!');
}
I am trying to take data from the "blog" array and push it to the "blogs" array in order to store it in the localstorage. Yet I get an error because of the folowing line: this.blogs.push(blog);
Check the contents of LocalStorage for null before you parse it and assign to this.blogs:
ngOnInit() {
var current = localStorage.getItem('blogs');
if (current !== null) {
this.blogs = JSON.parse(current);
}
console.log('this.blogs is: ' + this.blogs);
}

CanJS - Incorrect list objects type after mapping

If I have this instantiation const someObj = new MyObj({...someParams}) and MyObj is defined this way:
MyObj = DefineMap.extend("MyObj", {
myProp: {
Type: AnotherType.List
}
})
and
AnotherType.List = CanList.extend("AnotherType.List", {
Map: AnotherType
}, {
serialize() {
const res = [];
for (let i = 0; i < this.attr("length"); i++) {
res.push(this.attr(i.toString()).serialize());
}
return res;
}
})
then the someObj.myProp[0] type is DefineMap instead of AnotherType. How can I fix this?
I already tried this but still not working: const someObj = canReflect.assignDeep(new MyObj(), {...someParams})
With the help of Bitovi community I solved it (thanks to Brad Momberger).
So Brad explained that it happens because of the DefineList's bubble binding and the same doesn't happen with DefineMap objects.
So we always need to serialize the lists first the get the correct type of the list entries at the end, like this:
const someObj = new MyObj({
...someParams,
myProp: someParams.myProp.serialize()
})
You can see the full explanation here.

ANGULAR Components array key in result get value by id

this.crudService.get('user.php?mode=test')
.subscribe((data:any) => {
{ for (var key in data) { this[key] = data[key]; } };
}
);
This use to work on angular 7 now on angular 13 i get this error (look image)
In template i was using the values for example in json string was and array and i had users, in template was {{users}} , {{posts}} etc.. now the this[key] give error , please help me out its very important can't find solution
i'll show an example code, and then applied to your code:
Example
// creating global variables to receive the values
users: any = null;
posts: any = null;
// simulating the data you will receive
data: any[] = [
{users: ['user1', 'user2', 'user3']},
{posts: ['post1', 'post2', 'post3']}
];
getCrudService() {
// access each object of the array
this.data.forEach(obj => {
// getting keys name and doing something with it
Object.keys(obj).forEach(key => {
// accessing global variable and setting array value by key name
this[String(key)] = obj[String(key)]
})
})
}
Apllied to your code
this.crudService.get('user.php?mode=test').subscribe((data:any) => {
data.forEach(obj => {
Object.keys(obj).forEach(key => {
this[String(key)] = obj[String(key)]
});
});
});
I hope it helped you, if you need help, just reply me.

Why Can't Iterate over an array in my model using the map() function

i have angular 7 component which is tied to a model and there is an array inside that model, the array was populated from a service. and it's populated.
the problem is i can't map over the array although it has elements there.
when i console it it shows the array has element. then i tried to console typeOf(array) it always gives object although it is an array !!.
i tried using this soluation but it didn't help either.
any help please?
export class FooModel {
foo : Foo
bars: Bar[];
}
export class SomeComponent implements OnInit {
model: FooModel;
constructor(private service: ProjectService) {
this.model = new FooModel();
this.model.bars = [];
}
ngOnInit() {
this.service.getFoos().subscribe((result: any) => {
// data is populated fine
this.model= <FooModel>result.data;
});
Console.log(this.model); // the model has data at this point
const arr = this.model.bars.map(a=> {
// never comes here
return a;
});
console.log(arr); // nothing is displayed here
// this works why ??
const arr2 = [1,2,3].map(s=> {
return s;
}
console.log(arr2); // it displays [1,2,3]
}
}
As the request is asynchronous, you might need to place the logic within the subscribe,
this.service.getFoos().subscribe((result: any) => {
// data is populated fine
this.model= <FooModel>result.data;
const arr = this.model.bars.map(a=> {
// never comes here
return a;
});
console.log(arr);
});
subscription is asynchronous so while it is still working the next line operation in the execution stack will be performed in this case the map you have after the subscription meanwhile it is still being populated in the background. You can try mapping in another life cycle hook say viewChecked hopefully it works. #cheers
Please look at the comments
export class FooModel {
foo : Foo
bars: Bar[];
}
export class SomeComponent implements OnInit {
model: FooModel;
constructor(private service: ProjectService) {
this.model = new FooModel();
this.model.bars = [];
}
ngOnInit() {
this.service.getFoos().subscribe((result: any) => {
// data is populated fine
this.model= <FooModel>result.data;
});
// the following starts to execute even before the model is populated above.
const arr = this.model.bars.map(a=> {
// never comes here because this.model.bars is empty at here and the length is 0 and nothing inside map executes
return a;
});
console.log(arr); // nothing is displayed here because it has nothing inside
// this works why ?? because you are using on an array which has some items.
const arr2 = [1,2,3].map(s=> {
return s;
}
console.log(arr2); // it displays [1,2,3]
}
}
So as Sajeetharan suggested, you have keep it inside subscribe()

How to specify the shape of a map with numeric keys using React.PropTypes? [duplicate]

React has lots of ways of using PropTypes to check the value of a prop. One that I commonly use is React.PropTypes.shape({...}). However, I recently came across a situation where I have an object that will have dynamic key/values inside. I know that each key should be a string (in a known format), and each value should be an int. Even using a custom prop validation function, it still assumes you know the key of the prop. How do I use PropTypes to check that both the keys and values of an object/shape are correct?
...
someArray: React.PropTypes.arrayOf(React.PropTypes.shape({
// How to specify a dynamic string key? Keys are a date/datetime string
<dynamicStringKey>: React.PropTypes.number
}))
...
So again: I want to at the very least check that the value of each key is a number. Ideally, I would also like to be able to check the the key itself is a string in the correct format.
To validate only the values, you can use React.PropTypes.objectOf.
...
someArray: React.PropTypes.arrayOf(
React.PropTypes.objectOf(React.PropTypes.number)
)
...
Note: This answer was written in 2015 when the current version of React was 0.14.3. It may or may not apply to the version of React you're using today.
That's an interesting question. From your question it sounds like you've
read about custom type checkers in the docs for Prop Validation.
For posterity I'll reproduce it here:
// You can also specify a custom validator. It should return an Error
// object if the validation fails. Don't `console.warn` or throw, as this
// won't work inside `oneOfType`.
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
When implementing type checkers I prefer to use React's built-in type
checkers as much as possible. You want to check if the values are
numbers, so we should use PropTypes.number for that, right? It would
be nice if we could just do PropTypes.number('not a number!') and get
the appropriate error, but unfortunately it's a little more involved
than that. The first stop is to understand...
How type checkers work
Here's the function signature of a type checker:
function(props, propName, componentName, location, propFullName) => null | Error
As you can see, all of the props are passed as the first argument and
the name of the prop being tested is passed as the second. The last
three arguments are used for printing out useful error messages and are
optional: componentName is self-explanatory. location will be one of
'prop', 'context', or 'childContext' (we're only interested in
'prop'), and propFullName is for when we're dealing with nested
props, e.g. someObj.someKey.
Armed with this knowledge, we can now invoke a type checker directly:
PropTypes.number({ myProp: 'bad' }, 'myProp');
// => [Error: Invalid undefined `myProp` of type `string` supplied
// to `<<anonymous>>`, expected `number`.]
See? Not quite as useful without all of the arguments. This is better:
PropTypes.number({ myProp: 'bad' }, 'myProp', 'MyComponent', 'prop')
// => [Error: Invalid prop `myProp` of type `string` supplied
// to `MyComponent`, expected `number`.]
An array type checker
One thing the docs don't mention is that when you supply a custom type
checker to PropTypes.arrayOf, it will be called for each array
element, and the first two arguments will be the array itself and the
current element's index, respectively. Now we can start sketching out
our type checker:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
console.log(propFullName, obj);
// 1. Check if `obj` is an Object using `PropTypes.object`
// 2. Check if all of its keys conform to some specified format
// 3. Check if all of its values are numbers
return null;
}
So far it'll always return null (which indicates valid props), but we
threw in a console.log to get a peek at what's going on. Now we can
test it like this:
var typeChecker = PropTypes.arrayOf(validArrayItem);
var myArray = [ { foo: 1 }, { bar: 'qux' } ];
var props = { myProp: myArray };
typeChecker(props, 'myProp', 'MyComponent', 'prop');
// -> myProp[0] { foo: 1 }
// myProp[1] { bar: 'qux' }
// => null
As you can see, propFullName is myProp[0] for the first item and
myProp[1] for the second.
Now let's flesh out the three parts of the function.
1. Check if obj is an Object using PropTypes.object
This is the easiest part:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
// Check if `obj` is an Object using `PropTypes.object`
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
return null;
}
var typeChecker = PropTypes.arrayOf(validArrayItem);
var props = { myProp: [ { foo: 1 }, 'bar' ] };
typeChecker(props, 'myProp', 'MyComponent', 'prop');
// => [Error: Invalid prop `myProp[1]` of type `string` supplied to
// `MyComponent`, expected `object`.]
Perfect! Next...
2. Check if all of its keys conform to some specified format
In your question you say "each key should be a string," but all object
keys in JavaScript are strings, so let's say, arbitrarily, that we want
to test if the keys all start with a capital letter. Let's make a custom
type checker for that:
var STARTS_WITH_UPPERCASE_LETTER_EXPR = /^[A-Z]/;
function validObjectKeys(props, propName, componentName, location, propFullName) {
var obj = props[propName];
var keys = Object.keys(obj);
// If the object is empty, consider it valid
if (keys.length === 0) { return null; }
var key;
var propFullNameWithKey;
for (var i = 0; i < keys.length; i++) {
key = keys[i];
propFullNameWithKey = (propFullName || propName) + '.' + key;
if (STARTS_WITH_UPPERCASE_LETTER_EXPR.test(key)) { continue; }
return new Error(
'Invalid key `' + propFullNameWithKey + '` supplied to ' +
'`' + componentName + '`; expected to match ' +
STARTS_WITH_UPPERCASE_LETTER_EXPR + '.'
);
}
return null;
}
We can test it on its own:
var props = { myProp: { Foo: 1, bar: 2 } };
validObjectKeys(props, 'myProp', 'MyComponent', 'prop');
// -> myProp.Foo Foo
// myProp.bar bar
// => [Error: Invalid key `myProp.bar` supplied to `MyComponent`;
// expected to match /^[A-Z]/.]
Great! Let's integrate it into our validArrayItem type checker:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
// Check if `obj` is an Object using `PropTypes.object`
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
// Check if all of its keys conform to some specified format
var validObjectKeysError = validObjectKeys(props, propFullName, componentName);
if (validObjectKeysError) { return validObjectKeysError; }
return null;
}
And test it out:
var props = { myProp: [ { Foo: 1 }, { bar: 2 } ] };
var typeChecker = PropTypes.arrayOf(validArrayItem);
typeChecker(props, 'myProp', 'MyComponent', 'prop');
// -> myProp[0].Foo Foo
// myProp[1].bar bar
// => [Error: Invalid key `myProp[1].bar` supplied to `MyComponent`;
// expected to match /^[A-Z]/.]
And finally...
3. Check if all of its values are numbers
Happily, we don't need to do much work here, since we can use the
built-in PropTypes.objectOf:
// Check if all of its values are numbers
var validObjectValues = PropTypes.objectOf(PropTypes.number);
var validObjectValuesError = validObjectValues(props, propFullName, componentName, location);
if (validObjectValuesError) { return validObjectValuesError; }
We'll test it below.
All together now
Here's our final code:
function validArrayItem(arr, idx, componentName, location, propFullName) {
var obj = arr[idx];
var props = {};
props[propFullName] = obj;
// Check if `obj` is an Object using `PropTypes.object`
var isObjectError = PropTypes.object(props, propFullName, componentName, location);
if (isObjectError) { return isObjectError; }
// Check if all of its keys conform to some specified format
var validObjectKeysError = validObjectKeys(props, propFullName, componentName);
if (validObjectKeysError) { return validObjectKeysError; }
// Check if all of its values are numbers
var validObjectValues = PropTypes.objectOf(PropTypes.number);
var validObjectValuesError = validObjectValues(props, propFullName, componentName, location);
if (validObjectValuesError) { return validObjectValuesError; }
return null;
}
We'll write a quick convenience function for testing and throw some data
at it:
function test(arrayToTest) {
var typeChecker = PropTypes.arrayOf(validArrayItem);
var props = { testProp: arrayToTest };
return typeChecker(props, 'testProp', 'MyComponent', 'prop');
}
test([ { Foo: 1 }, { Bar: 2 } ]);
// => null
test([ { Foo: 1 }, { bar: 2 } ]);
// => [Error: Invalid key `testProp[1].bar` supplied to `MyComponent`;
// expected to match /^[A-Z]/.]
test([ { Foo: 1 }, { Bar: false } ]);
// => [Error: Invalid prop `testProp[1].Bar` of type `boolean` supplied to
// `MyComponent`, expected `number`.]
It works! Now you can use it in your React component just like the
built-in type checkers:
MyComponent.propTypes = {
someArray: PropTypes.arrayOf(validArrayItem);
};
Of course, I would recommend giving it a more meaningful name and moving
it into its own module.
The Problem for me was ...
Object has dynamic keys (which i don't wanna check)
Values have a fixed data structure (which i wanna check)
Shape of the underlying data structure is known and can be checked
// An object with property values of a certain shape
optionalObject: PropTypes.objectOf(
PropTypes.shape({
color: PropTypes.string.isRequired,
fontSize: PropTypes.number
})
);
So for my problem the missing part was PropTypes.objectOf from there you are able to create any type of structures with dynamic keys.
Also in combination with PropTypes.oneOfType it becomes very flexible.
You can create your own validator by passing a function.
See customProp here.
I believe you can do something like React.PropTypes.arrayOf(customValidator).
Here's the validator you're looking for:
function objectWithNumericKeys(obj) {
if (Object.keys(obj).some(key => isNaN(key)))
return new Error('Validation failed!');
}

Resources