Angular - How to use a dynamic templateUrl for a directive? - angularjs

So, for whatever reason, I am trying to create a slider, where the contents of each slide are different HTML templates. So instead of an image slider, you could say it's a HTML slider.
So in my HTML I just have this code, and the controls for the slider are also inside this HTML template:
<slide-template></slide-template>
And here is my entire slide module:
(function() {
'use strict';
angular
.module('slideCtrl', [])
.directive('slideTemplate', function() {
return {
restrict: 'E',
templateUrl: 'views/slides/slide-1.html',
replace: true,
controller: 'slideController',
controllerAs: 'slides'
}
})
.controller('slideController', function() {
var vm = this;
});
})();
I'm not sure how to move forward with this, I've tried looking around but haven't found anything that I felt I could use. Inside the controller, I would like to have an array of slide template URLs and a corresponding variable to indicate the current slide:
slideUrl = [ 'views/slides/slide-1.html', 'views/slides/slide-2.html'];
slideNum = 0;
Ideally, I would then like my directive to use these variables to determine what variable it will use for templateUrl. So by default, you can see that slideNum is 0, meaning that I want to use slide1.html or slideUrl[0]. So, my templateUrl would be something like slideUrl[slideNum]. Of course, this can't be done as the directive wouldn't be able to access that data, so I'm not sure how to do this.
The end result would be that if you clicked one of the slide navigation icons, you would be updating the slideNum variable, which would instantly change the templateUrl used.
I guess I am essentially wanting a slider which doesn't rely on some images or something like that for content, but instead is a slider of actual HTML content.
Any ideas? If I haven't explained myself well enough, please let me know.

hi I would solve it like this by creating a "main.html" template and in that:
//main.html
<div ng-if="slide == 1">
<ng-include src="'slide1.html'"/>
</div>
<div ng-if="slide == 2">
<ng-include src="'slide2.html'"/>
</div>
<div ng-if="slide == 3">
<ng-include src="'slide3.html'"/>
</div>
//controller
.controller('slideController', function() {
$scope.slide = 1
//logic to switch slides
});
for animations on the slide transitions take a look at this code pen
animations

I would suggest a main directive, where you would place the different slides on one page.
For instance, the main directive:
<div ng-include src="'slider0.html'" ng-if="slider%4==0"></div>
<div ng-include src="'slider1.html'" ng-if="slider%4==1"></div>
<div ng-include src="'slider2.html'" ng-if="slider%4==2"></div>
<div ng-include src="'slider3.html'" ng-if="slider%4==3"></div>
And then in the controller of the directive you set:
$scope.slider = 0;
// Some more logic like:
$scope.slider++;

You could move this to a link function and replace your compiled slide dynamically by adding them to the slideUrl array. This method is flexible enough to allow you to manage the slides in the controller, also you could potentially pass the slide urls to the directive through an scoped attribute.
.directive('slideTemplate', function($http, $compile, $templateCache) {
return {
restrict: 'E',
replace: true,
controller: 'slideController',
controllerAs: 'slides',
link : function(scope, el, attrs) {
// Bind active slide number to controller scope
scope.slides.num = 0;
// Declare slide urls
var slideUrl = [
'views/slides/slide-1.html',
'views/slides/slide-2.html'
];
// Load a slide and replace the directives inner html
// with the next slide.
function loadSlide(template) {
// Get the template, cache it and append to element
$http.get(template, { cache: $templateCache })
.success(function(content) {
el.replaceWith($compile(content)(scope));
}
);
}
// Progress to the next slide, this is bound to the
// controllers scope and can be called from there.
scope.slides.next = function() {
var next = scope.slides.num + 1;
var slide = slideUrl[next] ? next : slide;
scope.slides.num = slide;
loadSlide(slideUrl[slide]);
}
}
}
});

Related

Change the view based on screen size

I am aware of a similar question being asked before:
Change the templateUrl of directive based on screen resolution AngularJS
This was first asked over a year ago and since then AngularJS got changed a bit. I am curious to find out if there are any other ways to achieve something similar as I haven't found many information about templateUrl swapping, so maybe I am barking up the wrong tree here.
I have a single page app without any routes.
html:
<body ng-app="App">
// lots of html same for both desktop/mobile
<my-dir></my-dir>
// even more here
</body>
template 1:
<p>Mobile</p>
template 2:
<p>Desktop</p>
I would like to render template 1 when the screen goes below 700px and template 2 otherwise. The templates change just what is inside my-dir directive. For example Template 1 renders list and template 2 renders table.
Another requirement would be to make it responsive if possible(aka templates would change as you resize the window)
At the moment I can use the solution from the above questions but are there any other ways to do it?
In your controller:
$scope.includeDesktopTemplate = false;
$scope.includeMobileTemplate = false;
var screenWidth = $window.innerWidth;
if (screenWidth < 700){
$scope.includeMobileTemplate = true;
}else{
$scope.includeDesktopTemplate = true;
}
html template:
<body ng-app="App">
<p ng-if="includeMobileTemplate">Mobile</p>
<p ng-if="includeDesktopTemplate">Desktop</p>
</body>
Hope it helps
You can add window resize and scroll event listener on my-dir directive:
angular.module("App").directive('myDir', ['$window', '$timeout', function($window, $timeout){
return {
restrict: 'EA',
scope: {},
template:'<div>
<p ng-if="showFirstTemplate">Mobile</p>
<p ng-if="showSecondTemplate">Desktop</p>
</div>',
link: function(scope, element, attr){
function checkTemplateVisible(event){
//use $timeout to make sure $apply called in a time manner
$timeout(function(){
//pageYoffset is equal to window scroll top position
if($window.pageYOffset > 700){
scope.showFirstTemplate = true;
scope.showSecondTemplate = false;
}else{
scope.showFirstTemplate = false;
scope.showSecondTemplate = true;
}
})
})
//scroll event make sure checkTemplateVisible called on browser scrolling
$window.on('scroll', checkTemplateVisible)
//resize event make sure checkTemplateVisible called on browser resizing
$window.on('resize', checkTemplateVisible)
}
}
}])

Angular. Prepend element to body, change from controller to directive

I'm doing a gallery in angular and this gallery has a light-box view that shows the image with a dark background when a gallery item is clicked.
Right now I have code inside a controller that does the trick like this:
$scope.modal = function (iElement) {
var darkDiv = angular.element('<div class="modal__dark-background"></div>');
var body = angular.element(document).find('body');
var overlay = $compile(darkDiv)($scope);
body.prepend(overlay);
}
I also have a directive to insert my main template in a custom element:
myApp.directive('gallery', function() {
return {
restrict: 'E',
templateUrl: 'partials/gallery.template.html'
}
})
My problem is that I don't know how to make a directive that has the functionality of the code inside the controller. I want to do this because I was toll that is never a good idea to put DOM-related code inside a controller.
I don't have enough rep to comment, unfortunately.
But I don't know how to do this for the case of the modal element.
Do what exactly? Do you want to have the directive execute the above code? Try this:
myApp.directive('gallery', function($scope) {
return {
restrict: 'E',
templateUrl: 'partials/gallery.template.html',
controller: function(){
$scope.modal = ...
}
}
})
Also I would consider using ui.bootstrap or something similar for tasks like this.

Replace the html of an element with the content of an external template in a directive?

I'm trying to create a directive which is a sidebar in my shell page that will change accordingly whenever a new route is hit, and will populate itself with the sub menu items relevant to that parent route. I have 4 different menus which are external templates and i want the contents of those html files to replace the menu, the link function of my directive looks like this so far:
link: function(scope, element, attrs, ngModel) {
scope.$on("$routeChangeSuccess", function (event, current, previous) {
element.html('<div ng-include=\'enterprisesMenu.html\'></div>');
});
};
But the element is not updating, however when i use inline templates the elements updates according, but because each template is complex i prefer not to have that html inside my directive, I've also tried element.html('<div ng-include src=\'enterprisesMenu.html\'></div>');
Any ideas?
Try $compile:
element.html($compile('<div ng-include=\'enterprisesMenu.html\'></div>')
(scope));
You could achieve this result by dynamically ng-including the desired template. For instance:
HTML:
<div class="your-sidebar" ng-controller="SidebarCtrl">
<div ng-include="sidebar.url" ></div>
</div>
Controller:
app.controller("SidebarCtrl", function($scope) {
$scope.sidebar = {
url: "initial-url"
};
$scope.$on("$routeChangeSuccess", function(event, current, previous) {
// decide potentially new value for $scope.sidebar.url
$scope.sidebar.url = newValueCalculatedAbove;
});
});
This solution does not require a directive, only an extra controller. It can be done with directive too, the HTML above is the template of the directive and the JS code the controller (no link function required).

Changing src of only hovered ng-include element, where value of src is a $scope variable

I have multiple ng-include elements that have src attribute set to $scope.template_url.
I want to change src of hovered element only to new template but changing it's value will change all of elements. How can i implement it?
Html code:
<section class="parent">
<div data-ng-include data-src="template_url"></div>
</section>
Javascript (in controller):
angular.element(document).on('mouseover', '.parent', function(){
$scope.$apply(function () {
$scope.template_url = "path/to/new/template.html";
});
});
Writing jQuery dom manipulation is dirty and also don't works:
$(this).attr('data-src', "path/to/new/template.html");
I'd suggest making this a directive. Directives have their own scope, so you can still do the "on hover use a different template" idea, but for each individual one that is hovered.
<div>
<div data-some-directive=""></div>
</div>
var myApp = angular.module('myApp',[]);
myApp.directive('someDirective', function() {
return {
controller: function ($scope) {
$scope.model = "Hello"
$scope.mouseover = function () {
$scope.model = "Hovered!";
};
},
scope:{},
restrict: 'AE',
replace: true,
template: '<div><input ng-mouseover="mouseover()" ng-model="model"></div>',
};
});
Heres a fiddle to see it in action.
Tweak the template variable in the directive to use a variable on your model for the include url.
By the way, angular already has a mouseover handler, so i've just linked that into the controller with ng-mouseover in the template.

Issue in creating custom directives of marquee of images (using Angular)

I am trying to move marquee of images to a custom directive. Initially in my index file i had a div like below
<div id="marquePic" style="width:90%"></div>
and later in the script(end of body) I was doing something like below
$(document).ready(function(){
var picData = [ //image data(json),param:image(image path),title(image title),link(image link)
{
image:'http://wowslider.com/sliders/demo-33/data1/images/dahlia.jpg',
title:'bbb',
link:'#'
},
{
image:'http://wowslider.com/sliders/demo-33/data1/images/dahlia.jpg',
title:'aa',
link:'#'
}
];
$("#marquePic").picMarque({
speed: 60,//scroll speed(ms)
errorimg: 'http://www.siaa.org.cn/style/common/nophoto.jpg',//error image path
data: picData
});
});
I am using jQuery PicMarquee.js to get marquee of images.
Doing above i was able to create marquee if images..Later i thought of using angular and move my html code to a separate directive so that i can reuse it.(Please note: For simplicity only I have only one div with marquee as id.In reality there are few more html elements and hence thought of creating custom directive)
When I create a custom directive above code doesn't work
<our-clients></our-clients>
and in js i have
ourApp.directive('ourClients', function() {
return {
replace: true,
templateUrl: 'directives/clients.html'
};
});
clients.html has only data related to marquee eg
<div id="marquePic" style="width:90%"></div>
and marquee related js(picData etc) is in the original html file which includes my custom directive.
However, after doing above i don't see it working. Please suggest.
Provide your profile pictures array to your directive and let it process those and call your third party library.
Also I'd not use id for that if you want to make a directive for it.
Controller:
$scope.picData = [{... images here ...}, {...}]
In Directive:
ourApp.directive('ourClients', function() {
return {
replace: true,
templateUrl: 'directives/clients.html',
scope: {
images: '='
}
link: function($scope, element){
//or just use $(element).picMargue
$(element).find('.marquePic').picMarque({
speed: 60,//scroll speed(ms)
errorimg: 'http://www.siaa.org.cn/style/common/nophoto.jpg',//error image path
data: $scope.images
});
}
};
});
Directive html:
<div>
<div class="marquePic"></div>
</div>
Call it:
<our-clients images="picData"></our-clients>

Resources