Angular 1.6 bindings inside controller - javascript

Im trying to pass some parameters to my component through bindings, but unfortunately I'm not having luck in using those params in my controller, this is my code:
angular.module('project1').component('menu', {
templateUrl: '/static/js/templates/menu.template.html',
bindings: {
rid: '#'
},
controller: ['Restaurant', function RestaurantListController(Restaurant) {
console.log(this.rid);
console.log(this);
this.restaurant = Restaurant.get({restaurantId: this.rid});
}]
});
HTML component:
<menu rid="1"></menu>
The interesting thing is that i can access the parameters in the template and when i do the 2 console log, the first one is undefined, but in the second one i can see the rid variable...so, i really don't understand what i'm missing.

With angular 1.6, your bindings are going to be ready on the method $onInit and not before.
If you need to re-enable auto bindings
https://toddmotto.com/angular-1-6-is-here#re-enabling-auto-bindings

If anyone still searching for solution, use $onInit method provided by angular.
this.$onInit = function () {
$http.get(`/api/v1/projects`).then((res) => {
$scope.projects = res.data;
}, (err) => {
$scope.error = err
})
};

Related

Configure ui-router with components containing multiple bindings

I am trying to find a better solution to use the ui-router together with angular components.
Consider two simple components:
app.component('componentOne', {
template: '<h1>My name is {{$ctrl.name}}, I am {{$ctrl.age}} years old.</h1>',
bindings : {
name : '#',
age : '#'
}
}
);
app.component('componentTwo', {
template: '<h1>I am component 2</h1>'
});
Right now, I am specifying the component and its parameter using the template property:
app.config(function($stateProvider, $urlRouterProvider ){
$stateProvider
.state('component1', {
url: "/component1",
template: "<component-one name=\"foo\" age=\"40\"></component-one>"
})
.state('component2', {
url: "/component2",
template: "<component-two></component-two>"
})
});
While this is working fine, I have components with arround ten bindings which makes the configuration of the ui-router realy awkward.
I tried using the component property but this doesn't work for me at all. The only other solution I found is to specify the parent using the require property and omit the bindings - but this doesn't feel right for me... Is there a better way to do this?
Here is a plnkr.
UI-Router component: routing exists in UI-Router 1.0+ (currently at 1.0.0-beta.1)
Here's an updated plunker: https://plnkr.co/edit/VwhnAvE7uNnvCkrrZ72z?p=preview
Bind static values
To bind static data to a component, use component and a resolve block which returns static data.
$stateProvider.state('component1', {
url: "/component1",
component: 'componentOne',
resolve: { name: () => 'foo', age: () => 40 }
})
Bind async values
To bind async values, use a resolve which returns promises for data. Note that one resolve can depend on a different resolve:
$stateProvider.state('component1Async', {
url: "/component1Async",
component: "componentOne",
resolve: {
data: ($http) => $http.get('asyncFooData.json').then(resp => resp.data),
name: (data) => data.name,
age: (data) => data.age
}
});
Bind lots of values
You mention you have 10 bindings on a component. Depending on the structure of the data you're binding, you can use JavaScript to construct the resolve block (it's "just javascript" after all)
var component2State = {
name: 'component2',
url: '/component2',
component: 'componentTwo',
resolve: {
data: ($http) => $http.get('asyncBarData.json').then(resp => resp.data)
}
}
function addResolve(key) {
component2State.resolve[key] = ((data) => data[key]);
}
['foo', 'bar', 'baz', 'qux', 'quux'].forEach(addResolve);
$stateProvider.state(component2State);
Alternatively, you can move your bindings a level down and create an object which will be the only bindings. If 10 bindings is what is bothering you.
One alternative you can try is to override the template by custom properties of states in $stateChangeStart event.
Run block like this to achieve this kind of behaviour.
app.run(function($rootScope){
//listen to $stateChangeStart
$rootScope.$on("$stateChangeStart",function(event, toState, toParams, fromState, fromParams, options){
//if the component attribute is set, override the template
if(toState.component){
//create element based on the component name
var ele = angular.element(document.createElement(camelCaseToDash(toState.component)));
//if there is any binding, add them to the element's attributes.
if(toState.componentBindings){
angular.forEach(toState.componentBindings,function(value,key){
ele.attr(key,value)
})
}
//you may also do something like getting bindings from toParams here
//override the template of state
toState.template = ele[0].outerHTML;
}
})
//convert camel case string to dash case
function camelCaseToDash(name) {
return name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
})
And with this now you can have component property in your state config.
app.config(function($stateProvider, $urlRouterProvider ){
$stateProvider
.state('component1', {
url: "/component1",
component:"componentOne",
componentBindings:{
name:"foo",
age:"40",
}
})
.state('component2', {
url: "/component2",
component:"componentTwo",
})
});
Here is the working plunker.
Still you may have a large config function, but it will look not so awkward.

Update ng-repeat after ng-resource call is finished

I am using ngResource in combination with ng-repeat and noticed that slow REST calls doesn't update the list properly. It keeps empty.
As far as I understood I need a binding between controller and ng-repeat element.
My resource and controller definition:
(function (configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource, REST_CONFIG) {
return {
Technical: $resource(REST_CONFIG.baseUrl + REST_CONFIG.path + '/', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function (configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
loggingResource.Technical.query(function (data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
ng.repeat usage:
<tr class="table-row" ng-repeat="log in logController.logs">
What I have tried so far:
ng-bind in combination with ng-repeat
$q with deferrer
$promise of ngResource
What did I miss?
My try to get it on plnkr: https://plnkr.co/edit/t1c5Pxi7pzgocDMDNITX
The {{}} provides a default binding between your controller and view and you don't need to add anything explicitly. I have updated your plunkr with some minor changes to injected constants etc. and it is working.
// Code goes here
angular.module("loggingModule", ['ngResource']);
(function(configure) {
configure('loggingResource', loggingResource);
function loggingResource($resource) {
return {
Technical: $resource('https://api.github.com/users/addi90/repos', {}, {
query: {
method: 'GET',
isArray: true
}
})
};
}
})(angular.module('loggingModule').factory);
(function(configure) {
configure('logController', logController);
function logController(loggingResource) {
var that = this;
that.logs = [{
title: 'test2'
}];
loggingResource.Technical.query(function(data) {
that.logs = data;
});
//that.logs = loggingResource.Technical.query();
}
})(angular.module('loggingModule').controller);
Since the api resource was not working, I have used my github repo api link there
The updated working plunkr is available here: https://plnkr.co/edit/cederzcAGCPVzc5xTeac?p=preview
Try to use
that.logs.push(data)
so you prevent to reinitialise the logs list. If you override the logs list and the ng-repeat is initialised before the logs list it seems to not be resolved.
Your logs list is set correctly after the rest call?

$rootScope.$emit and $on

I'm developing a big app with Angular, and I have the need to launch an event with $rootscope.$emit, and listen to this event inside a factory. I can't do that inside a controller because, when I init the User, the controller hasn't loaded yet.
The code works, but I want to know if this is the best way to do that.
Factory one, which emit the event:
angular
.module('app.core')
.factory('AuthenticationService', ['$rootScope', 'requestHttpService', AuthenticationService])
function AuthenticationService($rootScope, requestHttpService) {
var isLoggedIn = false;
return {
logIn: logIn
}
function logIn(action) {
return requestHttpService.runOpenRequest(action)
.then(logInComplete)
.catch(logInError);
function logInComplete(userData) {
$rootScope.$emit('initUser', userData);
}
function logInError(error) {
console.log('Something went wrong: ' + error);
}
}
};
And factory two, which listen to the event:
angular
.module('app.user')
.factory('manageUser', ['$rootScope', manageUserFactory]);
function manageUserFactory($rootScope) {
var user = {'id': '', 'name': ''};
$rootScope.$on('initUser', function (event, userData) {
initUser(userData);
});
return {
initUser: initUser
}
function initUser(userData) {
user = {'id': userData.id, 'name': userData.name};
}
};
I will suggest not to use events here.
As you are using $emit and $emit moves upward in the scope hierarchy including the current scope on which it is emitted and here in your example $rootScope is already at the top. So it will be used only to notify events binded to $rootScope .
I will suggest to call the factory method directly by injecting it as below:
replace $rootScope.$emit('initUser', userData);
with manageUser.initUser(); by injecting manageUser faactory in AuthenticationService.
Events are least preferred because of their performace cost.

Angular Meteor $reactive vs getReactively

I'm learning Angular Meteor and I have a question:
What is the difference between using $reactive and getReactively?
If you take a look at the API reference you get this for $reactive (http://www.angular-meteor.com/api/1.3.2/reactive):
A service that takes care of the reactivity of your Meteor data, and updates your AngularJS code.
This service wraps context (can be used with this or $scope) - so you can use it with any context as you wish
And this for getReactively (http://www.angular-meteor.com/api/1.3.2/get-reactively):
Use this method to get a context variable and watch it reactively, so each change of this variable causes the dependents (autorun, helper functions and subscriptions) to run again.
The getReactively method is part of the ReactiveContext, and available on every context and $scope.
As far as I could understand $reactive will make everything reactive ('this', '$scope' and son on as long as you apply it to them) and getReactively will make only that particular variable, or object reactive.
So if I make this:
controller: function ($scope, $reactive) {
var vm = $reactive(this).attach($scope);
vm.sort = {
name: 1
};
this.helpers({
parties: () => {
return Parties.find({}, { sort : vm.sort });
}
});
this.subscribe('parties', () => {
return [{
sort: vm.sort
},
this.getReactively('searchText')
]
});
});
Why don't I get the same result as if I was doing this:
controller: function ($scope, $reactive) {
var vm = $reactive(this).attach($scope);
vm.sort = {
name: 1
};
this.helpers({
parties: () => {
return Parties.find({}, { sort : this.getReactively('sort') });
}
});
this.subscribe('parties', () => {
return [{
sort: this.getReactively('sort')
},
this.getReactively('searchText')
]
});
});
If $reactive takes care of reactivity I was expecting to see anything inside this and $scope to be reactive and, like in getReactively, whenever something is changed, to cause its dependents to run again.
So: what am I missing?

Angular Directives performance issue

I have a .NET webservice that returns an object like:
myObj = {
prop1: value,
prop2:value,
...
prop5:value
}
I created an angular service that returns this entire object(myObj).
I created 5 distinct directives to display these properties in different pages in the application(sometimes, some of them can be in the same page).
I'm calling the angular service in these directives, creating for any of them this "link" function:
link: function (scope, element, attrs) {
getService.getMethod().$promise.then(
function (myObj) {
element.text(myObj.prop1); // .prop2, ... , prop5
},
function (statusCode) {
console.log(statusCode);
}
);
}
I have the feeling that my approach is not the best, calling five times the angular service(through the $promise) obtaining actually the same object(myObj).
If you are interested also how the service is looking:
var localResource = $resource('https://.....',
{},
{'getAll': {method: 'JSONP', isArray: false, params: {callback: 'JSON_CALLBACK'}}}
);
return {
getMethod: function () {
return localResource.getAll();
}
}
Please help, if someone has an ideea haw can I improve it.
Thank you!
It sounds like you can make your getMethod issue only one server call.
.factory("getService", function () {
var getAll;
return {
getMethod: function () {
if (!getAll) {
getAll = localResource.getAll();
}
return getAll;
};
};
});

Categories