Angular.js data accessor - javascript

I'm trying to learn Angular, and I'm stuck on the following.
I have a PHP background, mostly use Laravel, and in Laravel you can use accessors in your models. So if you have a model User, which has a firstname and lastname. You can create an accessor for fullname which will return both the firstname and lastname:
public function getFullNameAttribute()
{
return $this->firstname . ' ' . $this->lastname;
}
Now, I would like to do the same thing in Angular.
I have a UserController with a user:
function UserController($scope) {
$scope.users = [
{ firstname: 'John', lastname: 'Doe' }
];
}
In the "view" I want to loop over the users with ng-repeat, but instead of doing
{{ user.firstname }} {{ user.lastname }}
I would like to be able to just use
{{ user.fullname }}
Maybe it's called differently in Angular or something, but I just can't seem to find it...
Thanks for the help!

Angular does not yet natively provide model abstractions (beside simplistic $resource service which deals mostly with RESTfull communication layer).
But that doesn't mean you can't write and re-use your own custom model accessors.
Option 1 - Use custom filter
PLUNKER
app.filter("fullName", function () {
return function (user){
return user && [user.firstName, user.lastName].join(' ');
}
});
app.controller("MainCtrl", [
"$scope",
function($scope) {
$scope.user = {
firstName: 'John',
lastName: 'Doe'
};
}
]);
User fullName: {{user | fullName}}
 
Option 2 - Use custom $resource instance accessors:
PLUNKER
app.factory('UserService', function ($resource){
// User is an instance of $resource service which
// in this example uses mock server endpoint
var User = $resource('user.json');
// Custom User accessor method
User.prototype.fullName = function(){
return [this.firstName, this.lastName].join(' ');
}
return User;
});
app.controller("MainCtrl", [
"$scope",
"UserService",
function($scope, UserService) {
$scope.user = UserService.get()
}
]);
User fullName: {{user.fullName()}}

In addition to Stewie's very sophisticated techniques, you can also use ng-init.
<div ng-repeat="user in users"
ng-init="user.fullname = user.firstname + ' ' + user.lastname">
{{user.fullname}}
</div>
http://plnkr.co/zU6vM5f8pI3Veh7jr1R9

angular does not have a model framework, here your scope is the model and any javascript object (plain old javascript object) attached to it. You might be interested in this article which talks about implementing an OO model layer on angular js. https://medium.com/opinionated-angularjs/2e6a067c73bc. Like others have said, implementing filters or directives might be the easiest and is also reusable.

Related

Ember.js: how can I find all bindings for a component without knowing the model and properties

How can I find all bindings for a given component at runtime. Like in example below, I would like to get the attribute name ('value') and the bound model property ('model.firstName') either from the route or controller.
Example:
Html
<script type="text/x-handlebars" data-template-name="index">
<x-input label="Name" value={{model.firstName}}></x-input>
</script>
JS
var App = Ember.Application.create({});
App.Person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function() {
return (this.get('firstName') + ' ' + this.get('lastName')).trim();
})
});
App.IndexRoute = Ember.Route.extend({
person: null,
model: function () {
if(!this.person) {
this.person = App.Person.create({
firstName: '',
lastName: ''
})
}
return this.person;
},
actions: {}
});
I am trying to use webcomponents and not ember components ({{...}}). One way data-binding works for the above example, but I am trying to figure out if I can wire two way databinding to this. I am very new to ember and wanted some pointers if there is an API to get all existing bindings for a component.

Firebase $id from second level Object

So I have this for example in firebase
clients {
//clients with unique keys {
invoices: {
// Invoices with unique Keys
}
}
}
I'm returning all this with one ref like so:
.controller('singleClientController', function($scope, $firebaseObject, fbUrl, $routeParams) {
var id = $routeParams.id;
var singleRef = new Firebase(fbUrl+'/clients/'+id);
var client = this;
client.details = $firebaseObject(singleRef);
})
so in my html, I'm trying to return the $id for both the client and the invoice. I am able to get the client no problem with {{client.details.$id}} but when I try to do the same thing with the invoice id {{invoice.$id}} I don't get anything.
The invoices are displayed through a foreach like so:
<tr ng-repeat="invoice in client.details.invoices">
<td>
<a href="#/invoices/details/{{invoice.$id}}/{{client.details.$id}}">
{{invoice.settings.number}}
</a>
</td>
...
</tr>
Is it because the invoices are inside the client? If so, how would you return the id for the invoices? It's driving me nuts! Please help!
For a better understanding of my firebase set-up here is a screenshot of what I'm talking about.
tl;dr - In Firebase it is ideal to have a flat data structure.
JSBin Example
In your case you have invoices nested under clients.
{
"clients": {
"1": {
"name": "Alison",
"invoices": {
"0001": {
"amount": 500
"paid": true
}
}
}
}
}
This feels natural because the invoices are nicely grouped underneath the proper client. However, this can lead to bad performance in syncing data with Firebase. Firebase reads all data underneath a node.
This means every time you read /clients/1 you also get the invoices downloaded over the network. Even if you just need the client's name, you'll also get the the invoices.
The solution is to flatten your data structure.
{
"clients": {
"1": {
"name": "Alison"
}
},
"clientInvoices": {
"1": {
"0001": {
"amount": 500
"paid": true
}
}
}
}
The important part to grasp here is the shared key.
In this example the key is 1. This is for simplicity. In reality you'll likely use a .push() id.
By using this pattern you'll still be able to retrieve all of the invoices for the client by simply knowing their key. This also decouples the client from the invoices.
As an added benefit your controller code and ng-repeat will be much easier.
In your case you should switch from a $firebaseObject to a $firebaseArray for the invoices. We can even create a helper factory that gets the invoices by a client's id.
.factory('invoices', function(fbUrl, $firebaseArray) {
return function(clientId) {
var ref = new Firebase(fbUrl).child('clientInvoices').child(clientId);
return $firebaseArray(ref);
}
})
// While we're at it, lets create a helper factory for retrieving a
// client by their id
.factory('clients', function(fbUrl, $firebaseObject) {
return function(clientId) {
var ref = new Firebase(fbUrl).child('clients').child(clientId);
return $firebaseObject(ref);
}
})
Now inject the helper factories into your controller and use the $routeParams.id to retrieve the client's invoices:
.controller('singleClientController', function($scope, $routeParams, invoices, clients) {
$scope.client = clients($routeParams.id);
$scope.clientInvoices = invoices($routeParams.id);
})
Now binding it to your template is a breeze:
<tr ng-repeat="invoice in clientInvoices">
<td>
<a href="#/invoices/details/{{invoice.$id}}/{{client.$id}}">
{{invoice.settings.number}}
</a>
</td>
...
</tr>

AngularJS + Parse API call service not constrained by userId as intended

For my app, I've created a service for Address, which allows me to manipulate Address objects for any given user. Aside from my standard CRUD functions, I need to have one function to list any address for a specified Parse.User.
services.js
.factory('Address',['$http', 'PARSE_CREDENTIALS', function ($http,PARSE_CREDENTIALS) {
return {
// constrain to User ID
getAll: function(userId) {
return $http.get('https://api.parse.com/1/classes/Address', {
headers: {
'X-Parse-Application-Id': PARSE_CREDENTIALS.APP_ID,
'X-Parse-REST-API-Key': PARSE_CREDENTIALS.REST_API_KEY,
'Content-Type' : 'application/json'
},
params: { "userId": userId }
});
},
// ...get(), edit(), add(), delete()
controllers.js
.controller('AddrCtrl', ['$scope', '$state', '$stateParams', '$rootScope', 'Address',
function($scope, $state, $stateParams, $rootScope, Address) {
Address.getAll($rootScope.user.id)
.success( function(data) {
console.log(data);
$scope.addresses = data.results;
})
}]);
In my Angular template, the view does return Address objects. But it returns all the Address objects when it should only be returning the Address objects with a corresponding userId. To clarify, the Address class has a userId pointer column. Each address only has one User.
Here is the log message that AddrCtrl returns in the console:
Object {results: Array[2]}
results: Array[2]
0: Object
firstName: "(test)"
lastName: "test"
// more unrelated properties
objectId: "yUEuFjLlzs"
updatedAt: "2014-12-02T20:17:55.608Z"
userId: Object
__type: "Pointer"
className: "_User"
objectId: "q1KADkp4i1"
I'm assuming that the issue lies somewhere in my $http.get() function. Ultimately, my questions is this: why does my params option not constrain my data.results to just the Address objects associated with one Parse.User?
Answer I am not looking for:
Return all Address objects and only save the ones matching Parse.User.current().id into $scope.
You need to use where clause to perform the query.
If the data type of userId is Pointer, you should write as following:
{"where": JSON.stringify({
"userId": {"__type":"Pointer","className":"_User","objectId":"userId"}}
)}

Ember.js: Where does this method go?

Coming to Ember from Rails, one of the places I'm struggling is trying to figure out Ember's definitions of models, views, and controllers.
I'm just testing out some sample Ember code. I'm getting my user events via the GitHub API, and I want to change the type name into something readable.
I have a jsbin here, but here's the gist:
App = Ember.Application.create();
App.IndexRoute = Ember.Route.extend({
model: function(){
return Ember.$.getJSON('https://api.github.com/users/thenickcox/events').then(function(data){
return data.splice(0,7);
});
}
});
I have a method that types a type and returns a string:
interpretType: function(type){
if (type === 'PushEvent') {
return 'Pushed';
}
return name;
}
In Rails, this would go on the model. But the only model here is the one that Ember created in memory by default (right?). So then I thought, it's something that each member of the array needs, because here's the view:
<h3> Some events</h3>
<ul>
{{#each}}
<li>I {{interpretType(type)}} to {{repo.name}}</li>
{{/each}}
</ul>
So is that something that goes on Ember.ArrayController? I tried that, like this:
App.IndexController = Ember.ArrayController.extend({
interpretType: function(type){
if (type === 'PushEvent') {
return 'Pushed';
}
return name;
}.property()
});
That just gave me an error. Where do I put this?
PS. So you don't have to look at the GitHub API, here's an example JSON object:
{
id: "1890853674",
type: "CreateEvent",
actor: {
id: 702327,
login: "thenickcox",
gravatar_id: "63f35d9e50dfd73281126b051a51668a",
url: "https://api.github.com/users/thenickcox",
avatar_url: "https://2.gravatar.com/avatar/63f35d9e50dfd73281126b051a51668a?d=https%3A%2F%2Fa248.e.akamai.net%2Fassets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png&r=x"
},
repo: {
id: 14463966,
name: "thenickcox/whiskey_taster",
url: "https://api.github.com/repos/thenickcox/whiskey_taster"
},
payload: {
ref: "master",
ref_type: "branch",
master_branch: "master",
description: "My first ember/rails app"
},
public: true,
created_at: "2013-11-17T09:00:17Z"
},
Here is an updated JSBin
Basically, the each can specify an itemController to decorate the model.
App.EventController = Ember.ObjectController.extend({
interpretType: function(){
var type = this.get('model.type');
if (type === 'PushEvent') {
type = 'Pushed';
}
return type;
}.property('model.type')
});
Handlebars doesn't have functions as you've written it, but since we are now using the event controller which wraps the single model, we just refer to interpretType to do the translation:
{{#each itemController='event'}}
<li>{{interpretType}} to {{repo.name}}</li>
{{/each}}
Put it inside an Ember.ObjectController
ArrayController's deal with methods related to the collection of data from the model, whereas ObjectController deals with methods related to the specific object.
I'm also learning Ember from a Rails background.
If you haven't already come across this, you will definetely want to check out ember-tools, it's a command line generator very similar to what we've got in rails. I cant imagine building an Ember app without something like it..

How to use parameters within the filter in AngularJS?

I want to use parameter in filter, when I iterate some arrays with ng-repeat
Example:
HTML-Part:
<tr ng-repeat="user in users | filter:isActive">
JavaScript-part:
$scope.isActive = function(user) {
return user.active === "1";
};
But I want to be able to use filter like
<tr ng-repeat="user in users | filter:isStatus('4')">
But its not working. How can I do something like that?
UPDATE: I guess I didn't really look at the documentation well enough but you can definitely use the filter filter with this syntax (see this fiddle) to filter by a property on the objects:
<tr ng-repeat="user in users | filter:{status:4}">
Here's my original answer in case it helps someone:
Using the filter filter you won't be able to pass in a parameter but there are at least two things you can do.
1) Set the data you want to filter by in a scope variable and reference that in your filter function like this fiddle.
JavaScript:
$scope.status = 1;
$scope.users = [{name: 'first user', status: 1},
{name: 'second user', status: 2},
{name: 'third user', status: 3}];
$scope.isStatus = function(user){
return (user.status == $scope.status);
};
Html:
<li ng-repeat="user in users | filter:isStatus">
OR
2) Create a new filter that takes in a parameter like this fiddle.
JavaScript:
var myApp = angular.module('myApp', []);
myApp.filter('isStatus', function() {
return function(input, status) {
var out = [];
for (var i = 0; i < input.length; i++){
if(input[i].status == status)
out.push(input[i]);
}
return out;
};
});
Html:
<li ng-repeat="user in users | isStatus:3">
Note this filter assumes there is a status property in the objects in the array which might make it less reusable but this is just an example. You can read this for more info on creating filters.
This question is almost identical to Passing arguments to angularjs filters, to which I already gave an answer. But I'm gonna post one more answer here just so that people see it.
Actually there is another (maybe better solution) where you can use the angular's native 'filter' filter and still pass arguments to your custom filter.
Consider the following code:
<li ng-repeat="user in users | filter:byStatusId(3)">
<span>{{user.name}}</span>
<li>
To make this work you just define your filter as the following:
$scope.byStatusId = function(statusId) {
return function(user) {
return user.status.id == statusId;
}
}
This approach is more versatile because you can do comparisons on values that are nested deep inside the object.
Checkout Reverse polarity of an angular.js filter to see how you can use this for other useful operations with filter.
If you have created an AngularJs custom filter, you can send multiple params to your filter.Here is usage in template
{{ variable | myFilter:arg1:arg2... }}
and if you use filter inside your controller here is how you can do that
angular.module('MyModule').controller('MyCtrl',function($scope, $filter){
$filter('MyFilter')(arg1, arg2, ...);
})
if you need more with examples and online demo, you can use this
AngularJs filters examples and demo
This may be slightly irrelevant, but if you're trying to apply multiple filters with custom functions, you should look into:
https://github.com/tak215/angular-filter-manager
Example I have a students list as below :
$scope.students = [
{ name: 'Hai', age: 25, gender: 'boy' },
{ name: 'Hai', age: 30, gender: 'girl' },
{ name: 'Ho', age: 25, gender: 'boy' },
{ name: 'Hoan', age: 40, gender: 'girl' },
{ name: 'Hieu', age: 25, gender: 'boy' }
];
I want to filter students via gender to be boy and filter by name of them.
The first I create a function named "filterbyboy" as following:
$scope.filterbyboy = function (genderstr) {
if ((typeof $scope.search === 'undefined')||($scope.search === ''))
return (genderstr = "")
else
return (genderstr = "boy");
};
Explaination: if not filter name then display all students else filter by input name and gender as 'boy'
Here is full HTMLcode and demo How to use and operator in AngularJs example

Categories