angular js dynamic directive passing model attribute - angularjs

I knwo that are many relevant posts about how creating dynamic directive mapping in angular but I cannot find anything similar in my case. As the description suggests I am trying to make a dynamic directive where depending the value of an object attribute the html is changed. I simplified my problem to this case:
I have a list of object "Widget" which has an attribute name 'type'. I want depending on the type attribute of this list to render html depending on the attribute. To be more specific the Widget.type can have 3 values for example: widget1,widget2 . So in my case I want the directive to return the file widget*.html. (widget1.html for widget type "widget1" etc). Though I pass the argument to the directive using attributes, the value is not evaluated and the string widget.type is return.
Can you help me?
Thx in advance.
app.js file:
var app = angular.module("MainCtrl", []);
app.controller("myCtrl", function($scope) {
function Widget(type){
this.type=type;
}
$scope.widgets=[];
$scope.widgets.push(new Widget('widget1'),new Widget('widget2'),new Widget('widget3'));
});
app.directive('widget', function() {
return {
restrict: 'E',
scope: {
obj: '='
},
templateUrl: function(element,attr){
//console.log(attr);
console.log(attr.obj);
return attr.obj+'.html';
}
}
});
index.html file:
<!DOCTYPE html>
<html ng-app="MainCtrl">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<!-- css -->
<link rel="stylesheet" href="style.css" />
<!-- utilities -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<!-- angular module file -->
<script src="script.js"></script>
</head>
<body ng-controller="myCtrl">
<div ng-repeat="widget in widgets">
{{widget.type}}
<widget obj='widget.type' ></widget>
</div>
</body>
widget1.html file:
<div class="widget">
widget1:{{widget.type}}
</div>
widget2.html,widget3.html are the same
Notice the console output in the directive.
I created a plunker so it's easier to notice the problem:http://plnkr.co/edit/ulBSQQrqpSV9g3BNGRhO?p=preview

First example with your directive: http://plnkr.co/edit/XAXy5RGLnvUZIoRj5xAs?p=preview
Secone example without your directive:
<div ng-repeat="widget in widgets">
<!--{{widget.type}}-->
<div ng-include="widget.template"></div>
</div>
Note: additional property in your Widget-object. Easier to handle.
First one is not as beautiful as the example without the directive, becaue your scope-variable of the directive has to be the same as the variable in your templates ('widget'). I like the second example much more.

Related

Angular directive as comment not working

I'm referring AngularJS documentation, but couldn't get "Comment Directive" working the way its mentioned in fact encountered an error. Please find below details:
HTML:-
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script type="text/javascript" src="JS/DirectiveAsComment.js"></script>
<title>Directive as Comment</title>
</head>
<body ng-app="directiveAsCommentApp">
<!-- directive:test-comment-directive -->
</body>
</html>
JS:-
var app = angular.module('directiveAsCommentApp', []);
app.directive('testCommentDirective', function(){
return {
restrict: 'M',
replace: true,
template: 'this text is displayed because of "test-comment-directive" custom directive'
};
});
Error: [$compile:tplrt] Template for directive 'testCommentDirective' must have exactly one root element.
please help me solve this issue..
From the DOCs:
When a directive is declared with template (or templateUrl) and replace mode on, the template must have exactly one root element. That is, the text of the template property or the content referenced by the templateUrl must be contained within a single html element. For example, <p>blah <em>blah</em> blah</p> instead of simply blah <em>blah</em> blah. Otherwise, the replacement operation would result in a single element (the directive) being replaced with multiple elements or nodes, which is unsupported and not commonly needed in practice.
Please ref: https://docs.angularjs.org/error/$compile/tplrt
Change this
template: 'this text is displayed because of "test-comment-directive" custom directive'
to
template: '<div>this text is displayed because of "test-comment-directive" custom directive</div>'
The Problem is with your template, write them into some tags like or or :
try this
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"> </script>
<title>Directive as Comment</title>
</head>
<body ng-app="myApp">
<!-- directive:test-comment-directive -->
</body>
</html>
and in app.js try this
var app = angular.module('myApp', []);
app.directive('testCommentDirective', function(){
return {
restrict: "M",
replace: true,
template: "<div>this text is displayed because of 'test-comment-directive' custom directive</div>"
};
});

Change AngularJS urls with ng-model

Is there any way to change AngularJS urls with ng-model?
var scotchApp = angular.module('scotchApp', ['ngRoute']);
scotchApp.controller('mainController', function($scope) {
$scope.gettext = function (){
};
});
<!-- index.html -->
<!DOCTYPE html>
<html ng-app="scotchApp">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.0.0/css/font-awesome.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.js"></script>
<script src="eventChange.js"></script>
</head>
<body ng-controller="mainController">
<div class="container">
<div class="row">
<div class="col-sm-1">Search</div>
<div class="col-sm-3"><input type="text" class="form-control" ng-model="search" ng-change="gettext()"></div>
</div>
</div>
</body>
</html>
I need to change the URL into something like this http://localhost/angulRoute/search=myVal when user type 'myVal' in the search box (actually inside gettext() function with search value)
You can do something like this in your controller:
$scope.query = $location.search().q;
$scope.$watch('query', function(newValue) {
$location.search('q', newValue);
});
You'd need to inject $location into your controller, and make sure in your route config includes:
reloadOnSearch: true
That would result in a URL like http://localhost/myRoute?q=myVal
Note: I would also add ng-model-options="{updateOn:'blur'}" to your input, to prevent updating the URL on every key press.
EXAMPLE
Here is a working example of how to do this: http://plnkr.co/edit/tphqPeJ0dO74Ux7WpXlU?p=preview
Note that, because of the way plnkr.co works, you won't see the URL changes in the address bar on that site. If you download the code and run it locally, the URL would be updated in the address bar.
Hi I found a javascript solution for that.
$scope.gettext = function (search){
window.history.pushState("object or string", "Title", "/angulRoute/search="+search);
};
with <input type="text" class="form-control" ng-model="search" ng-change="gettext(search)" > worked for me. However anyone have an AngulerJs solution they are welcome :D

AngularJS change Page title from view

I got an index.html page with the following code
<html>
<head>
<title>{{title}}</title>
</head>
</html>
And i got a view.html
<div ng-controller="changeCtrl">
<input type="text" ng-model="page-title">
</div>
The routing works perfectly,
Now how can i bind the page-title model to the {{title}} while i type?
Thanks
To avoid using $rootScope or moving ng-app, use a service to handle setting the page title. In the snippet below I've given an example of using a service.
angular.module('app', [])
.service('PageService', function($window) {
return {
setTitle: function (newTitle) { $window.document.title = newTitle; }
};
})
.controller('ChangeCtrl', function(PageService) {
this.setPageTitle = PageService.setTitle;
});
<html>
<head>
<title>My Awesome App</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="app">
<div ng-controller="ChangeCtrl as ctrl">
<label>{{ctrl.title}}</label>
<input type="text" ng-model="ctrl.title" ng-change="ctrl.setPageTitle(ctrl.title)">
</div>
</body>
</html>
First of all, since the expression is right under the html root element, the angular application must "cover" this element. So ng-app should be on the html element:
<html ng-app="app">
Second, since the expression is outside of any controller scope, angular looks for the title field in the $rootScope. So, you need your input field, inside a view handled by a controller, to modify the value of a $rootScope attribute.
That can't be done:
<input ng-model="title" />
will set the field titleon the controller scope. What can be done, though, is to access an object, by scope inheritance, defined in the root scope, and modify one of its attributes. So, firstmake sure such an object exists in the root scope:
angular.module('app').run(function($rootScope) {
$rootScope.page = {
title: 'default title'
};
});
Then change the expressions to access the title attribute of this object:
<title>{{ page.title }}</title>
and in the controller view:
<input ng-model="page.title" />
make all your data bindings inside ng-controllers scope, that is {{title}} should inside or you move to your ng-controller to html tag, :)

code breaks when used inside custom directive

I'm trying to combine angular and the frontend framework materialize, since I find it better than angular-material. The following code works and results in a parallax scrolling sample:
<html lang="en">
<head>
<link href="css/materialize.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<link href="css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="bower_components/angular/angular.min.js"></script>
<script src="js/materialize.js"></script>
<script src="js/init.js"></script>
<script src="angularApp.js"></script>
</head>
<body ng-app='angularApp'>
(...)
<div class="parallax-container">
<div class="parallax"><img src="http://i3.minus.com/ibdMPM9Oo2TGYu.png" ></div>
</div>
<div class="section white">
<div class="row container">
<h2 class="header">Parallax</h2>
<p class="grey-text text-darken-3 lighten-3">Parallax is an effect where the background content or image in this case, is moved at a different speed than the foreground content while scrolling.</p>
</div>
</div>
<div class="parallax-container">
<div class="parallax"><img src="http://i3.minus.com/ibdMPM9Oo2TGYu.png" ></div>
</div>
</body>
</html>
However, this parallax effect stops working completely when I "refactor" it into a custom directive, like this:
<html lang="en">
(...)
<body ng-app='angularApp'>
(...)
<hometab></hometab>
</body>
</html>
///////
(function(){
var app = angular.module("angularApp", []);
app.directive('hometab', function(){
return{
restrict: 'E',
templateUrl: 'hometab.html'
};
});
})();
(hometab.html contains the code that used to be in the main html).
Why is this breaking the code?
Edit: To clarify, by breaking I mean that the code is inserted, but the parallax effect isn't working (I assume the javascript isn't doing its job correctly?)
Try wrapping your directive in a div and see it if works. I.e. <div hometab></div>
i experienced this issue couple days ago,
what i to do to fix this is just set transclude to be true, and set replace to be true. in case your code, it will be like this.
(function(){
var app = angular.module("angularApp", []);
app.directive('hometab', function(){
return{
restrict: 'E',
templateUrl: 'hometab.html',
transclude : true,
replace : true
};
});
})();

How do I include HTML from an attribute as a token in a directive template

I want to inject HTML as an attribute into a template and have it display within the directive that passed the attribute.
My HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.4.0-rc.0" src="https://code.angularjs.org/1.4.0-rc.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app">
<div>
<my-test mymessage='<b>Booya!</b>'></my-test>
</div>
</body>
</html>
My Javascript
app = angular.module('app', [])
.directive("myTest", function(){
return {
scope: {
mymessage: '='
},
restrict: 'E',
transclude: true,
template: '<p>Guess what...{{mymessage}}...you know it!</p>'
};
});
Here is the code in Plunker: http://plnkr.co/edit/ZJSkf1Ye4ccKURTJU8KD?p=preview
Note how it shows the literal for the binding.
I'm sure there are several problems here:
I am probably not properly binding the attribute within the directive scope
Once I have problem #1 solved I am pretty sure the HTML will be escaped.
What changes do I need to make in order to get the directive to render properly?
If you are binding HTML without Angular code (expressions and directives), then you need to use ng-bind-html:
scope: {
mymessage: "#" // no need for two-way binding
},
template: '<p>Guess what...<span ng-bind-html="mymessage"></span>...you know it!</p>'
This, on its own, would not work since it is unsafe. You have two options then:
1) include ngSanitize dependecy to your app:
angular.module("app", ["ngSanitize"])
This will automatically apply HTML sanitation - Demo
or, 2) use $sce service and call $sce.trustAsHtml on the variable holding the HTML content. This would not work, however with one-way string binding "#".

Resources