Template attributes not resolved for upgraded Angular 1 component directive - javascript

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?

Related

Call link function directive Angular 1 ES6

I create a directive based on ES6 style:
export default class myDirective {
constructor() {
this.restrict = 'E';
this.scope = {};
this.link = link;
}
link() {
console.log('link myDirective');
}
}
then in index.js:
import angular from 'angular';
import myDirective from './myDirective';
export default angular
.module('app.directives', [])
.directive('myDirective ', () => new myDirective())
.name;
But when I call myDirective on html like: <my-directive><my-directive> it does not call link function or compile function. What can I do?
We use ES6 here, and our directives dont really look like that, I'll give an example:
import templateUrl from './some.html';
import SomeController from './someController';
export default () => ({
templateUrl,
controller: SomeController,
controllerAs: 'vm',
scope: {
someVariable: '='
},
link: (scope, element, attrs, controller) => {
scope.link = {
someFunction: function some() { }
}
},
bindToController: true
});
You get the idea anyway. Try structuring it like that and see if the link function works as you would expect.
I have the same problem using AngularJS + ES6 + Webpack. Maybe you could add this in your Directive, in your compile function:
compile() {
//console.log("compile");
return this.link.bind(this);
}
Check this links for more acurated info:
https://github.com/geniuscarrier/webpack-angular-es6/blob/master/app/modules/home/directive/footer.js
https://www.michaelbromley.co.uk/blog/exploring-es6-classes-in-angularjs-1.x/

Error Binding strings with Angular 1.5 components

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.

Angular 1.5.x - Issue with nested components

First of all, I'm using components.
I have this "parent" component:
(function() {
'use strict';
angular
.module('parentModule', [])
.component('parent', {
templateUrl: 'parent.tpl.html',
controller: ParentCtrl,
transclude: true,
bindings: {
item: '='
}
});
function ParentCtrl() {
var vm = this;
vm.item = {
'id': 1,
'name': 'test'
};
}
})();
And I'm simply trying to share the object item with another component, like this:
(function() {
'use strict';
angular
.module('childModule', [])
.component('child', {
templateUrl: 'child.tpl.html',
controller: ChildCtrl,
require: {
parent: '^item'
}
});
function ChildCtrl() {
console.log(this.parent)
var vm = this;
}
})();
View (Parent):
Parent Component:
<h1 ng-bind='$ctrl.item.name'></h1>
<child></child>
View (Child):
Child component:
Here I want to print the test that is in the parent component
<h2 ng-bind='$ctrl.item.name'></h2>
Actually I'm getting the following error:
Expression 'undefined' in attribute 'item' used with directive
'parent' is non-assignable!
Here's the DEMO to illustrate better the situation
Can you explain to me how I can make it work?
You need to remove the bindings from yor parent component.
bindings binds to the component controller like scope binds to a directive's scope. You're not passing anything to <parent></parent> So you have to remove it.
Then your child component requires a parent component, not an item.
So
require: {
parent: '^parent'
}
Of course the child template should be modified to:
<h2 ng-bind='$ctrl.parent.item.name'></h2>
Finally, if from the child controller you want to log the item that is inside the parent, you will have to write:
function ChildCtrl($timeout) {
var vm = this;
$timeout(function() {
console.log(vm.parent.item);
});
}
I never need the timeout in my components, so there might be something obvious that I missed.
http://plnkr.co/edit/0DRlbedeXN1Z5ZL45Ysf?p=preview
EDIT:
Oh I forgot, you need to use the $onInit hook:
this.$onInit = function() {
console.log(vm.parent.item);
}
Your child should take the item as input via bindings.
(function() {
'use strict';
angular
.module('childModule', [])
.component('child', {
templateUrl: 'child.tpl.html',
controller: ChildCtrl,
bindings: {
item: '='
}
});
function ChildCtrl() {
console.log(this.parent)
var vm = this;
}
})();
So your parent template will look like
<h1 ng-bind='$ctrl.item.name'></h1>
<child item="$ctrl.item"></child>
The rest should work same.

angular 1.5 (components): how to use directives with es6?

in folder directives i created two files: directives.js and color.js
directives i've imported into app.js
directives.js:
import angular from 'angular';
import ColorDirective from './color';
const moduleName = 'app.directives';
angular.module(moduleName, [])
.directive('color', ColorDirective);
export default moduleName;
color.js
import angular from 'angular';
let ColorDirective = function () {
return {
link: function (scope, element) {
console.log('ColorDirective');
}
}
}
export default ColorDirective;
and on one element in component i've added color as attr.
But it's not working. I mean inner link loop. Why? What i have wrong coded? How to use directives with angular 1.5 & es2016 ?
From what you have written its not possible to see the problem. The code you provide works, assuming that you have included your module into the page and the code is correctly compiled.
I have put your code into a fiddle, https://jsfiddle.net/fccmxchx/
let ColorDirective = function () {
return {
link: function (scope, element) {
console.log('ColorDirective');
element.text('ColorDirective');
}
}
}
angular.module('app.directives', [])
.directive('color', ColorDirective);
unfortunately I cannot split your code into modules, but that is what your code is trying to do
I'm not very familiar with es6 syntax but here is the typescript's way I'm using:
class ColorDirective implements angular.IDirective {
constructor() { }
link(scope, iElement, iAttrs, ngModelCtrl) {
}
/**
* Instance creation
*/
static getInstance(): angular.IDirectiveFactory {
// deendency injection for directive
// http://stackoverflow.com/questions/30878157/inject-dependency-to-the-angularjs-directive-using-typescript
let directive: angular.IDirectiveFactory = () => new ColorDirective();
directive.$inject = [];
return directive;
}
}
angular
.module('moduleName')
.directive('color', ColorDirective.getInstance());
EDIT: after some research, I've found the es6 way to do the same thing as above:
import angular from 'angular';
class ColorDirective {
constructor() {
}
link(scope, element) {
console.log('ColorDirective');
}
static getInstance() {
var directive = new ColorDirective();
}
}
export default ColorDirective.getInstance();

How to use Angular 1 directive variable inside the Angular 2 component function

var app = angular.module('addApp', []);
app.controller('addController', ['$scope', function ($scope) {
}]).directive('add', function(){
return {
scope:{},
template: '<input type="number" ng-model=number>',
controller: 'addController'
};
});
var adapter = new ng.upgrade.UpgradeAdapter();
AppComponent = ng.core.Component({
selector: 'my-app',
template: '<add></add>sum={{sum}}',
directives: [adapter.upgradeNg1Component('add')]
}).Class({
constructor:function() {
this.frist=5;
},
add():function{
this.sum=frist+number;
}
});
app.directive('myApp', adapter.downgradeNg2Component(AppComponent));
document.addEventListener('DOMContentLoaded', function() {
adapter.bootstrap(document.body, ['addApp']);
console.log(adapter);
});
I've upgraded the above Angular 1 code into Angular 2 using UpgradeAdapter.
I want to further write an app using Angular 2. So I want to use input numbers inside the angular component.
I think the only possibility is to use interpolation of Angular2 to pass an object of the component to the Angular1 directive so the latter can bind the input against it. Primitive types can't be used at this level since Angular2 directives don't support two way binding on such fields. You need to provide a "container" object for your value, so you can update the container by reference and share data between the Angular1 directive and the Angular2 component.
It's mandatory for this to define a sub scope for your directive in this case. "Scopes" can be shared between Angular1 directives and Angular2 components.
Here is the approach I would recommend:
var app = angular.module('addApp', []);
app.controller('addController', ['$scope', function ($scope) {
}]).directive('add', function(){
return {
scope: {
number: '='
},
template: '<input type="number" ng-model="number.value">',
controller: 'addController'
};
});
The Angular2 component would provide a container for the number:
AppComponent = ng.core
.Component({
selector: 'my-app',
template: `
<add [number]="number"></add>
<span (click)="add()">add</span>
sum={{sum}}
`,
directives: [adapter.upgradeNg1Component('add')]
})
.Class({
constructor:function() {
this.frist = 5;
this.number = { value: 10 };
},
add() {
this.sum = this.frist + this.number.value;
}
});
Here is the corresponding plunkr: https://plnkr.co/edit/M4m4aZMNr3yc7TgMD9Ko?p=preview.
Hope it helps you,
Thierry

Categories

Resources