Can a CSS class selector be applied to a component? - angularjs

I have the following Plunker that uses ui-router and Angular's 1.6x new component feature.
The state 'userRegister' becomes active then initialises the 'userRegister' component. This component injects a new <user-register/> into the <ui-view> then injects the HTML contents of the ng-template script block, which is all working fine.
The final DOM ends up being:
<ui-view class="ng-scope">
<user-register class="ng-scope ng-isolate-scope">
<h1 class="header">Create account</h1>
</user-register>
</ui-view>
However, I cannot find a way to add a CSS class selector to the <user-register/> tag.
e.g. using a class selector called .example I'd like to achieve the following:
<user-register class="example ng-scope ng-isolate-scope">...<user-register/>
Any ideas please?

Sure you could always wrap the template on a div and put the class there.
If don't want to do it, you can inject the $element and use the $postLink function to add the class you need:
.component('userRegister', {
templateUrl: '/views/user-register',
controller: function($element) {
this.$postLink = function() {
$element.addClass('example');
}
}
})
Here is the working plunker:
https://plnkr.co/edit/VuWu8L9VqrgJRGnxItY2?p=preview
Final DOM:
<user-register class="ng-scope ng-isolate-scope example">
<h1 class="header">Create account</h1>
</user-register>

Related

How to avoid `require` and Access the controller of the parent component in transclusion

I'm trying to build a form component that receives an object as input and use the template defined into the object to ng-include the right template to render the form defined in the model.
The problem I have is the object might be defined in the above component. For example this:
<somecomponent>
<formx object="$ctrl.settings"></formx>
</somecomponent>
Unfortunately, it doesn't seem to work. From what I read the transcluded block should be using the scope of the above controller. Is there a way to access the scope of the component somecomponent?
By the way, what I'm looking for is to do the same as:
<div ng-controller="SomeController as ctrl">
<formx object="ctrl.settings"></formx>
</div>
But instead of using a plain controller I'd like to use a component without using an explicit require as the parent component might be different from time to time.
With components the ng-include directive adds a child scope to the isolate scope. Transcluded components need to reference $parent:
<somecomponent settings="'ss'">
̶<̶f̶o̶r̶m̶x̶ ̶o̶b̶j̶e̶c̶t̶=̶"̶$̶c̶t̶r̶l̶.̶s̶e̶t̶t̶i̶n̶g̶s̶"̶>̶<̶/̶f̶o̶r̶m̶x̶>̶
<formx object="$parent.$ctrl.settings"></formx>
</somecomponent>
The DEMO
angular.module("app",[])
.component("somecomponent",{
transclude: true,
bindings: {settings:"<"},
template: `
<fieldset>
somecomponent scope-{{$id}}
<ng-transclude>
</ng-transclude>
</fieldset>
`
})
.component("formx",{
bindings: {object:"<"},
template: `
<fieldset>
formx scope-{{$id}}<br>
object={{$ctrl.object}}
</fieldset>
`
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<somecomponent settings="'ss'">
<formx object="$parent.$ctrl.settings"></formx>
</somecomponent>
</body>

Ng-if inside ng-template does not work

I created a component and I am trying to make the component's template dynamic, that is, for some condition the parent tag should be a div, otherwise it should be an anchor tag.
I have been trying to use ng-if but somehow it wont work. Here is a code snippet. For some reason, even if the ng-if is true, the nested div (.testDiv .testThumbnail) will be undefined and this will break my component.
I cannot understand why it doesn't find the component even if the ng-if is true. I am new to Angular JS, so maybe I am missing something here? Or there is a better way to dynamically create the component's parent tags according to some condition.
function myCardController($window) {
var element = angular.element(document.querySelector('.testDiv .testThumbnail'));//is undefined
}
angular.module('myApp').component('myCard', {
templateUrl: 'testTemplate', ,
controller: ["$window", myCardController],
});
<script type="text/ng-template" id="testTemplate">
<div ng-if="true"
class="testDiv">
<div role="img" class="testThumbnail"></div>
</div>
<a ng-if="false" class="tesstDiv">same content</a>
</script>
You probably miss to add ng-app or ng-controller directive. Use following HTML as your template:
<div ng-controller = "myCardController">
<div ng-if="show"
class="testDiv">
<div role="img" class="testThumbnail"></div>
</div>
<a ng-if="!show" class="tesstDiv">same content</a>
</div>
Update your JS code as like:
function myCardController($window) {
var element = angular.element(document.querySelector('.testDiv .testThumbnail'));//is undefined
$scope.show = true;
}
angular.module('myApp').component('myCard', {
templateUrl: 'testTemplate.html', ,
controller: ["$window", myCardController],
});
You also can use ng-app="myApp" in your body tag.

components load before call angularjs

I am working on angular js components there are two components that will called when their respective button is clicked
Issue: when the page loads both components are initialized , but in real they should be initialized when I click on the respective component
example:
index.html
<div class='breadcrumbs'>
<div class='inner'>
<ul class='cf' style=" margin-top: 0px;">
<li ng-click="WizardsViewerCtrl.createFirst()">
</li>
<li ng-click="WizardsViewerCtrl.createSecond()">
<span>Arrange Questions</span> </a>
</li>
</ul>
</div>
</div>
<div ng-show="viewFirstRoute">
<first-component></first-component>
</div>
<div ng-show="viewSecondRoute">
<second-component></second-component>
</div>
index.js
$scope.viewFirstRoute=false;
$scope.viewSecondRoute=false;
function createFirst() {
$scope.viewFirstRoute=true;
$scope.viewSecondRoute=false;
}
function createSecond() {
$scope.viewFirstRoute=false;
$scope.viewSecondRoute=true;
}
problem is when this page index.html is loaded ,both components are initialized
first.component.js
angular
.module('AOTC')
.component('firstComponent', {
templateUrl: 'first.html',
controllerAs: 'firstCtrl',
controller:(function ($scope) {
console.log("first component loaded");//it prints while index.html is loaded even i didn't clicked on this component
})});
second.component.js
angular
.module('AOTC')
.component('secondComponent', {
templateUrl: 'second.html',
controllerAs: 'secondCtrl',
controller:(function ($scope) {
console.log("second component loaded");//it prints while index.html is loaded even i didn't clicked on second component
})});
summarizing: two components on same route must be initialized when I click on the respective component , but behavior noticed components are loaded as soon as route is loaded it doesn't care about the click, see comments in the JS file
Use ng-if instead of ng-show. it should fix the issue.
<div ng-if="viewFirstRoute">
<first-component></first-component>
</div>
<div ng-if="viewSecondRoute">
<second-component></second-component>
</div>
ng-show: it will add component to DOM but set its display property to none.
ng-if: Will add or remove component from DOM conditionally.

Angular component with transcluded markup

I am trying to create an Angular component and transclude the inner HTML of the component, but the markup of the inner HTML does not seem to be compiling. My use case for this is that the component has an attribute binding that I want to use in multiple ways, so the template will never be exactly the same.
For example, say I have the following simple controller:
class ComponentCtrl {
$onInit() {
this.variable = 'hello world';
}
}
let MyComponent = {
controller: ComponentCtrl
};
app.component('myComponent', MyComponent);
I want the following HTML:
<my-component>
<div style="color: green;">{{ $ctrl.variable }}</div>
</my-component>
<my-component>
<div style="color: red;">{{ $ctrl.variable }}</div>
</my-component>
to render as:
<div style="color: green;">hello world</div>
<div style="color: red;">hello world</div>
However, right now it is only rendering as:
<div style="color: green;"></div>
<div style="color: red;"></div>
without the markup being evaluated.
Is there something I'm doing wrong?
did you write the right name of controller inside your {{ }} in html? you wrote controller: ComponentCtrl and then {{ $ctrl.variable }}. it looks like they must have the same names
I think the problem come from {{ $ctrl.variable }}. In fact $ctrl try to link with a parent controller not with the controller of your component.
If you want interact with the controller of your component you need to use some parameter.
Transclusion is not made by default, you have to especify on your component that it has to be transcluded. Also, you didn't especify on your template where it should be trasncluded. Therefore, your component should look like:
let MyComponent = {
transclude: true, // tell angular to transclude it
template: '<ng-transclude></ng-transclude>', // tell where it will be transcluded
controller: ComponentCtrl
};
app.component('myComponent', MyComponent);
However, how was told on comments, component scopes are always isolated. Therefore, ou won't be able to access {{ $ctrl.variable }} from outside the component.
The transcluded content's scope has a $parent property that always points to the host component's scope.
So you could do something like this -
<my-component>
<div style="color: green;">{{ $parent.$ctrl.variable }}</div>
</my-component>
<my-component>
<div style="color: red;">{{ $parent.$ctrl.variable }}</div>
</my-component>
Plunk link that uses $parent property - http://run.plnkr.co/preview/ckdwiuzlb00073b661a7blt3f/

Angular UI Bootstrap Modal strips id and class attributes

Live Example
Adding the following Angular UI Bootstrap Modal:
<div id="my-id" class="my-class" modal="opened">
<p>Modal Here</p>
</div>
results in:
<div class="modal ng-scope">
<p>Modal Here</p>
</div>
Why the id and class attributes are stripped?
I would like to set some CSS styling on the dialog, e.g. dialog's width, or styling some dialog's inner elements. How could I achieve that?
Because I just came across this irritating issue myself and the documentation and default behavior isn't obvious. You can now pass in additional classes via the $modal.open() method using the windowClass option:
var modalInstance = $modal.open({
templateUrl: 'templateUrl.html',
controller: Controller,
windowClass: 'custom-css-class',
...
});
Can't set an ID though which is lame. More info in the official angular-ui modal docs.
Here's the github issue explaining why the id is being stripped.
As for the class, I'm not sure why's that stripped, but you can use $dialog options to specify the class (which will fix your issue):
<div id="my-id" modal="opened" options="{dialogClass: 'modal my-class'}">
<p>Modal Here</p>
</div>

Resources