Consider:
$scope.taylor = {
firstName: 'taylor',
lastName: 'mcintyre',
order: 22
}
Using $resource, I might want to save this:
people.save($scope.taylor);
However, I do not want the property "order" to be sent along with the request.
Angular ignores properties prefixed with '$$' for it's own internal use, but it doesn't feel right prefixing my own hidden properties in this way, e.g.
$scope.taylor = {
firstName: 'taylor',
lastName: 'mcintyre',
$$order: 22
}
Deleting unwanted properties is the common-sense solution, but does Angular have a better solution for this?
I know you are looking for an "Angular way" to exclude keys, but angular.copy() doesn't seem to support this. The angular.toJson() documentation states: Properties with leading $ characters will be stripped since angular uses this notation internally. This sounds like using $ should be reserved for angular and not used by us in our objects.
In light of the situation I created a simple CodePen example showing how easily this can be done using a library like UnderscoreJS.
I'm sure there are more elegant ways to do this, but my example does accomplish what I understood to be your primary goal.
I included the UnderscoreJS library in my file and added the following code:
var person = {
firstName: 'John',
lastName: 'Smith',
order: 22,
excludeKeys: [
'order',
'excludeKeys'
]
};
var personCopy = _.omit(person, person.excludeKeys);
console.log('person: ', person);
console.log('person copy: ', personCopy);
I hope this is useful.
Related
I'm trying to optimize my E2E tests to incorporate the usage of entities.
Our tests are basically filling data into a form on a webpage. Our tests are using the PageObject method where our PageObject stores our elements in variables and we also have variables containing the interactions with the elements stored in the PO file.
Our spec file is what calls the PO file and inputs the data into each element similar to(this is just examples of what we are doing):
PO File:
this.firstNameField = by.model('firstName');
this.lastNameField = by.model('lastName');
this.setFirstNameField = function(firstname) {
element(this.firstNameField).sendKeys(firstname);
};
this.setLastNameField = function(lastname) {
element(this.lastNameField).sendKeys(lastname);
};
Spec file:
pageObject.setFirstNameField('TestName');
pageObject.setLastNameField('TestLastName');
In our spec file, we have roughly 100 lines of this code which is not very effecient from what I can tell. I want to remove this style and use Entity's instead, however I'm not sure exactly how i would go about this, hence why I'm coming here.
A friend of mine gave me a hint of how i'd go about this and here is what he has provided me:
spec file:
var nameEntity = {
firstName: 'TestName',
lastName: 'TestLastName'
};
pageObject.PopulateUIWithNameEntity(nameEntity);
Now i know i can switch the nameEntity to be stored in the pageObejct file, however i'm not exactly sure how the PopulateUIWIthNameEntity should be created.
I've tried the following but I can't seem to get it to input the values from the nameEntity into the element itself.
pageObject File:
this.PopulateUIWithNameEntity = function(nameEntity) {
element(this.setFirstNameField).sendKeys(nameEntity);
};
You were close... just needed a little refactor.
Adding your test data to an object (hash) is definitely a good idea. Then you just need to extract the elements from it in your method. You also already have individual methods for each individual action... so you just needed to use them.
spec...
var nameEntity = {
firstName: 'TestName',
lastName: 'TestLastName'
};
pageObject.populateUIWithNameEntity(nameEntity);
page object...
this.populateUIWithNameEntity = function(nameEntity) {
this.setFirstNameField(nameEntity.firstName);
this.setLastNameField(nameEntity.lastName);
};
Using AngularFire, I am extending the object factories in order to have encapsulated data and to allow specific features, as explained in the official tutorial. I have a data structure like the following:
{
'articles': {
'article-asd876a': {
title: 'abc',
text: 'lorem ipsum ...',
comments: {
'comment-ad4e6a': true,
'comment-dsd9a7': true
}
}
},
'comments': {
'comment-ad4e6a': {
text: 'comment text1',
articleId: 'article-asd876a'
},
'comment-dsd9a7': {
text: 'comment text2',
articleId: 'article-asd876a'
}
}
}
Now I would love to be able to do this:
var article = new Article(8); // Returns the object created by my object factory, fetching data from firebase
var comments = article.getComments(); // Returns an array of type Comment
var firstText = comments[0].getText();
var article2 = comments[0].getArticle(); // article2 === article
But this fails for me on many levels. One of them being: In Article, I can only store the Comment ID, and therefore have to recreate the Comment Object using new Comment(commentId), for which I need to inject Comment into Article. The same is true for Comment, so that I end up with a circular dependency Article -> Comment -> Article. The following fiddle shows the behavior: http://jsfiddle.net/michaschwab/v0qzdgtq/.
What am I doing wrong? Is this a bad concept/structure for angular? Thanks!!
What am I doing wrong? Is this a bad concept/structure for angular?
Thanks!!
You are creating circular dependencies.
I can do var Comment = $injector.get('Comment'); to avoid the error,
is that the best solution?
I see two solutions:-
1) Lazy injecting solution (you suggested yourself)
This is the best solution to avoid those circular dependencies in AngularJS. Although looking at the AngularFire documentation you are into an uncharted territory as some of these things in AngularFire are experimental in nature.
Here is the working fiddle from your
var Comment = $injector.get('Comment');
You are essentially lazy injecting your references.
http://jsfiddle.net/yogeshgadge/ymxkt6up/5/
2) Module Run block:
With this option you may be able to inject those dependencies into your factories instead of using $injector.
My idea is to have a list of words in which there are lots of diacritics or accents. And for example instead of writing Róse to have Róse I'd like to write Rose and have Róse in results.
First of all I googled for it, here in StackOverflow the problem was partially solved.
But what if I have array like this, check jsFiddle:
$scope.names = [{name: 'Jamón', surname: 'Géroux'},
{name: 'Andrés', surname: 'Guérin'},
{name: 'Cristián', surname: 'Róse'},
{name: 'Fernán', surname:'Raúlien'}];
};
Then the solution doesn't work: jsFiddle.
And what if I had, for example, custom filter for highlight:
<td ng-bind-html="name.name | highlight:search.$"></td>
I've fount out, that all examples here https://builtwith.angularjs.org have the same problem with diacritics/accents. And only one website there http://illicoweb.videotron.com uses "simple technics" with JavaScript function and str.replace.
Any ideas on really simple and fast solution? Without storing two tables with diacritics/accents and without it.
Thanks in advance!
I've updated this JSFiddle with my modifications to your filter. I've simply modified the filter to search the full name rather than just the first name as demonstrated:
$scope.ignoreAccents = function(item) {
if (!$scope.search)
return true;
var fullName = item.name + ' ' + item.surname;
var text = removeAccents(fullName.toLowerCase());
var search = removeAccents($scope.search.toLowerCase());
return text.indexOf(search) > -1;
};
This approach should hopefully negate any need for two separate tables.
I have a TS definition file for Ext JS containing function store.add(a : any) (it has many overloads so I guess this is to simplify the d.ts file). I want to pass it a literal object which implements a Person interface:
interface Person
{
name: string;
age: number
}
store.add(<Person>{ name: "Sam" });
This gives me intellisense but unfortunately it is just coercing the literal into a Person, without detecting the missing field. This works as I want:
var p : Person = { name: "Sam" }; // detects a missing field
store.add(p);
But is there a way to do this without a separate variable?
I realise this would be solved with 'fixed' definition files, but I think many Javascript libraries have too many overloads to allow this. I almost need a way to dynamically overload the function definition..! Would generics help here?
Yes generics seem to be the answer. In the definition file changing:
add?( model:any ): Ext.data.IModel[];
to
add?<T>( model:T ): Ext.data.IModel[];
Allows you to call
store.add<Person>({ name: "sam" });
And it correctly shows an error!
I recently started learning AngularJS+Firebase. I'm trying to write in my firebase an object like this:
{
title: "Personal Information",
say: [
[{ "eng": "What's", "ukr": "Що є" }, { "eng": "your", "ukr": "твоє" }, { "eng": "surname?", "ukr": "прізвище?" }],
[{ "eng": "Smith", "ukr": "Сміт" }],
[{ "eng": "What's", "ukr": "Що є" }, { "eng": "your", "ukr": "твоє" }, { "eng": "first", "ukr": "перше" }, { "eng": "name?", "ukr": "ім'я?(не фамілія)" }]
]
}
with line:
lessondata.add($scope.topic);
where 'lessondata' is service created with angularFireCollection() and $scope.topic - object bound to my UI.
But got the following error:
Firebase.push failed: first argument contains an invalid key ($$hashKey) in property 'say.0.0'. Keys must be non-empty strings and can't contain ".", "#", "$", "/", "[", or "]"
As I understood Firebase do not allow to use 0 as a key even if it's a key in an attached array for which zero key is natural. So should I change my object structure in some hardcoded instance or I miss something? Thanks in advance!
EDIT: As Anant points out in the comments, in the latest stable version of Angular (1.0.7 atm), you can use angular.copy(obj) to remove $$hashkey attributes.
Like Michael said, the '$' in '$$hashKey' is the issue. Angular creates the $$hashKey properties behind the scenes (see more here: https://groups.google.com/forum/#!topic/angular/pI0IgNHKjxw). I've gotten around this issue by doing something like myRef.push(angular.fromJson(angular.toJson(myAngularObject))).
The issue is the $ in "$$hashKey", not the 0. 0 is allowed.
I wanted to throw another answer in here that is much simpler, just use track by in your repeat. It will get rid of the $$hashKey attribute that is causing so much grief.
<div ng-repeat="item in items track by $index">{{item.name}}</div>
I'm a bit late to this but I thought I would like to add my two cents as I was shaking my head to all the other answers. Hopefully this can help you to avoid this issue all together.
Use the angularFire library intended to handle angular data and use it's methods.
while yes you can use the pure javascript library methods to .push() .add() .update(), .set() ect.
So if you want to avoid any clashes when firebase communicates with angular javascript you need to be using the appropriate .$foo() methods (i.e. .$save()). In your case just add the $ to your .add() (make it .$add())
so use lessondata.$add($scope.topic);
differences when saving with firebase's vs angularfire's
AngularFire's $save() method is implemented using Firebase's set() method.
Firebase's push() operation corresponds to AngularFire's $add() method
Typically you should be using set()/$save() if you either have an object that already exists in the database or if you are working with objects that have a natural key.
more info on that here: https://stackoverflow.com/a/35959496/4642530
Things to note with AngularFire
For a callback:
and if you want a callback to know if your data saved correctly you can do something like this:
var list = $firebaseArray(ref);
list.$add({ foo: "bar" }).then(function(ref) {
var id = ref.key();
console.log("added record with id " + id);
list.$indexFor(id); // returns location in the array
});
I was surprised this wasn't mentioned earlier in any of the other answers, but take a look at these docs https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray-addnewdata
Cheers.
The best way to get rid of the $$hasKeys is to use "track by" in ng-repeats as in the following (as mentioned in the answer above)
<div ng-repeat="(key, value) in myObj track by $index"> ... </div>
but you can also set track as par of ng-model-options
ng-model-options="{ trackBy: '$value.someKeyOnYourObject' }"
on a form control. This way also improves performance of your angular app.
Another way is to remove the $$hashKey is using
angular.copy(someObj);
If all else fails, you could also use lodash to remove the keys that start with the "$".
_.omitBy(yourObject, function(value, key){return _.startsWith(key, '$');});
You can also strip out the property before pushing it.
delete $scope.topic.$$hashKey