Angular 1.5 Component template with slot for child-dom - javascript

I'm attempting to make a reusable self contained angular 1.5 component that acts as a wrapper for other page content. When a value is changed it will toggle the child content. The solution of hiding and showing content is not in question but how to make a reusable component that can handle injecting the children into the components template and not over write the template.
page.html
<div class="some page">
<my-cmp value="false">
<p>this static content is toggled by my-cmp</p>
<div>more stuff</div>
<ul>
<li>no limits</li>
</ul>
</my-cmp>
</div>
When value is changed the component should change the content on the page.
<div class="some page">
<my-cmp value="true">
<div>
<h1>Content is hidden</h1>
<button>Show content</button>
</div>
</my-cmp>
</div>
I have been reading the angular 1.5 docs and haven't found any example that does this. The problem i'm having is that the child content is over writing the content in my components template and i have no way of telling the component the exact spot to place the child content. The component should be able to switch between the two states freely.
my-cmp.html
<div class="my-cmp">
<div ng-if="showContent">
<!--child content should be inserted here by the component -->
<!-- how do i tell the my-cmp template i want child elements injected here? -->
<!-- this is the only part i'm missing -->
</div>
<div ng-if="!showContent">
<h1>Content is hidden</h1>
<button>Show content</button>
</div>
</div>

(function() {
angular
.module('app.my-component')
.component('myCmp', {
transclude: true,
bindings: {
showContent: '='
},
templateUrl: 'my-cmp.html',
controller: 'MyCmpController'
});
})();
(function() {
angular
.module('app.my-component')
.controller('MyCmpController', MyCmpController);
/*#ngInject*/
function MyCmpController($scope) {
$scope.showHiddenContent = showHiddenContent;
function showHiddenContent() {
$scope.showContent = !$scope.showContent;
}
}
})();
In HTML:
<div class="some-page">
<my-cmp show-content="isHidden">
<p>this static content is toggled by my-cmp</p>
<div>more stuff</div>
<ul>
<li>no limits</li>
</ul>
</my-cmp>
</div>
my-cmp.html
<div class="my-cmp">
<div class="content" ng-if="showContent" ng-transclude> <!-- ng-show="showContent" -->
<!-- content is added by children passed in -->
</div>
<div ng-if="!showContent"> <!-- ng-hide="!showContent" -->
<h1>Content is hidden</h1>
<button type="button" ng-click="showHiddenContent()">Show content</button>
</div>
</div>

I managed to find some old documents showing how to do this. the transclude: true, in the component is needed.
my-cmp.js
(function() {
angular.module("app").component("my-cmp", {
templateUrl: "views/tmpl/components/my-cmp.html",
controller: [myCmp],
restrict: 'E',
transclude: true,
binding: {
show: '<',
}
});
function myCmp() {...}
})();
my-cmp.html
<div class="my-cmp">
<div class="content" ng-if="showContent" ng-transclude>
<!-- content is added by children passed in -->
</div>
<div ng-if="!hideContent">
<h1>Content is hidden</h1>
<button>Show content</button>
</div>
</div>
adding ng-transclude as an attribute to the element that is the target, will tell angular to inject the child DOM elements in this spot and not over write your template.

Related

Angular element inside bootstrap popover

I am using Bootstrap with angular 2, My angular (click) is not working inside my popover content.
HTML:
<p id="myPopover-profile">Operator</p>
<div class="profile-option" id="profile-option">
<div class="user-name"> Jhone Doe</div>
<div class="line"> </div>
<div class="end-session" (click)="endSession()"> End Session </div>
<div class="logout"> Logout </div>
</div>
</div>
TS:
ngOnInit() {
$('#myPopover-profile').popover({
placement: 'bottom',
toggle: 'popover',
html: true,
content: function() {
return $('#profile-option').html();
}
});
}
endSession(){
console.log("test");
}
why dont you create a component for your popover and in the popover html you do something like <my-component></my-component> therefore you can use myComponent.ts file to have the logic you want.

Hide parent div and show ui-view in Angular Js

How to hide the div "whole" when click on #clickit so that my setting page content won't show and .user page will show in ui-view
setting html page
<!-- page content -->
<div class="whole">
<div class="page-title">
<div class="title_left">
<h3>Settings</h3>
</div>
<div class="clearfix"></div>
<div class="row" id="clickit"> // <-- Click to hide "whole" class
<div class="col-md-12 col-sm-12 col-xs-12">
<a ui-sref=".user">Users & practitioners</a>
</div>
</div>
</div>
</div>
<div ui-view> // I HAVE TO SHOW THIS DIV AND HIDE SETTING DIV
</div>
<!-- /page content -->
I don't want a jquery solution.. all i need a angular of pure javascript solution
<div class="whole" ng-hide="hideWhole> // <-- assign a scope var dependency
<div class="page-title">
<div class="title_left">
<h3>Settings</h3>
</div>
<div class="clearfix"></div>
<div class="row" id="clickit" ng-click="hideWhole=1"> // <-- update scope var
Ideally you'll use a controller variable (ctrl.hideWhole in controllerAs syntax) so you can update the display of the element from elsewhere in the app.
Here is a JavaScript solution to your problem, attaching a function to the onclick event of your element which sets the css display attribute to none.
<div class="row" id="clickit" onclick="hideWhole()">
function hideWhole() {
var wholeDiv = document.getElementsByClassName(".whole");
wholeDiv.style.display = 'none';
}

How to access controller-variables in multiple templates

Hi I´m new to Angular JS, and I am trying to create a "lightbox" by showing or hiding a div that fills the screen. However, the "ng-click" event and the div are on different templates.
First Template (where the div is supposed to show up):
<body ng-app="portfolio" ng-controller="lightboxController" >
<div ng-show="lightbox" class="lightbox">
<p ng-click="checkLightbox()">go back</p>
<p>{{message}}</p>
</div>
<div class="top-menu">
portfolio
about
contact
</div>
<div id="content-wrapper">
<div id="main-content">
<div ng-view></div>
</div>
</div>
</body>
Relevant controller:
app.controller('lightboxController', function($scope) {
$scope.checkLightbox = function(){
$scope.lightbox;
lightbox= !lightbox;
console.log(lightbox);
};
$scope.message = 'Everyone come and see how good I look!';
});
Second Template (where the ng-click event is):
<div class="grid">
<div class="grid-item dreiD video" ng-click="checkLightbox()">
<img src="data/portfolio/logo-animation/thumb.gif" class="thumb"/>
The error message says: ReferenceError: lightbox is not defined
Do as below:
Your html look like
<body ng-app="app" ng-controller="lightboxController" >
<div ng-show="lightbox" class="lightbox">
<p ng-click="checkLightbox()">go back</p>
<p>{{message}}</p>
</div>
<div class="top-menu">
portfolio
about
contact
</div>
<div id="content-wrapper">
<div id="main-content">
<div ng-view></div>
</div>
</div>
<div class="grid">
<div class="grid-item dreiD video" ng-click="checkLightbox()">
<img src="data/portfolio/logo-animation/thumb.gif" class="thumb"/>
</div>
</div>
</body>
Angular controller look like:
var app = angular.module('app', []);
app.controller('lightboxController', function($scope) {
$scope.checkLightbox = function(){
$scope.lightbox;
$scope.lightbox= !$scope.lightbox;
console.log(lightbox);
};
$scope.message = 'Everyone come and see how good I look!';
});
You can run this by clicking here
Im not really sure what your checkLightbox() method are supposed to do.
But if you want to make this reusable the best solution would be to refactor the checkLightbox functionality into a reusable directive.
Something like this:
function checkLightbox() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.on('click', function() {
// add your code here
})
}
}
}
app.directive('checkLightbox', checkLightbox);
Now you can use this anywhere in your views by adding the check-lightbox attribute wherever you want.
<div class="grid">
<div class="grid-item dreiD video" check-lightbox>
<img src="data/portfolio/logo-animation/thumb.gif" class="thumb"/>
Declare function or variable in $rootScope whenever you need to use them in any other controller.
Convert your controller into a factory and create the scopes at the $rootScope level:
app.factory('lightboxController', ['$rootScope', function($rootScope) {
$rootScope.checkLightbox = function(){
$rootScope.lightbox;
lightbox= !lightbox;
console.log(lightbox);
};
$rootScope.message = 'Everyone come and see how good I look!';
}]);

How to use ng-transclude in directives

I have two directives free-form and free-form-canvas
<div ng-repeat="controls in ctrls">
<free-form></free-form>
</div>
free-form.html
<div id="{{controls.uiControlInstanceId}}">
<free-form-canvas data-controls="controls"></free-form-canvas>
</div>
free-form-canvas.html
<div>
<!-- ctrl -->
<div ng-switch on="ctrl.controlProps[1].containerInstance.uiContainerTypeId">
<div ng-switch-when="NAVIGATOR">
<navigation></navigation>
</div>
<div ng-switch-when="CAROUSEL_VIEW">
<carousel></carousel>
</div>
<web-tile ng-switch-when="WEB_TILE" class="drag_tiles" tilegroup="tileGroups">
</web-tile>
<!-- <news-bulletin data-bulletin="" ng-switch-when="WEB_BULLETIN_BOARD"></news-bulletin> -->
</div>
<floating-options></floating-options>
<floating-popup></floating-popup>
</div>
but I need to make transclude free form canvas i.e,
<div ng-repeat="controls in ctrls">
<div id="{{controls.uiControlInstanceId}}">
<free-form-canvas data-controls="controls"></free-form-canvas>
<!-- I need to include free form canvas here it self but nothing gets display -->
<div>
<!-- ctrl -->
<div ng-switch on="ctrl.controlProps[1].containerInstance.uiContainerTypeId">
<div ng-switch-when="NAVIGATOR">
<navigation></navigation>
</div>
<div ng-switch-when="CAROUSEL_VIEW">
<carousel></carousel>
</div>
<web-tile ng-switch-when="WEB_TILE" class="drag_tiles" tilegroup="tileGroups">
</web-tile>
<!-- <news-bulletin data-bulletin="" ng-switch-when="WEB_BULLETIN_BOARD"></news-bulletin> -->
</div>
<floating-options></floating-options>
<floating-popup></floating-popup>
</div>
</div>
</div>
by using ng-transclude I can solve this but I didn't know how to use ng-transclude.
As noted above by New Dev:
You don't need a transclude - <free-form> has the template with <free-form-canvas> and so it will be included as the content of <free-form>. The only difference is that in the output you will have <free-form> <free-form-canvas>....</free-form>, i.e. the output would have a wrapping <free-form>, which you don't have in your example, but that shouldn't matter. If you really care, you could use replace: true (although it is being deprecated).

How to use AngularJS features to insert multiple divs in one div (i.e. some kind of multi-element template)

In my AngularJS application, I would like to make some kind of directive that inserts multiple HTML elements to an HTML element.
Basically, I want Angular to build this result:
<div class="maindiv">
<!-- Below are some divs common to all "maindiv" -->
<div> ..XXX.. </div>
<div> ..YYY.. </div>
<!-- Below are some elements specific to maindiv1 -->
<div> ..ABCD.. </div>
<div> ..EFGH.. </div>
<div> ..IJKL.. </div>
</div>
<div class="maindiv">
<!-- Below are some divs common to all "maindiv" -->
<div> ..XXX.. </div>
<div> ..YYY.. </div>
<!-- Below are some elements specific to this particular maindiv -->
<div> ..1235.. </div>
<div> ..5678.. </div>
</div>
... from HTML that looks like this :
<div class="maindiv" generate-common-stuff>
<!-- Below are some elements specific to maindiv1 -->
<div> ..ABCD.. </div>
<div> ..EFGH.. </div>
<div> ..IJKL.. </div>
</div>
<div class="maindiv" generate-common-stuff>
<!-- Below are some elements specific to this particular maindiv -->
<div> ..1235.. </div>
<div> ..5678.. </div>
</div>
... or like this:
<div class="maindiv">
<generate-common-stuff></generate-common-stuff>
<!-- Below are some elements specific to maindiv1 -->
...
When I tried to make my own directive, Angular complained about having multiple root elements in my template.
Note that because of incompatibilities with other libraries (JQueryMobile), I cannot regroup <div> ..XXX.. </div><div> ..YYY.. </div> under a div and use it in the template. They must be inserted just under the maindiv
Do you have any idea on how to implement that ?
Try write a directive called generate-stuff and pass common and specific stuff as attributes, binded from the scope.
<generate-stuff common="commonStuffInScope" specific="specificStuffInScope"></generate-stuff>
...and in your directive
myModule.directive('', function() {
return {
...
scope: {
common: "=common",
specific: "=specific"
}
...
};
});
so in the link function you have a bidirectional binding either to the common and the specific variables on the scope.
Releated documentation fragment: http://docs.angularjs.org/guide/directive#directivedefinitionobject

Categories

Resources