Say I have a component that looks like this:
parent.component.js
...
template: `<div>
<child-one value="value" callback="callback"></child-one>
<child-two value="value"></child-two>
</div>`,
controller: function() {
this.callback = function(n) {
this.value = n;
}
}
...
Then the child components look like this:
childOne.component.js
...
bindings: {
value: '<',
callback: '<'
},
template: `<input type="text"
ng-model="$ctrl.value"
ng-change="$ctrl.callback($ctrl.value)"
/>`
...
childTwo.component.js
...
bindings: {
value: '<'
},
template: `<div>{{$ctrl.value}}</div>`
...
(binding technique thanks to krawaller)
I want the value that is set in childOne to go to childTwo. Updating the value in childOne does update the value in the parent but does not pass it down to childTwo.
Note:
You are setting values in the this object. Not directly in the $scope.
Use $scope, instead of this
Modified code:
parent.component.js
...
template: `<div>
<child-one value="value" callback="callback"></child-one>
<child-two value="value"></child-two>
</div>`,
controller: function($scope) {
$scope.callback = function(n) {
$scope.value = n;
console.log($scope.value);
}
}
...
If you want to write a code with this keyword, then use controllerAs syntax.
Refer below code for parent.component.js
...
template: `<div>parent: {{vm.value}} <br/><div/>
<div>
<child-one value="vm.value" callback="vm.callback"></child-one>
<child-two value="vm.value"></child-two>
</div>`,
controller: function() {
const vm = this;
vm.callback = function(n) {
vm.value = n;
console.log(vm.value);
}
},
controllerAs: "vm"
...
Result:
Updating the value in childOne will update the value in the parent as well as childTwo.
Related
I have following component
componentsModule.component('detailComponent', {
templateUrl: 'detailTemplate.html',
scope: {},
bindings: {
textParam: '='
},
controller: ['$mdDialog', function($mdDialog) {
this.textParam // this is undefined always
}]
});
I am loading template in a dialog from other controller like this
$scope.textParam = tParam;
const parentEl = angular.element(document.body);
$mdDialog.show({
template: '<detail-component text-param="$scope.textParam"></detail-component>',
ariaLabel: 'detailComponentDialog',
parent: parentEl
}).then((savedDatails) => {});
If I use binding as # then I get $scope.textParam as a string inside my controller in this.textParam but if I set binding as < or = then I get undefined
I have the following angular:
angular.module("app.components", []);
angular.module("app", [
"app.components"
]);
angular.module("app.components")
.component('testWidget', {
templateUrl: '/Widgets/TestWidget/Templates/TestWidget.template.html',
bindings: {
something: "="
},
controller: function () {
var ctrl = this;
// ctrl has nothing on it
}
});
<div ng-app="app">
<test-widget something="Shoopy"></test-widget>
</div>
but the something is not part of the object (this) in the controller. what have i missed?
two-way binding (expects a parent scope property to watch for value changes):
bindings: {
something: "="
}
A parent controller would need to set the property:
$scope.Shoopy = "hello world"
For parameter bindings use the following:
string value binding:
bindings: {
something: "#"
}
you forgot controllerAs since you're using this instead of $scope
try this
angular.module("app.components")
.component('testWidget', {
templateUrl: '/Widgets/TestWidget/Templates/TestWidget.template.html',
bindings: {
something: "="
},
controllerAs: "ctrl",
controller: function () {
var ctrl = this;
// ctrl has nothing on it
}
});
I want to transform the scoped variable, like trimming the passed string.
but it shows always as it passed.
here is my sample code,
export function testDirective() {
return {
restrict: 'E',
template: `<a>{{vm.testText}}</a>`,
scope: {
testText: '#'
},
controller: TestController,
controllerAs: 'vm',
bindToController: true
}
}
export class TestController {
testText: string;
constructor(private $scope: angular.IScope) {
// when getting variable, I need to transform the value
$scope.$watch( () => this.testText, (newVal: string) => {
this.testText = newVal.trim() + ' Edited'; // this doesn't affact
});
}
}
why that code is not working?
To make it work I added additional variable(trimmedText), but I don't think this is right approach,
export function testDirective() {
return {
restrict: 'E',
template: `<a>{{vm.trimmedText}}</a>`,
scope: {
testText: '#'
},
controller: TestController,
controllerAs: 'vm',
bindToController: true
}
}
export class TestController {
testText: string;
trimmedText: string;
constructor(private $scope: angular.IScope) {
// when getting variable, I need to transform the value
$scope.$watch( () => this.testText, (newVal: string) => {
this.trimmedText = newVal.trim() + ' Edited'; // it works
});
}
}
Please advice me
#Expert wanna be, using the = sign in the isolated scope of the directive definition sets up two way data binding within the directive.
Check the below code snippet, here's the jsfiddle link.You can find more information about the different types of data binding in directives here
The HTML
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
<custom-directive test-text="ctrl.text"></custom-directive>
</div>
</div>
The Angular code
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.controller('CustomDirectiveController', CustomDirectiveController)
.directive('customDirective', customDirective);
function DefaultController() {
var vm = this;
vm.text = 'Hello, ';
}
function customDirective() {
var directive = {
restrict: 'E',
template: `{{vm.testText}}`,
scope: {
testText: '='
},
controller: CustomDirectiveController,
controllerAs: 'vm',
bindToController: true
};
return directive;
}
function CustomDirectiveController() {
var vm = this;
// transforming/updating the value here
vm.testText = vm.testText + 'World!';
}
$scope.$watch( () => this.testText, // <-- use this.textText here
'#' is not right binding, if you want to modify it - use '='.
But using additional variable is actually correct approach.
Another good way for simple transformation is using filter.
I want to pass a function from the parent component to the child component and give it an argument that has also been given from the parent component to the child. (showOrHideSub="item.showOrHideSub(item.id)" ) I have tried different ways and it doesn't work.
This is my html (parent component) in which I want to use the child component tag. vm is the controller of this scope:
<li ng-repeat="item in vm.menuItems">
<menu-item-comp id="item.id" showOrHideSub="item.showOrHideSub(item.id)" />
</li>
Here is the child component template. itemVm is the controller of this component:
<div id="{{itemVm.id}}" ng-mouseover="itemVm.showOrHideSub(itemVm.id)">
<div id="itemVm.subId" class="menuItemImgText">{{ itemVm.label }}</div>
Here is the child component js:
module.component('menuItemComp', {
templateUrl: '/webapp/app/components/menu/menuItemComponent.html',
bindings: {
id: '<',
showOrHideSub: '&',
label: '<',
submenuId: '<',
},
controllerAs: 'itemVm',
controller: ['LogService', menuCtrl]
});
function menuCtrl($scope, LogService) {
var itemVm = this;
}
And here is the showOrHideSub() function in the parent controller:
vm.showOrHideSub = function (submenu) {
console.log(submenu);
switch (submenu) {
case 'menuItemDivPositions':
console.log('position');
break;
case 'menuItemDivOther':
console.log('other');
break;
}
}
I know that in directives the way to do it is by object mapping such as showOrHideSub="item.showOrHideSub({item: item.id})" but it doesn't seem to work in component.
If you're working with components, you have to do it the components way.
It looks like you have a hierarchy of components (child / parent).
Functions and attributes inside the parent can be inherited by children using require.
require: {
parent: '^^parentComponent'
}
This way, if the parent defines a function showOrHideSub, the children can call it directly using this.parent.showOrHideSub(xxx)
This is not the only way to solve your issue but this is the right way™ for the architecture you chose.
var parentComponent = {
bindings: {},
controller: ParentController,
template: `
<li ng-repeat="item in $ctrl.menuItems">
<child-component item="item"></child-component>
</li>
`
};
var childComponent = {
bindings: {
item: '<'
},
require: {
parent: '^^parentComponent'
},
controller: ChildController,
template: '<button ng-click="$ctrl.buttonClick($ctrl.item.id);">{{$ctrl.item.name}}</button>'
};
function ParentController() {
this.menuItems = [{id:1, name:"item1"},{id:2, name:"item2"}];
this.showOrHideSub = function(param) {
console.log("parent function called with param: " + param);
}
}
function ChildController() {
var vm = this;
this.buttonClick = function(id) {
vm.parent.showOrHideSub(id);
}
}
angular.module('app', []);
angular.module('app')
.component('parentComponent', parentComponent)
.component('childComponent', childComponent);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="app">
<parent-component></parent-component>
</div>
Change 'item' to 'vm' in below code. You are binding the item function 'showOrHideSub(item.id)' which doesn't exist. Here is the updated code.
<li ng-repeat="item in vm.menuItems">
<menu-item-comp id="item.id" showOrHideSub="vm.showOrHideSub(item.id)" />
</li>
login.js
var app= angular.module('login',[]);
app.controller('LoginCtrl', function($scope)
{
$scope.sayhello="Hello"+ $scope.username;
}).directive('loginDir', function(){
return {
scope:{},
templateUrl: 'logintpl.html',
controller: 'LoginCtrl'
};
});
var adapter = new ng.upgrade.UpgradeAdapter();
AppComponent = ng.core
.Component({
selector: 'login',
directives: [adapter.upgradeNg1Component('loginDir')],
template: '<login-dir></login-dir>'
})
.Class({
constructor: function() {}
});
app.directive('login', adapter.downgradeNg2Component(AppComponent));
document.addEventListener('DOMContentLoaded', function() {
adapter.bootstrap(document.body, ['login']);
console.log(adapter);
});
logintpl.html
<input type="name" ng-model="username">
how can i use $scope.sayhello variable inside the component.
eg: component template should be,template:'<login-dir></login-dir>{{sayhello}}
AppComponent = ng.core
.Component({
selector: 'login',
directives: [adapter.upgradeNg1Component('loginDir')],
template: '<login-dir></login-dir> {{sayhello}}'
})
.Class({
constructor: function() {
this.sayhello = "Hello World !!!";
}
});
Explanation
In Angular 2, there is no model called $scope. They replaced it with simple variables with in the Class.
We can consider the whole Class as a controller in Angular 1.x. We can create variable with this.variable_name with in a Class. constructor is the function which will be invoked first in the component. So, we can initialize all our variable here.
So, $scope.variable_name in Angular 1.x is same (or likely to) as this.variable_name in Angular 2.
In fact the sayhello attribute can be only used within your loginDir directive since it's defined in its associated scope.
You could have a use case like that:
app.controller('LoginCtrl', function($scope) {
$scope.sayhello = function() {
console.log("Hello"+ $scope.username);
}
}).directive('loginDir', function() {
return {
scope:{},
templateUrl: 'logintpl.html',
controller: 'LoginCtrl'
};
});
In the template of your Angular1 directive, you will be able to use this function:
<input type="name" ng-model="username">
<span ng-click="sayhello()">Test</span>
I don't know what you exactly want to do. Here is the corresponding plunkr: https://plnkr.co/edit/ribHwk8uSXHRv0JkLlo0?p=preview.
Edit
You can have access to attributes of the parent component from the directive since the Angular1 directive scope is internal to your directive. With Angular1, you can't either. The only thing you could do is to define a parameter to your Angular1 directive that corresponds to an attribute of your parent component and update it by reference.
Here is a sample
app.controller('LoginCtrl', function($scope) {
$scope.updateSayhelloInParent = function() {
console.log("Hello"+ $scope.username);
$scope.sayhello.message = $scope.username;
}
}).directive('loginDir', function(){
return {
scope:{
sayhello: '='
},
templateUrl: 'logintpl.html',
controller: 'LoginCtrl'
};
});
And the way to use the directive in the component:
AppComponent = ng.core
.Component({
selector: 'login',
directives: [adapter.upgradeNg1Component('loginDir')],
template: `
<login-dir [sayhello]="sayHello"></login-dir>
<br/><br/>
SayHello in component:
{{sayHello | json}}
`
})
.Class({
constructor: function() {
this.sayHello = {
message: 'default message'
}
}
});
Corresponding plunkr is here: https://plnkr.co/edit/ribHwk8uSXHRv0JkLlo0?p=preview.
Hope it helps you,
Thierry