I am new to angularjs, and I have an jquery background.
I want to compile json from the server into an element with an template.
What I now have for so far is:
The template:
<script type="text/ng-template" id="/tiles.html">
<div ng-repeat="tile in tiles">
{{tile.name}}<img ng-src="tile.src" />
</div>
</script>
The button for displaying the content:
<button ng-click="imageOptions.addFromList()">+ Add Image from list</button>
The function:
$scope.imageOptions.addFromList = function (){
$http
.get('/json/Tiles/get')
.success(function(data){
$scope.tiles = data;
console.log(data);
})
.error(function(data){
console.log("something did go wrong");
});
$(".prompt").html('<div ng-include src="/tiles.html"></div>');
};
The placeholder:
<div class="prompt"></div>
The placeholder will be used many times with also other content.
So I can not just type the html from the .html() argument. Like this:
<div class="prompt"><div ng-include src="/tiles.html"></div></div>
When I inspect the .prompt div it will stay uncompiled
The first thing you should do is remove jQuery library from your app while you get familiar with angular methodology.
There is no need to use html() method when all you need to do is include your template through a variety of different ways in your html source.
If the data isn't already available for ng-repeat it will simply fail quietly and do nothing. Then when the data is available it will respond automatically.
You could simply do:
<div class="prompt" ng-include src="/tiles.html"></div>
Or you could make a simple directive that will accomplish the same thing .
app.directive('prompt', function() {
return {
restrict: 'C',/* use for "class" */
templateUrl: '/tiles.html'
}
});
Simply change this
<div ng-include src="/tiles.html">
to this
<div ng-include src="'/tiles.html'">
While coding your single page application in angularjs, ideally there should not be any need for you to first get a reference to an element and then perform some action on it (You may think of this as the first step of switching from a jquery background to angularjs domain).
To achieve complete separation of model, view and controller you should just define your templates and controllers accordingly. These mappings and references should be managed by angularjs on its own.
As correctly mentioned above you should not be using .html() method of jquery. If you have included jquery in your document, it will be internally used by angularjs, but, including jquery should not be mandatory for using angularjs.
ng-repeat and ng-include also create a separate scope, so you may want to take care of those as well in future.
For your query, you may reference the template by including extra quotes in ng-include as:
<div class="prompt">
<div ng-include src="'tiles.html'"></div>
</div>
http://jsfiddle.net/PKKp8/
Related
I'm a bit new in angular.js and I wonder how to proper implement fullpage.js into angular.js. I tried to add fullpage as example ...
this code direct into controller i wanted to use for fullpage.js
$('#fullpage').fullpage({
sectionsColor: ['#21f7ff', '#bdcc4b', '#fcae49'],
});
but the problem is when I put this code into controller i wont recongnize nothing else... ( other functions for click)
my exmaple of implementation in controller :
'use strict';
angular.module('APP')
.controller('MainCtrl', function ($scope,Auth) {
$('#fullpage').fullpage({
sectionsColor: ['#21f7ff', '#bdcc4b', '#fcae49'],
});
$scope.moveUp = function(){
$.fn.fullpage.moveSectionUp();
};
$scope.moveDown = function(){
$.fn.fullpage.moveSectionDown();
};
});
view :
<div id="fullpage">
<div class="section">
<button class="button" ng-click="moveDown()">Down</button>
</div>
<div class="section">
<button class="button" ng-click="moveUp()"> Up </button>
</div>
</div>
I'm using angular-fullastack-generator for project start so this is a partial view.
My general question is how to proper implement fullpage.js into angular.js ?
Update Nov 2018:
Now you can make use of the official fullPage.js wrapper for Angular.
If you take a look at fullPage.js FAQs you will find this:
My javascript/jQuery events don't work anymore when using fullPage.js
Short answer: if you are using options such as verticalCentered:true or overflowScroll:true in fullPage.js initialization, then you will to treat your selectors as dynamic elements and use delegation. (by using things such as on from jQuery). Another option is to add your code in the afterRender callback of fullpage.js
Explanation: if you are using options such as verticalCentered:true or overflowScroll:true of fullPage.js, your content will be wrapped inside other elements changing its position in the DOM structure of the site. This way, your content would be consider as "dynamically added content" and most plugins need the content to be originally on the site to perform their tasks. Using the afterRender callback to initialize your plugins, fullPage.js makes sure to initialize them only when fullPage.js has stopped changing the DOM structure of the site.
Which means you would need to use delegation for your events (such as click) or add them in the afterRender callback.
Consider the following:
<script type="text/ng-template" id="myTemplateName">
{{item.SomeProperty}}
<script>
<div ng-repeat="container in List">
<div ng-repeat="item in container.Items">
<!-- CASE 1 -->
<div ng-include="'myTemplateName'"></div>
</div>
<!-- CASE 2 -->
<div ng-include="'myTemplateName'" />
</div>
The code above works in case1, but not in case2: case 1 will work because the template uses item, which is made available by the ng-repeat statement outside of the template, case 2 doesn't work because there is no item, instead i want it to use container.SomeProperty.
Maybe i am misusing angular includes, but i wanted to use them like partials in ASP.Net MVC. There, you can define a partial and you are able to pass in a model.
Is there any way in angular that allows me to set what item means inside the template?
Problem solved using Matt's answer:
module.controller("ItemController", ['$scope', function ($scope) {
$scope.templateitem = ($scope.$parent.item) ? $scope.$parent.item : $scope.$parent.$parent.container.Item;
}]);
Still, it feels kind of dirty: the controller needs to know how it can be used. It would be better if i could pass this to the controller from the outside.
A better approach:
I didnt really like the solution above, because the controller needs to know how it will be used, so i used a directive:
app.directive("opportunity", function () {
return {
restrict: "E",
templateUrl: "opportunityTemplate",
scope: { templateitem: "=model" }
};
});
In view:
<script type="text/ng-template" id="opportunityTemplate">
{{templateitem.SomeProperty}}
</script>
<opportunity model="container.Item"></opportunity>
<opportunity model="somethingElse.Item"></opportunity>
Now all i need to find out is how i can pass the templatename into the directive, and i can make a re-usable "partial" directive (please tell me if i am reinventing the wheel here?)
What you need to use is ng-controller, or some routing system like ui-router that connects partials to controllers through route definitions.
For the first example, when you add your partial, also specify a controller which takes care of dealing with the model:
HTML:
<div ng-controller="MyCtrl" ng-include="'myTemplateName'" />
<!-- inside the template -->
<div>{{item.somekey}}</div>
Controller:
angular.module('myapp.ctrl', [])
.controller('MyCtrl', ['$scope', function($scope){
$scope.item = { somekey: 'somevalue' };
}]);
In the second example, use a routing system like ui-router. An example of that can be seen here, where certain partials are attached to controllers and specific urls:
https://github.com/angular-ui/ui-router/tree/gh-pages/sample
I have a tabbed navigtion in my webapp that looks like this
Now I want to Change the directive each time the user clicks on one of the Navigation points. My Idea was to init the page with the first template.
$scope.currentDirective = $compile('<div order-Sale></div>');
Then when the user clicks on a tab, I wanted to change and compile the content again with a new directive in it. But for some reason this is not working. How would you proceed in order to archive this dynamic content loading? I really want to only load the content on necessary need and not just to show or hide it. I think using directives is the right way to go for it, but I'm a but stuck at the implementation... Someone any pointer ? (I don't want to use any jQuery)
What I tried [Edit]:
The controller.js
app.controller('pageController',['$scope','$compile', function($scope, $compile){
var templates = ['<div first-template></div>','<div second-template></div>'];
$scope.currentTemplate = $compile(templates[0]);
$scope.changeTemplate = function(id) {
$scope.currentTemplate = $compile(templates[id]);
};
}]);
The HTML
<div ng-controller="pageController">
<li>
<a ng-click="changeTemplate('1')">Change Template</a>
</li>
{{currentTemplate}}
</div>
UPDATE
$compile returns a linking function not a value, you cannot just bind it to your template with interpolation.
You should use ngBindHtml instead of regular bindings ( ngBind & {{ }} ).
ngBindHtml does compiling, linking and watching all out-of-the-box.
With ng-bind-html-unsafe removed, how do I inject HTML?
Here is a plunker
app.controller('pageController',['$scope','$compile','$sce', function($scope, $compile, $sce){
var templates = ['<div>first-template</div>','<div>second-template</div>'];
$scope.currentTemplate = $sce.trustAsHtml(templates[0]);
$scope.changeTemplate = function(id) {
$scope.currentTemplate = $sce.trustAsHtml(templates[id]);
};
}]);
The markup:
<div ng-controller="pageController">
<button ng-click="changeTemplate('1')">Change Template</button>
<div ng-bind-html="currentTemplate"></div>
</div>
For more robust dynamic content loading you have two good alternatives:
ngRoute from angular team.
ui-router from angular-ui team.
If you want to change and compile the content again, well that's exactly what ng-view/ ui-view directives already do for you.
Why not just use a directive:
You probably need to load a different template (html partial) for each tab.
You probably need to change the url based on the tab (and vice versa)
You probably need to instantiate a different controller for each tab.
ngRoute and ui-router come with their own directives.
You can implement your own route module if you want but that's more than just a directive.
I am trying to render a piece of html, available on a dynamic route, the route is fetched via a $http.get() call, it returns a piece of html,
Just to give an example I try to load this html partial:
<h1>{{ pagetitle }}</h1>
this is a simple page example
I made a small fiddle, to mock the problem, but for the simplicity i left the http call out, and just added the html in a string on the scope.
The controller is:
function Ctrl($scope) {
$scope.data = {
view: "<h1>whaaaaa</h1>"
};
}
The page html is this:
<div ng-app="">
<div ng-controller="Ctrl">
<div ng-include src="data.view"></div>
</div>
</div>
The problem is that it does not add the string into the html file (ng-include), but it makes a http call to a url made of that string aparently.
So Is it not possible to just enter a string into an include? if not, what is the proper way to make an http call to a dynamic url, and enter the returned url into the page.
You can play with it in JSFiddle.
You can add static hooks to the template cache, so let's say you have a service that gets the template:
myApp.service('myTemplateService', ['$http', '$templateCache', function ($templateCache) {
$http(/* ... */).then(function (result) {
$templateCache.put('my-dynamic-template', result);
});
/* ... */
}]);
Then, you can do:
<div include src=" 'my-dynamic-template' "></div>
NOTE reference name must be wrapped in single quotes, this always bytes me.... NOTE
Do note that you'll have to have assigned it to the template cache before angular tries to resolve the url.
EDIT:
If the dynamic URL logic is simple enough, you can also use a conditional in the ng-include, e.g.
<div ng-include="isTrue() && 'template1.html' || 'template2.html'"
You can't do that with ng-include; reference:
ngInclude|src – {string} – angular expression evaluating to URL. If
the source is a string constant, make sure you wrap it in quotes, e.g.
src="'myPartialTemplate.html'".
Use directly ng-bind-html-unsafe like this:
<div ng-bind-html-unsafe="data.view"></div>
Demo
Creates a binding that will innerHTML the result of evaluating the
expression into the current element. The innerHTML-ed content will not
be sanitized! You should use this directive only if ngBindHtml
directive is too restrictive and when you absolutely trust the source
of the content you are binding to.
You can use ng-bind-html-unsafe
If you do not trust the html which comes from your ajax request, you can also use ng-bind-html which needs the $sanitize service to work (DEMO).
<div ng-bind-html="data.view"></div>
To use that you need to include an extra js file .
angular.module('app', ['ngSanitize']);
And of course add the app to ng-app:
<div ng-app="app">
ng-include expects a URL to an external HTML fragment, not an HTML string.
You want ng-bind-html-unsafe:
Updated demo.
I have a working Angular.js app with HTML5 mode enabled.
$location.Html5mode(true).hashbang("!");
What I want to achieve is to get some URLs or <a> tags to do the normal browsing behaviour instead of changing the URL in the address bar using HTML5 history API and handling it using Angular controllers.
I have this links:
<a href='/auth/facebook'>Sign in with Facebook</a>
<a href='/auth/twitter'>Sign in with Twitter</a>
<a href='/auth/...'>Sign in with ...</a>
And I want the browser to redirect the user to /auth/... so the user will be then redirected to an authentication service.
Is there any way I can do this?
Adding target="_self" works in Angular 1.0.1:
<a target="_self" href='/auth/facebook'>Sign in with Facebook</a>
This feature is documented (https://docs.angularjs.org/guide/$location - search for '_self')
If you're curious, look at the angular source (line 5365 # v1.0.1). The click hijacking only happens if !elm.attr('target') is true.
An alternative to Fran6co's method is to disable the 'rewriteLinks' option in the $locationProvider:
$locationProvider.html5Mode({
enabled: true,
rewriteLinks: false
});
This will accomplish exactly the same thing as calling $rootElement.off('click'), but will not interfere with other javascript that handles click events on your app's root element.
See docs, and relevant source
This is the code for turning off deep linking all together. It disables the click event handler from the rootElement.
angular.module('myApp', [])
.run(['$location', '$rootElement', function ($location, $rootElement) {
$rootElement.off('click');
}]);
To work off the Nik's answer, if you have lots of links and don't want to add targets to each one of them, you can use a directive:
Module.directive('a', function () {
return {
restrict: 'E',
link: function(scope, element, attrs) {
element.attr("target", "_self");
}
};
});
I've run into the same issue a few times now with angular, and while I've come up with two functional solutions, both feel like hacks and not very "angular".
Hack #1:
Bind a window.location refresh to the link's click event.
<a
href=/external/link.html
onclick="window.location = 'http://example.com/external/link.html';"
>
The downside and problems with this approach are fairly obvious.
Hack #2
Setup Angular $routes that perform a $window.location change.
// Route
.when('/external', {
templateUrl: 'path/to/dummy/template',
controller: 'external'
})
// Controller
.controller('external', ['$window', function ($window) {
$window.location = 'http://www.google.com';
}])
I imagine that you could extend this using $routeParams or query strings to have one controller handle all "external" links.
As I said, neither of these solutions are very satisfactory, but if you must get this working in the short term, they might help.
On a side note, I would really like to see Angular support rel=external for this type of functionality, much like jQueryMobile uses it to disable ajax page loading.
To add to Dragonfly's answer, a best practice I have found to limit the number of target="_self" attributes is to never put the ng-app attribute on the body tag. By doing that you are telling angular that everything within the body tags are a part of the angular app.
If you are working within a static wrapper that should not be affected by angular, put your ng-app attribute on a div (or other element) that surrounds only the location your angular app is going to be working in. This way you will only have to put the target='_self' attribute on links that will be children of the ng-app element.
<body>
... top markup ...
<div ng-app="myApp">
<div ng-view></div>
</div>
... bottom markup ...
</body>
In your routes try:
$routeProvider.otherwise({})