I got a component which binds a parent function. I know I have to use non-primitive values as arguments but I still get undefined. What's going on ? Here's a sample code that demonstrates my problem.
Component:
app.component('testComponent', {
template:'<button ng-click="$ctrl.hasStatus({val:700})">Test</button>',
bindings:{
hasStatus:'&'
},
controller:function() {
var ctrl = this;
}
})
Parent:
<test-component has-status='hasStatus(statusObj)'></test-component>
and in the controller:
$scope.hasStatus = function(obj) {
console.log(obj) // undefined
}
And the plunker
As your has-status attribute function has hasStatus(statusObj), where statusObj is parameter of object. That's why you should pass status statusObj property mentioned in JSON, while passing parameter to hasStatus method.
ng-click="$ctrl.hasStatus({statusObj: { val: 700}})"
Demo Plunker
Related
I am using angular file uploader in child component and need to access the fileitem when onAfterAddingFile is fired. I have implemented binding in component. So far, I have tried this-
Childcontroller:
$onInit() {
this.feedFileInfo = 'abc';//here it is updated
this.uploader.filters.push({
name: 'customFilter',
fn: function(item /*{File|FileLikeObject}*/, options) {
return this.queue.length < 10;
}
});
this.uploader.onAfterAddingFile = function(fileItem) {
console.log('fileItem');
this.feedFileInfo = 'xyz';//this value is not being updated to feedFileInfo variable and hence cannot be obtained in parent controller
console.info('onAfterAddingFile', fileItem);
};
I need the updated value ie. fileitem in this variable.
Any guidance would be appreciated.
You use the this from the declared function, not the outer one.
One classical workaround is to use:
var that = this;
this.uploader.onAfterAddingFile = function (fileItem) {
that.feedFileInfo = 'xyz';
};
Receiving error cannot read property 'type' of undefined at PollListCtrl.runQuery.
I'm not sure why this is coming up undefined. I've console logged profile-polls.controller.js where listConfig is created to make sure there is an object here. The response is
{type: "all", filters: {pollsCreated: Array(3)}}
But it is coming up undefined when I try to pass it to poll-list component in the profile-polls.html. Below is the gist for the relevant files. Can anyone tell me why this is not passing correctly?
https://gist.github.com/RawleJuglal/fa668a60e88b6f7a95b456858cf20597
I think you need to define watcher for your component. On start listConfig is undefined and only after some delay (next digest cycle) it gets value. So we create watcher and cancel it after if statement.
app.component('pollList', {
bindings: {
listConfig: '='
},
templateUrl: 'poll-list.html',
controllerAs: 'vm',
controller: PollListCtrl
});
function PollListCtrl($scope) {
var vm = this;
function run(){
console.log(vm.listConfig);
}
var cancelWatch = $scope.$watch(function () {
return vm.listConfig;
},
function (newVal, oldVal) {
if (newVal !== undefined){
run();
cancelWatch();
}
});
}
Demo plunkr
You should never use $scope in your angularjs application.
You can use ngOnInit() life cycle hook to access "bindings" value instead constructor.
ngOnInit(){
$ctrl.listConfig = { type: 'all' };
$ctrl.limit = 5;
}
I am having no joy with implementing require: {} property as part of an angular component. Allow me to demonstrate with an example I have.
This is the component/directive that supposed to fetch a list of judgements. Nothing very fancy, just a simple factory call.
// judgements.component.js
function JudgementsController(GetJudgements) {
var ctrl = this;
ctrl.Get = function () {
GetJudgements.get().$promise.then(
function (data) {
ctrl.Judgements = data.judgements;
}, function (error) {
// show error message
});
}
ctrl.$onInit = function () {
ctrl.Get();
};
}
angular
.module('App')
//.component('cJudgements', {
// controller: JudgementsController,
//});
.directive('cJudgements', function () {
return {
scope: true,
controller: 'JudgementsController',
//bindToController: true,
};
});
I am trying to implement component require property to give me access to ctrl.Judgements from the above component/directive as follows:
// list.component.js
function ListController(GetList, GetJudgements) {
var ctrl = this;
ctrl.list = [];
ctrl.Get = function () {
GetList.get().$promise.then(
function (data) {
ctrl.list = data.list;
}, function (error) {
// show error message
});
};
//ctrl.GetJudgements = function () {
// GetJudgements.get().$promise.then(
// function (data) {
// ctrl.Judgements = data.judgements;
// }, function (error) {
// // show error message
// });
//}
ctrl.$onInit = function () {
ctrl.Get();
//ctrl.GetJudgements();
};
}
angular
.module('App')
.component('cTheList', {
bindings: {
listid: '<',
},
controller: ListController,
controllerAs: 'ctrl',
require: {
jCtrl: 'cJudgements',
},
template: `
<c-list-item ng-repeat="item in ctrl.list"
item="item"
judgements="ctrl.Judgements"></c-list-item>
<!--
obviously the reference to judgements here needs to change
or even better to be moved into require of cListItem component
-->
`,
});
Nice and simple no magic involved. A keen reader probably noticed GetJudgement service call in the ListController. This is what I am trying to remove from TheList component using require property.
The reason? Is actually simple. I want to stop database being hammered by Judgement requests as much as possible. It's a static list and there is really no need to request it more than once per instance of the app.
So far I have only been successful with receiving the following error message:
Error: $compile:ctreq
Missing Required Controller
Controller 'cJudgements', required by directive 'cTheList', can't be found!
Can anyone see what I am doing wrong?
PS: I am using angular 1.5
PSS: I do not mind which way cJudgement is implemented (directive or component).
PSSS: If someone wonders I have tried using jCtrl: '^cJudgements'.
PSSSS: And multiple ^s for that matter just in case.
Edit
#Kindzoku posted a link to the article that I have read before posting the question. I hope this also helps someone in understanding $onInit and require in Angular 1.5+.
Plunker
Due to popular demand I made a plunker example.
You should use required components in this.$onInit = function(){}
Here is a good article https://toddmotto.com/on-init-require-object-syntax-angular-component/
The $onInit in your case should be written like this:
ctrl.$onInit = function () {
ctrl.jCtrl.Get();
};
#iiminov has the right answer. No parent HTML c-judgements was defined.
Working plunker.
I want to create a new Angular compontent, which should change the status of an object/model and then call the defined callback function.
The code looks like this:
HTML
<status-circle status="obj.status" on-update="save(obj)"></status-circle>
statusCirlcle.js
angular.module('app').component('statusCircle', {
templateUrl: '/views/components/statusCircle.html',
bindings: {
status: '=',
onUpdate: '&'
},
controller: function () {
this.update = function (status) {
if(this.status !== status) {
this.status = status;
this.onUpdate();
}
};
}
});
The property will be updated correctly (two-way binding works) and the save callback function gets called, BUT the object has the old status property set.
My question: How can I update the object in the callback function?
JSFiddle: https://jsfiddle.net/h26qv49j/2/
It looks like in statusCircle.js you should change
this.onUpdate();
to
this.onUpdate({status: status});
and then in the HTML change
<status-circle status="obj.status" on-update="save(obj)"></status-circle>
To
<status-circle status="obj.status" on-update="save(status)"></status-circle>
And lastly... in your controller where your save() function is put
this.save = function(status) {this.obj.status = status};
Is there a way to pass parameters to a state without them being represented in the url?
Let's say I have the following state:
.state('accounts.content.viewaccounts.details.contacts.edit', {
url:'/edit/{contactId:[0-9]{1,10}}',
templateUrl: 'app/common/templates/StandardForm.html',
resolve: {blahblah}
Is there a way for me to send an optional parameter to this state, when navigating to it with $state.go, without adding it into the url?
You can use shared properties service and pass elements between controllers:
.service('sharedProperties', function () {
var item = null;
return {
getItem: function () {
return item;
},
setItem: function(value) {
item = value;
}
}});
Then before you move to controller you use:
sharedProperties.setItem(your-item);
and after you load the new controller you use:
var model = sharedProperties.getItem();
No. Not without using something like a service. see #Liad's answer