In Angular JS I would want to get a component controller instance manually, that ideally is achieved through the require binding in component. I have a requirement where in I am outside the Angular JS component environment and I cannot use require.
// Sample require binding
module.component('superHeros', {
template: `
<h2>
Super Heros Component
</h2>
`,
require: {
parentCtrl: '^heroList'
},
controller: superHerosControler
});
Though I thought this could be achieved with $injector of angular but does not seem to work (throws Unknown provider).
So I want to do this
require: {
parentCtrl: '^heroList'
}
like this this.requiredController = $injector.get(<componentName>);
I understand that this works perfectly for Services or factories. Is there a way to achieve this or a workaround that can essentially get me an component controller instance ?
Plunker here
Any help would be appreciated. Thanks
Related
I am unable to inject resolve property of ui-routing in controller.
It is giving
Error: $injector:unpr
Unknown Provider
When I'm using controller property in state definition object as following
.state('widget', {
url: '/widgets',
template: '<h1>{{name}}</h1>',
controller: function(widget, $scope) {
$scope.name = widget.name;
},
resolve: {
// standard resolve value promise definition
widget: function() {
return {
name: 'myWidget'
};
},
// resolve promise injects sibling promise
features: function(widget) {
return ['featureA', 'featureB'].map(function(feature) {
return widget.name+':'+feature;
});
}
}
});
Then it is working fine and I'm able to get the widget in controller and able to use in html.
Please see the fiddle for code.
http://jsfiddle.net/sunilmadaan07/ugsx6c1w/8/
Might be I'm making a silly mistake.
Before posting this question I have tried returning with simple object, promise object to the property.
Thanks In Advance.
You can not get resolved data in the directive with the code you did. Basically, you are trying to implement component based structure with an older version of angular 1.3.x.
You have two options to achieve this.
Create route controller then you can access resolve to the controller as local dependency then use that dependency as binding to the directive.
Here is example - http://plnkr.co/edit/TOPMLUXc7GhXTeYL0IFj?p=preview
Upgrade angular version to 1.5.x and use "ui-router-route-to-components": "^0.1.0"
Here working example of your code - http://jsfiddle.net/ugsx6c1w/25/
In order for the controller to be able to use resolvers, it should be route component (only in UI Router 1.x) or route controller.
widget and features are local dependencies, they aren't registered in the injector and aren't available anywhere in the application except route controller/component.
As explained here, resolvers can be passed to nested components in UI Router 0.3.x and injected directly to route components in 1.x.
I'm just learning Angular and have a very basic app set up. When rendering some data via the templateUrl property of a route, what would be the best way to include a sub-controller in the returned template? For example, including a "createOrEditItem" template at the bottom of a "viewItem" template so that the "createOrEditItem" can be reused on its own later?
I've tried putting a div in the template with its ng-controller attribute set to a controller name that I've defined at the app level, but it's not being activated. Should this be done with a directive instead to make it instantiate when the master controller has its contents set, or am I missing something more fundamental?
yes, as mentioned in the later part of the question, you should be using a directive. Or, if using AngularJS >= v1.5, component should be the choice because they are pluggable and works well with nesting too.
Note that for the route also, you can directly use a component like this:
var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
// ^^^^ other components can be used here
controller: function() {
this.user = {name: 'world'};
}
});
myMod.config(function($routeProvider) {
$routeProvider.when('/', {
template: '<home></home>'
});
});
Now, as the comment suggests, you can freely use other components in the template of home component.
Hope this helps a bit!
A directive can be used.
Another option is to use a seperate view/route. So when you add a ui-view tag, you could define your view and route.
This is explained here:
https://scotch.io/tutorials/angular-routing-using-ui-router
I am trying to make my first component in angular but i think i am doing it wrong.
In my controller, i call a resource to get data from a web service.
My component look like this :
.component('test', {
templateUrl: '/layout/test.template.html',
bindings: {
data: '='
},
controller: myController
})
i use my component like this in my template :
<test data="row" ng-repeat="row in rowCollection"></test>
The problem is that i call the web service each time i use the component.
In fact, in rowCollection, i have all the data i need. Can i use my component without controller and just use the data passed.
Can you help me ?
thanks
Ok, Writing the problem makes me solve it.
myController was used by my page and my component.
I created a new controler for my component where i don't call the webservice and just use the data passed.
It works well.
I'm using Angular 1.5+ with Typescript, preparing my code to be compatible with Angular 2. I've got a situation where many of my components need to use an application-wide repository location for the views that are mapped to their templateUrl properties, but sometimes a view needs a specific, local implementation.
So, normally the views are hosted on a fast-served CDN, they're re-used between multiple sites that all belong to the same general code base, api, etc. This is done to prevent duplicating them and to centralize what doesn't need to be repeated.
Rarely, I'll need to override this behavior and use a more specific, fine-tuned view. So my approach to this was to add a binding to the components called viewLocation, with the intent to use it like this;
<component-name></component-name> is the default. In this situation, the default CDN path is used.
<component-name view-location="local"></component-name> is a unique situation. If this happens, the templateUrl should be able to respond to that and switch to a path relative to the specific application and not from the CDN.
I thought it would be pretty simple until I realized that I wouldn't have access to the binding properties within the actual constructor or templateUrl function, as is demonstrated here;
export class SidebarComponent extends Component {
constructor() {
super();
this.bindings = { viewLocation: '=' };
// other properties
this.templateUrl = ($someService: IServiceInterface): string => {
// help! I don't have access to the viewLocation property!
}
}
}
So is there anything else I can do to get access to that property?
This is not done in TS, but the AngularJS 1.5 component generally provides the $element and $attrs to the $injector when you are using an injectable template.
This is an example in AngularJS using Javascript where the template URL is picked based on an attribute set on the component:
angular.module('example', []);
angular.module('example').component('myComponent', {
templateUrl: function($element, $attrs, $log) {
$log.info('determining template to be used');
if($attrs.useTemplate) {
return $attrs.useTemplate;
}
return 'default.html';
}
});
Template snippet:
<my-component use-template="hui.html"></my-component>
<my-component use-template="bu.html"></my-component>
<p></p>
<my-component></my-component>
Working example:
template injection in an angular 1.5 component
I'm trying to create a simple Angular 1 + 2 hybrid application in TypeScript using the component directive pattern as described here: https://angular.io/docs/ts/latest/guide/upgrade.html#!#using-angular-1-component-directives-from-angular-2-code
I got it working with the regular directive declaration, but I can't get it to work with the component directive.
Just to contextualize: I'm actually creating a new Angular 2 application, but I need a component that hasn't been converted yet, called Formly, so I'm thinking about using the Angular 1 version in the meantime.
The full code is here: https://plnkr.co/edit/J5rK48?p=preview
I created a component directive following the heroDetail sample on the guide:
export const tstv1 = {
template: `<a>Angular 1: {{value}}</a><br/>`,
controller: function($scope) {
$scope.value = 'Angular 1';
}
};
Then I tried to use it by upgrading the component:
const tstv1 = upgradeAdapter.upgradeNg1Component('tstv1')
However this throws an error:
[$injector:unpr] Unknown provider: tstv1DirectiveProvider <- tstv1Directive
http://errors.angularjs.org/1.5.0-rc.1/$injector/unpr?>p0=tstv1DirectiveProvider%20%3C-%20tstv1Directive
What am I doing wrong?
Also, the examples don't have a import statement for the component directive. Should I add it or not? I have tried both and it doesn't work either way. If there isn't a import, how would Angular know where to get the directive from?
I tried it like this:
import {tstv1} from 'src/tstv1/tstv1.component'
Like I said I got it working using a regular directive:
app.directive('tstv1directive', function() {
return {
restrict: 'E',
require: '?ngModel',
template: '<a>Inside directive: {{value}}</a>',
controller: function($scope) {
$scope.value = "Works!"
}
}
}))
and I can upgrade and use it just fine:
upgradeAdapter.upgradeNg1Component('tstv1directive')
see the working one: https://plnkr.co/edit/nAiqX2w4ENkYd6Z8db7M?p=preview
The way you 'create' the component directive was actually just create an object, you have to register it as a component.
see main.ts
app.component('tstv1', tstv1);
and you have to import the tstv1 object as well in main.ts
after that, just downgrade it like the one you did with the regular directive.