Dynamic update of ng-include through controller and directive Angualar-js - angularjs

Left side of the page sometimes have just info, sometimes it has side menu. When there is side menu, the content of main section depends on the option clicked.
Since the same kind of layout is used in multiple places, I created nested directives for the link/info side menu.
I have problem with the link option. Once link clicked, it calls the controller function to update the ng-include location of the main content holder. Controller does update the variable that holds the ng-include link, but ng-include does not include new template.
HTML:
Left Side
<left-side hasmenu='hasMenu' update-tab='updateTab' options='menuOptions'></left-side>
Main Content area:
<div id="tabContentWrapper" class="left two-third-screen-tab " ng-include="tab">
Controller:
var myStatsController = angular.module('myStatsController', []);
myStatsController.controller('StatsCtrl',
['$scope', '$rootScope',function ($scope, $rootScope){
//default tab
$scope.tab = 'stats/views/stats.html';
$scope.updateTab = function(newTab){
$scope.tab=newTab;
};
//all tabs
$scope.menuOptions =[{'text':'Stats','link':'stats/views/stats.html','isTab':true,'isFullScreen':false},
{'text':'Summary','link':'stats/views/summary.html','isTab':true, 'isFullScreen':false}
}]);
Directives:
1) Left Side directive
HTML:
<div class="sideMenu left">
<div ng-switch="hasmenu" class="sideMenuNavSection">
<side-menu ng-switch-when="true" update-tab="updateTab(tab)" options='options'></side-menu>
<side-info ng-switch-when="false" options='options'></side-info>
</div>
<home-button></home-button>
Directive code:
//left side area
myDirectives.directive('leftSide', function () {
return {
restrict: 'AE',
replace: true,
scope:
{
options: "=",
hasmenu:"=",
updateTab:"&"
},
templateUrl: 'shared/leftSide.html'
};
});
2) Directive that choose what to display link or info sections:
//side navigation
myDirectives.directive('sideMenu', function ($compile) {
return {
restrict: 'AE',
scope: {
options: "=",
updateTab:"&"
},
link: function (scope, element, attrs) {
for (var item in scope.options) {
if (scope.options[item].isTab === true) {
var link = scope.options[item];
element.append('<tab-link update-tab="'+attrs.updateTab+'" fullscreen = "' + link.isFullScreen + '" linktext="' + link.text + '" options="' + link.link + '"></tab-link>');
}
else {
// info section - not relevant for question
}
$compile(element.contents())(scope);
}
}
};
});
Finally Directive that constructs tabLink and calles controller function updateTab(newTab) to update $scope.tab for ng-include
myDirectives.directive('tabLink', function ($compile) {
return {
replace: true,
restrict: 'AE',
scope: {
options: "#",
linktext: "#",
fullscreen: "#",
updateTab:"&"
},
template: '<div class="infoText narrow "><div class="sideMenuItem bold">{{linktext}}</div><div class="greenArrow"></div></div>',
link: function (scope, element, attr) {
var tab = document.getElementById('tabContentWrapper');
tab = angular.element(tab);
element.on('click', function (event) {
//calls controller function
scope.updateTab()(scope.options);
$compile(tab.contents())(scope);
});
}
};
});
Thanks

Because you are initiating this thing with a normal element on click, It doesn't invoke the digest cycle. Add $scope.$apply() after $scope.tab=newTab;

Hello I also wanted to change the template dynamically and I found the one way of doing that as follows:
1) set ng-include value as follows:
<div ng-controller="mainCtrl">
<div id="tabContentWrapper" class="left two-third-screen-tab " ng-include="getTab()">
</div>
2) define function "getTab()" in your controller as follows:
app.controller("mainCtrl", function(){
$scope.getTab = function (){
// you can have different conditions here for e.g.
if(x){
return "x template path";
}else if (y){
return "y template path";
}
// Or you can just return path directly as "/return "path of the template";/"
}
});

Related

How to bind my tab to tabContent using AngularJS approach with DevExpress dx-tabs

I have created dx-tabs that are dynamically loaded in the view using custom directives. I am able to output the tabs but I cannot figure out how to bind the cooresponding tab-pane with the tab. I am thinking inside of my ji-Tabset directive in the addTab function I need to somehow create a variable that will loop through my scope.tabs array and then for each integer I need to create an instance of that variable. Then inside of my ji-tab directive I need to push both the text and the content to the scope.tabs array. Any thoughts?
Directive 1 - Ji-Tabset
module FormTest {
angular
.module('FormTest') //Gets the FormTest Module
.directive('jiTabset', function () {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function ($scope) {
var tabs = $scope.tabs = [];
$scope.select = function (args) {
var tab = args.itemData;
angular.forEach(tabs, function (tab) {
tab.selected = false;
});
tab.selected = true;
};
$scope.tabSettings = {
bindingOptions: { items: "tabs" },
onItemClick: $scope.select
}
this.addTab = function (tab) {
tabs.push(tab);
};
},
templateUrl: "FormTest/views/ji-Tabset.html",
};
});
}
Directive 2 - Ji-Tab
module FormTest {
angular
.module('FormTest') //Gets the FormTest Module
.directive('jiTab', function () {
return {
require: '^jiTabset',
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'FormTest/views/ji-Tab.html',
link: function (scope, element, attrs, tabsCtrl) {
scope.text = attrs.tabName;
tabsCtrl.addTab(scope);
}
};
});
}
Ji-Tabset templateUrl
<div dx-tabs='tabSettings'></div><div ng-transclude></div>
Ji-Tab templateUrl
<div ng-show="selected" ng-transclude></div>
Main view
<ji-tabset name="Tabs" label="testing">
<ji-tab tab-name="General">
<ji-button label="Button 1"></ji-button>
</ji-tab>
<ji-tab tab-name="Stats"></ji-tab>
<ji-tab tab-name="Stuff"></ji-tab>
<ji-tab tab-name="Other"></ji-tab>
<ji-tab tab-name="More stuff"></ji-tab>
</ji-tabset>
In case anyone ever runs into this, I was able to figure out the solution.
I had to make a few changes in my directives and use DevExpress' onItemClick event handler itemData. I had to add a view for ji-tab directive and transclude everything inside of it. And also loop through my tabs array with angular.foreach. I have updated the original post with the correct solution.

Toggle text Collapse to Expand

I am trying to change "Collapse" text to "Expand" on click of h2 tag. at the same time I am applying "active" class to h2 tag. using following directive, which is working fine, but now I am clueless about how to change "collapse" text to Expand on h2 tag click
HTML
<h2 class="panel-title" toggleclass>My Tandem URL</h2>
<a class="collapse-arrow">Collapse</a>
js
.directive("toggleclass", function () {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
element.bind("click", function () {
element.toggleClass('active');
});
}
}
})
Can anyone plz help..
The
If you want to do DOM manipulation in your directive you could change the contents of the element by doing:
var link = element.find('a');
link.text(link.text() === 'Collapse' ? 'Expand' : 'Collapse')`.
Using html instead of text also works.
You have to move the link inside the h2 to have the directive see the link.
Another approach is having the link text changed via the scope, but then you need to have a template and bind the link text to the directive scope. Then you have to wrap both elements in a directive.
This may make the solution a bit too big for this simple use case...
Another suggestion on making up an directive: encapsulates html and behaivor inside: fiddle .
angular.module('myModule', [])
.directive('togglable', function () {
return {
restrict: 'EA',
template: '<div><h2 class="panel-title" ng-class="{ active: isCollapsed }" ng-click="toggle()" ng-transclude></h2><a class="collapse-arrow">{{ isCollapsed ? "Expand" : "Collapse"}}</a></div>',
transclude: true,
replace: true,
scope: false,
controller: function ($scope) {
$scope.isCollapsed = false;
$scope.toggle = function () {
$scope.isCollapsed = !$scope.isCollapsed;
};
}
};
});
Key features:
- no manual DOM manipulaton;
- uses transclude feature;
Such a directive is easy to use:
<togglable>My Tandem URL</togglable>
Depending on how your app works, maybe you can try this:
HTML:
<h2 class="panel-title" ng-class="{'active': expand}" toggleclass>My Tandem URL</h2>
<a class="collapse-arrow" ng-hide="expand">Collapse</a>
<a class="collapse-arrow" ng-show="expand">Expand</a>
JS:
angular.module('myApp', [])
.controller('myCTRL', function($scope) {
$scope.expand = false;
})
.directive("toggleclass", function () {
return {
restrict: 'A',
scope: false,
link: function (scope, element, attrs) {
element.bind('click', function() {
scope.$apply(function() {
scope.expand = !scope.expand;
});
});
}
}
});
http://jsfiddle.net/uqbc9asf/

AngularJS: How to implement a directive that outputs its markup?

DEMO
Imagine I have some markup, e.g.:
<my-input model="data.firstName"></my-input>
Now, I would like to create a my-markup directive that will add a button to show/hide its markup.
So, this:
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
should result in this:
and when the button is clicked, the markup should appear:
The my-markup directive should not break any data bindings of its children.
Here is my attempt to implement this.
The markup appears, but the button doesn't work. Any ideas how to fix this?
PLAYGROUND HERE
Here is my approach. Couple of things:-
1) Instead of isolated scope on myMarkup, create a child scope, ultimately the actual directive myInput will be isolated. This would be required if you do need to support multiple myMarkup directive under the same scope.
2) You need a click event on the button, i wouldn't do logic on the markup instead abstract out to a method on the scope.
3) You would just need one button, do not need 2 buttons. Just change the text of the button.
.directive('myMarkup', function($compile) {
return {
restrict: 'A',
scope: true, //Create a child scope
compile: function(element) {
//Just need one button
var showButton = '<button ng-click="toggleMarkup()">{{model.showMarkup ? "Hide": "Show"}} Markup</button>';
var markup = '<pre ng-show="model.showMarkup">' + escapeHtml(element.html()) + '</pre>';
//append the markups
element.append(showButton).append(markup);
return linker;
}
};
function linker(scope, element) {
scope.model = {
showMarkup: false
};
//Click event handler on the button to toggle markup
scope.toggleMarkup = function(){
scope.model.showMarkup = !scope.model.showMarkup;
}
};
});
Demo
Please see below
function escapeHtml(html) {
return html.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
angular.module('App', []).controller('AppCtrl', function($scope) {
$scope.data = {
firstName: 'David'
};
}).directive('myInput', function() {
return {
restrict: 'E',
scope: {
model: '='
},
template: '<input class="my-input" type="text" ng-model="model">'
};
}).directive('myMarkup', function() {
return {
restrict: 'A',
scope: {},
link: function(scope, elem, attr) {
},
compile: function(element) {
var showButton = '<button ng-if="data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Hide Markup</button>';
var hideButton = '<button ng-if="!data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Show Markup</button>';
var markup = '<pre ng-if="data.showMarkup">' + escapeHtml(element.html()) + '</pre>';
element.append(showButton);
element.append(hideButton);
element.append(markup);
return function(scope, element) {
scope.data = {
showMarkup: true
};
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="App" ng-controller="AppCtrl">
<pre>data = {{ data | json }}</pre>
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
</body>

Angularjs directive depends on outer directive

I need to develop a workflow editor in Angularjs
This requires a directive(inner) that should add a div with some data and data for this directive should come from another directive(outer)
series of divs will be added right, top or bottom based on parameters.
Since you didn't post any code or exact requirements, please take a look on this demo where it shows calling directive from other directive:
HTML
<div ng-controller="MyCtrl">
<div directive-foo></div>
JS
var app = angular.module('myApp',[]);
app.directive('directiveFoo', function() {
return {
template: '<div directive-bar="123">bar</div>',
replace: true,
controller: function() {
console.log('in foo ctrl');
this.isFooAlive = function() {
return 'Foo is alive and well';
}
}
}
});
app.directive('directiveBar', function() {
return {
controller: function() {
console.log('in bar ctrl');
},
require: 'directiveFoo',
link: function(scope, element, attrs, fooCtrl) {
console.log(fooCtrl.isFooAlive());
}
}
});
function MyCtrl($scope) {
}
FIDDLE DEMO
Hope it will help you

AngularJS: how to watch tab selection?

I am using angularjs tab and pane example components
angular.module('components', []).
directive('tabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
},
template:
'<div class="tabbable">' +
'<ul class="nav nav-tabs">' +
'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">'+
'{{pane.title}}' +
'</li>' +
'</ul>' +
'<div class="tab-content" ng-transclude></div>' +
'</div>',
replace: true
};
}).
directive('pane', function() {
return {
require: '^tabs',
restrict: 'E',
transclude: true,
scope: { title: '#' },
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
},
template:
'<div class="tab-pane" ng-class="{active: selected}" ng-transclude>' +
'</div>',
replace: true
};
})
SomePage.html
<tabs>
<pane title="Datos Generales">
<div ng-include src="'/resources/js/page/running/admin/templates/update-data.html'"></div>
</pane>
<pane title="LocalizaciĆ³n">
<div ng-include src="'/resources/js/page/running/admin/templates/update-location.html'"></div>
</pane>
<pane title="Datos Contacto">
<div ng-include src="'/resources/js/page/running/admin/templates/update-contacts.html'"></div>
</pane>
<pane title="Variantes">
<div ng-include src="'/resources/js/page/running/admin/templates/update-variants.html'"></div>
</pane>
</tabs>
In the second pane there is a GoogleMap. I would like to refresh the google map when the second pane is selected.
I don't know how to get selected pane in the controller. I've tried $scope.panes but is undefined
Here are three ways you could solve your problem:
define a method on your controller, specify that method in an attribute on the tabs element, use the '&' syntax on the tabs directive's isolate scope definition to enable the directive to call the specified method when a pane is selected.
define an object on your controller's scope with a "selectedPane" property. Use the '=' sytnax on the tabs directive's isolate scope definition to enable two-way databinding. Set this property whenever a pane is selected.
have the tabs directive $emit an event, and have your controller listen for it using $on.
Update: #qopuir asked for more details about option 1.
Suppose the controller method is something like the following:
$scope.paneChanged = function(pane) {
$scope.selectedPane = pane
...
}
On the tabs element, we'll specify this function with attribute pane-changed:
<tabs pane-changed="paneChanged(selectedPane)">
Then, the tabs directive can use the '&' syntax to call this method:
.directive('tabs', function() {
return {
restrict: 'E',
transclude: true,
scope: { paneChanged: '&' },
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
$scope.paneChanged({selectedPane: pane});
}

Resources