I have created a component in angular js which shows some data
But on change of my customer binding i want to call some service in component controller which is not reflecting,Here is the below code
In my Test1.html
<tab-customer title="Test Comp" customerCode="vm.customerCode" functioname="fnTest"></tab-customer>
vm.CustomerCode is changing on basis of dropdown so its changing
app.comp.TestComp is already defined
angular
.module('app.comp.TestComp')
.component('tabCustomer', {
templateUrl: 'Component.html',
controller: tabCompCustomer,
controllerAs: 'vm',
bindings: {
title: '#',
functioname: '#',
customerCode: '<'
}
});
function tabCompCustomer(tableCustomerService, $rootScope,$scope, $filter) {
var vm = this;
$scope.$watch('vm.customerCode', function (newValue, oldValue) {
console.log('got1')
console.log(newValue) // This is also not working
});
$onChanges = function (changes) {
console.log('got')
console.log(changes) // This is also not working
};
};
On Change of a Customer Code i need to fire my component again but i am not getting any fire event can anyone help
If the question is still unclear comment i will detailed it
You should be changing your attribute binding from customerCode to customer-code, So that will pass vm.customerCode to component. And remove $watch function from component controller, as $onChanges function will get fire automatically on each bindings change.
customerCode="vm.customerCode"
should be
customer-code="vm.customerCode"
Note: make $onChanges = to this.$onChanges =
Related
I am trying to create a reusable component which can be reused accross the application. We are using Angular 1.5.8
There is some data that needs to be passed from the parent component to child component. (Typically an object holding information). It can be
After some reading i found out there is attribute called require where you can mention the name of the parent component and then can access the methods of parent controller.
The main drawback is the parent component name is hardcoded. And that limits the reusability of the component.
Is there anyway where we can pass data from parent component to child in dynamic way.
Code sample
app.component('parent',
{ restrict: 'E',
scope: {},
templateUrl: 'app/parent.html',
controller: function(){
var vm = this;
vm.sayHello = function (){
return {
parentName : 'parent1',
parentCode : 'parentCode1'
};
};
},
controllerAs: 'vm'});
app.component('child', {
require: {
parentCtrl: '^^parent'
},
controller: function() {
var self = this;
this.$onInit = function() {
self.parentCtrl.sayHello();
};
}
});
Thanks
add bindings to child component in the definition object:
app.component('child',{
bindings:{
data: '<'
},
templateUrl: 'app/child.html',
controller: childController});
then in parent.html you use the following:
Plunkr here : https://plnkr.co/edit/d6wS1dHVsYT3fNkMUVNY?p=preview
angular.noop is just an empty function so you can put if you do not have any controller for the component
$ctrl is default if you do not specify controllerAs alias name
Also you can use on child component $onInit() $onChanges() and $onDestroy() lifecycle hooks to control what the component will do at certain points.
If you use .component drop the restrict: 'E' is already an element
I will explain what exactly I'm trying to do before explaining the issue. I have a Directive which holds a form, and I need to access that form from the parent element (where the Directive is used) when clicking on a submit button to check fi the form is valid.
To do this, I am trying to use $scope.$parent[$attrs.directiveName] = this; and then binding some methods to the the Directive such as this.isValid which will be exposed and executable in the parent.
This works fine when running locally, but when minifying and building my code (Yeoman angular-fullstack) I will get an error for aProvider being unknown which I traced back to a $scopeProvider error in the Controller.
I've had similar issues in the past, and my first thought was that I need to specifically say $inject for $scope so that the name isn't lost. But alas.....no luck.
Is something glaringly obvious that I am doing wrong?
Any help appreciated.
(function() {
'use strict';
angular
.module('myApp')
.directive('formDirective', formDirective);
function formDirective() {
var directive = {
templateUrl: 'path/to/template.html',
restrict: 'EA',
scope: {
user: '='
},
controller: controller
};
return directive;
controller.$inject = ['$scope', '$attrs', 'myService'];
function controller($scope, $attrs, myService) {
$scope.myService = myService;
// Exposes the Directive Controller on the parent Scope with name Directive's name
$scope.$parent[$attrs.directiveName] = this;
this.isValid = function() {
return $scope.myForm.$valid;
};
this.setDirty = function() {
Object.keys($scope.myForm).forEach(function(key) {
if (!key.match(/\$/)) {
$scope.myForm[key].$setDirty();
$scope.myForm[key].$setTouched();
}
});
$scope.myForm.$setDirty();
};
}
}
})();
Change the directive to a component and implement a clear interface.
Parent Container (parent.html):
<form-component some-input="importantInfo" on-update="someFunction(data)">
</form-component>
Parent controller (parent.js):
//...
$scope.importantInfo = {data: 'data...'};
$scope.someFunction = function (data) {
//do stuff with the data
}
//..
form-component.js:
angular.module('app')
.component('formComponent', {
template:'<template-etc>',
controller: Controller,
controllerAs: 'ctrl',
bindings: {
onUpdate: '&',
someInput: '<'
}
});
function Controller() {
var ctrl = this;
ctrl.someFormThing = function (value) {
ctrl.onUpdate({data: value})
}
}
So if an event in your form triggers the function ctrl.someFormThing(data). This can be passed up to the parent by calling ctrl.onUpdate().
I am new to angularjs. I am trying angular 1.5 nested component. Can I bind parent component property in child component.
Ex:
<div ng-app='cbsApp' ng-controller='cbsCnt as ct'>
<cbs-cus-comp com-bind='ct.name'>
<child child-com-bind='cbsCusCompCntAs.name'></child>
</cbs-cus-comp>
</div>
I can get ct.name value in com-bind. But can't get cbsCusCompCntAs.name in child-com-bind. (cbsCusCompCntAs is cbs-cus-comp controller)
Working Plunker : https://plnkr.co/edit/axQwTn?p=preview
Thanks in advance.
In your first case you are referring directly to the controller scope via controllerAs.
When using components in angular 1.5 you can get hold of your parent component via require which will make parent's properties available after $onInit as per Components Documentation:
Note that the required controllers will not be available during the
instantiation of the controller, but they are guaranteed to be
available just before the $onInit method is executed!
In your specific case you can update the child component to require the parent:
var child = {
require : {parentComp:'^cbsCusComp'},
template : 'Child : <b{{cbsCusChildCompCntAs.childComBind}}</b>',
controller : cbsCusChildCompCnt,
controllerAs: 'cbsCusChildCompCntAs'
};
and its controller to get the data you need (I used the same names as you just to see it work):
function cbsCusChildCompCnt(){
this.$onInit = function() {
this.childComBind = this.parentComp.name;
};
}
Updated plunker is here.
Wow... what a wonderful example...
Took me a while to analyse it... so, I wrote my own (I think a bit more readable) version.
I really do not know how to work with Plunker... so here's the code...
Extract from my index.html file
<div ng-controller='appCtrl as ctrl'>
<parent bind-id='ctrl.name'>
<child bind-toid='parentCtrlAs.name'></child>
</parent>
</div>
The .js file
(function () {
'use strict';
var
parentComponent =
{
bindings :
{
bindId:'='
},
controller : parentCtrl,
controllerAs: 'parentCtrlAs',
restrict : 'A',
transclude : true,
templateUrl : 'parent.html',
};
var
childComponent =
{
controller : childCtrl,
controllerAs: 'childCtrlAs',
restrict : 'A',
require :
{
myParent :'^parent'
},
templateUrl : 'child.html',
};
angular
.module('app', [])
.controller('appCtrl' , appCtrl)
.component('parent' , parentComponent)
.component('child' , childComponent);
function appCtrl(){
this.name = 'Main..';
}
function childCtrl(){
this.$onInit = function() {
this.bindToid = this.myParent.name;
};
}
function parentCtrl(){
this.name = 'Parent Component';
}
})();
Hope it helps,
Regards,
Johnny
Although using the "require" parameter works, it creates a tightly bound relationship between the component acting as a child, which uses the "require" parameter, and the component acting as a parent, which consumes the child functionality.
A better solution is to use component communication as shown here.
Basically, you define a binding function in the child component definition, like so,
angular.module('app').component('componentName', {
templateUrl: 'my-template.html',
bindings: {
myFunction: '&'
},
controller: function() { // Do something here}
});
Then, in the parent markup you provide a function to call,
Parent HTML
<user-list select-user="$ctrl.selectUser(user)">
</user-list>
Finally, in the parent controller, provide an implementation of the selectUser function.
Here's a working Plunk.
I'm having a problem with a new state I added to our web site.
Short description
I have a state (/page)'q.explorer' which contains a button; when clicked it should go to a child state (named 'q.explorer.detail') but it does not. However: in the logging I see that it does try to go to that (/state) and the new url is formatted as defined in the child state.
But still the template and controller that are actually used is the 'parent' which contains the button ...
This may be a little confusing to explain; so I have also added some code in the hope that this will clarify my problem.
The setup looks like this:
$stateProvider
.state('q', {
url: '/:locale/app',
data : { ui: "V2" },
views: {
'application' : {template: '<div ui-view=""><page-home-v2></page-home-v2></div>' }
}, controller: function($scope, $stateParams, siteNavigation) {
siteNavigation.applyUrlParameters($stateParams);
}
}).state('q.explorer', {
url: '/explorer?:year&:month&:guide',
template: '<page-explorer-v2></page-explorer-v2>',
controller: function($scope, $stateParams, siteNavigation) {
console.log("controller: qlaro.explorer");
siteNavigation.applyUrlParameters($stateParams);
}
}).state('q.explorer.detail', {
url: '/detail',
template: '<page-explorer-detail-v2></page-explorer-detail-v2>',
controller: function($scope, $stateParams, siteNavigation) {
console.log("controller: qlaro.explorer.detail");
siteNavigation.applyUrlParameters($stateParams);
}
})
angular
.module('q.components')
.service('siteNavigation', function($state, $location) {
var service = this;
service.applyUrlParameters = function($stateParams) {
if (!$stateParams) $stateParams = $state.params;
console.log('Apply state parameters for state: ' + $state.current.name);
console.log('URL >> ' + $location.url());
};
};
Somewhere deep in the template of "q.explorer" there is a button to open the detail view ("q.explorer.detail"). It uses this code:
function goToDetail() {
var ui = $state.current.data.ui;
if (ui === "V1") { /*...*/ }
else if (ui === "V2") {
var params = {
year: Store.getYear(),
month: Store.getMonth(),
locale: Store.getLocale()
}
var guide = Store.getActiveSidebarItem();
if (guide) params.guide = guide.slug;
console.log("go to explorer: " + params.guide);
$state.go('q.explorer.detail', params);
}
else console.log("Unable to go to state because the ui version is not known.");
}
And this is what I see in the console after clicking the link:
go to explorer: ebit
controller: q.explorer
Apply state parameters for state: q.explorer.detail
URL >> /nl/app/explorer/detail?year=2015&month=11&guide=ebit
As you can see, it uses the controller of the 'parent' iso the child page I want to open. Even though $state.current.name is correct ... Or maybe I should say it does not change from state ...
Any help is welcome.
(PS: We are using Angular 1.4.9)
Looks like you are using nested states such that q.explorer.detail is a child of q.explorer. To render the child state's template, you also need a specific ui-view where it can be placed into. And this will be searched in the template of the parent state. Getting the console.log() output just means the controllers are instantiated, but that even happens if the template isn't rendered at all.
So check if you have an ui-view in the template of the q.explorer state. For more details, please see: https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views
You could also fix this by not making q.explorer.detail a child of q.explorer. A child state is created as soon as you need the dot notation.
Yoy have to add somewhere in 'q.explorer' state's template entry point for nested view 'q.explorer.detail', otherwise child controller will not be called.
For example:
template: '<page-explorer-v2></page-explorer-v2><ui-view></ui-view>',
instead of
template: '<page-explorer-v2></page-explorer-v2>'
See jsfiddle: http://jsfiddle.net/jcpmsuxj/42/
Upd. As #ajaegle mentioned you should to visit official docs page:
https://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views
I am struggling with data binding in AngularJs.
I have the following piece of markup in .html file that includes the custom directive:
<my-directive ng-repeat="i in object" attr-1="{{i.some_variable}}"></my-directive>
Note: 'some-variable' is being updated every 10 seconds(based on the associate collection and passed to template through controller).
The directive's code includes:
myApp.directive('myDirective', function () {
scope: {
'attr-1': '=attr1'
which throws this exception because of the brackets in attr-1(see html code above).
It works though if I use read-only access(note at sign below):
myApp.directive('myDirective', function () {
scope: {
'attr-1': '#attr1'
I use scope.attr-1 in directive's HTML to show its value.
The problem is that with read-only access UI is not reflecting the change in attribute change.
I've found solution with $parse or $eval(couldn't make them work tho). Is there a better one there?
You'll need only two-way binding and I think $parse or $eval is not needed.
Please have a look at the demo below or in this fiddle.
It uses $interval to simulate your updating but the update can also come from other sources e.g. web socket or ajax request.
I'm using controllerAs and bindToController syntax (AngularJs version 1.4 or newer required) but the same is also possible with just an isolated scope. See guide in angular docs.
The $watch in the controller of the directive is only to show how the directive can detect that the data have changed.
angular.module('demoApp', [])
.controller('MainController', MainController)
.directive('myDirective', myDirective);
function MainController($interval) {
var self = this,
refreshTime = 1000; //interval time in ms
activate();
function activate() {
this.data = 0;
$interval(updateView, refreshTime);
}
function updateView() {
self.data = Math.round(Math.random()*100, 0);
}
}
function myDirective() {
return {
restrict: 'E',
scope: {
},
bindToController: {
data: '='
},
template: '<div><p>directive data: {{directiveCtrl.data}}</p></div>',
controller: function($scope) {
$scope.$watch('directiveCtrl.data', function(newValue) {
console.log('data changed', newValue);
});
},
controllerAs: 'directiveCtrl'
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-app="demoApp" ng-controller="MainController as ctrl">
model value in ctrl. {{ctrl.data}}
<my-directive data="ctrl.data"></my-directive>
</div>
I've come to the following solution(in case somebody runs into the the same problem):
// Directive's code
myApp.directive('myDir', function () { return {
restrict: 'E',
templateUrl: function () {
return 'my-dir.html';
},
scope: {
'id': '#arId',
'x': '#arX',
'y': '#arY',
//....
},
link: function ($scope, element, attrs) {
// *** SOLUTION ***
attrs.$observe('arId', function (id) {
$scope.id = id;
});
//...
}
Update: somebody sent me this answer, they have the same problem and came up with a very similar if not exact same solution:
Using a directive inside an ng-repeat, and a mysterious power of scope '#'
It is useful to read because they explain what's the idea behind it.