I am trying to bind a string through the html of an angular 1.5 component. I am getting an error message that says:
Error: [$compile:nonassign] Expression ''My Title'' in attribute 'title' used with directive 'selectList' is non-assignable!
This is the html where I am calling the component:
index.html
<select-list title="'My Title'"></select-list>
and the component:
export var selectListComponent = {
bindings: {
title: "="
},
templateUrl: 'path/selectList.html',
controller: selectListController
};
and the component html:
<div>{{$ctrl.title}}</div>
You're using two way binding and providing a constant string as the binding target.
You would need to change your component to use:
export var selectListComponent = {
bindings: {
title: "#"
},
templateUrl: 'path/selectList.html',
controller: selectListController
};
The # will evaluate the value it is passed (a string in this case) and then perform one-way binding to the directive scope.
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 have the following Angular 1 directive:
return function(module) {
module.directive('myButtonUpgrade', myButtonUpgrade);
function myButtonUpgrade() {
return {
restrict: 'E',
template: template(),
scope: {
image: '=',
imageColour: '='
}
};
function template() {
var html = '<my-button visible="flipVisible"\
enabled="enabled"\
image="{{::image}}"\
image-colour="{{::imageColour}}"\
</my-button>';
return html;
}
}
return myButtonUpgrade;
};
The directive uses "my-button" directive inside its template. The "my-button" directive requires "image" and "image-colour" attribute values to be present before compile its template.
I have got the following Angular 2 component which upgrades Angular 1:
import { Component } from '#angular/core';
import { upgradeAdapter } from '../../../upgrade/index';
var myButtonEx = upgradeAdapter.upgradeNg1Component('myButtonUpgrade');
#Component({
selector: 'my-button-ex',
template: '<my-button-ex [image]="refresh"></my-button-ex>',
directives: [myButtonEx]
})
export class ButtonExComponent {
}
As you can see the template sets [image] bindings to "refresh", however that did not resolve when rendering "my-button-ex" Angular 1 directive.
It looks like the upgrade adapter tries to compile HTML syntax first without resolving template arguments:
upgrade_ng1_adapter.ts
compileTemplate(...){
this.linkFn = compileHtml(this.directive.template);
}
"this.directive.template" - template arguments not resolved!
Any ideas?
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 using 1.5 components, I don't think that matters though.
I'm trying to do a single = binding between a parent controller and a child directive isolate scope. The child isolate scope is interpolating the binding literally; instead of vm.data interpolating to the data I defined in the controller, it's coming out literally vm.data as a string.
If I try one way binding with # then, again, the "interpolated" value results in {{vm.data}} literally.
How can I get the string defined in the parent controller into the child component's template?
angular
.module('app', [])
.controller('Ctrl', function () {
this.str = '<0>, blah, <1>';
})
.component('appComponent', {
restrict: 'E',
controller: 'Ctrl as vm',
template: '<div><app-str appstr-data="{{vm.str}}"></app-str></div>'
})
.component('appStr', {
bindings: { appstrData: '#' },
restrict: 'EA',
template: function($element, $attrs) {
console.log($attrs.appstrData)
return '<span>'+$attrs.appstrData+'</span>';
}
});
https://plnkr.co/edit/VWVlhDbhP2uDRKtXJZdE?p=preview
If you wanted to get the string defined in parent controller to get render in child you should use {{appStr.appstrData}} interpolation only to use it inside child template.
Very first thing you need to change is, you are returning incorrect template from appStr template.
Instead of
return '<span>'+$attrs.appstrData+'</span>';
Use
return '<span>{{appStr.appstrData}}</span>';
Basically you should use component name to have access to component bindings, like here component name is appStr so that's why binding of variable could be accessible using {{appStr.appstrData}}
Component
.component('appStr', {
bindings: { appstrData: '#' },
restrict: 'EA',
template: function($element, $attrs) {
return '<span>{{appStr.appstrData}}</span>'; //<--change here
}
});
Demo Plunkr
Plunkr with = binding
Plunkr with no bindings (isolate: false) means no isolated scope
Here's a directive I created:
HTML:
<p-test something="'bla'"></p-test>
JavaScript:
.directive('pTest', function() {
return {
scope: {
something: '=?'
},
templateUrl: 'components/testTemplate.html',
controller: 'testController'
};
});
I'd like to be able to pass 'bla' as a string without the '', in the following way:
<p-test something="bla"></p-test>
I know it's possible via the attributes parameter in link, but it's irrelevant in this case (correct me if I'm wrong) as I'm passing these parameters directly to scope.
I'd like to be able to pass 'bla' as a string without the '', in the following way:
You would just need text binding (#) binding for that, instead of 2 way binding.
.directive('pTest', function() {
return {
scope: {
something: '#?' //<-- Here
},
templateUrl: 'components/testTemplate.html',
controller: 'testController'
};
});
and with the text binding if you want to bind scope properties then use interpolation. i.e example if bla is a scope variable holding a string then just do:
<p-test something="{{bla}}"></p-test>
Plnkr