I am looking a way to include asynchronously angularJS application with it's own styles and views included to JS into Polymer component. To make some kind of iframe.
If you have any ideas, suggestions or real-world examples, it would be really helpfull!
Finally, I found a solution on my own. Just needed to bootstrap angular application on the specific element to avoid conflicts in case it will run more than one application. And implemented ajax calling of the script.
Example:
<link rel="import" href="../polymer/polymer.html">
<!-- Defines element markup -->
<dom-module id="my-element">
<template>
<div id="my-app">
<ui-view></ui-view>
</div>
</template>
<!-- Registers custom element -->
<script>
Polymer({
is: 'my-element',
// Fires when an instance of the element is created
created: function() {},
// Fires when the local DOM has been fully prepared
ready: function() {
$.getScript('./app/may-ng-app.min.js');
},
// Fires when the element was inserted into the document
attached: function() {},
// Fires when the element was removed from the document
detached: function() {},
// Fires when an attribute was added, removed, or updated
attributeChanged: function(name, type) {}
});
</script>
</dom-module>
In this case, angular app bootstrapping on the my-app element. And we can deploy separately angular application, and hold it in the polymer container. It gives us flexibility, and speed download of the page.
Related
Been struggling on how to tie angular with CSS animations... I'm sure I'm not getting something fundamental.
My goal is to have a list of search results. When you click one, a detail view will slide out from under the list. When you click another, the old detail view slides back, the details for the new one load, and then the detail view slides back in.
I don't have any code to show that even remotely works, but if anyone can point me at an example or offer some basic code, I would be extremely grateful.
Thanks!
You want to use ng-animate for animations.
ng-animate essentially adds CSS classes to elements automatically (often for a short time) during, for example, state transitions, during the population of an ng-repeat, etc.
You do not add the CSS classes to elements yourself this is done by ng-animate.
You do, however, apply the styling for those class in your style-sheet.
Its up to you, in your CSS, to do something with those automatically added CSS classes. Typically, you might want to do a fade in using CSS transitions.
For example ... https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions
OR
http://www.nganimate.org/
The ng-animate link details exactly which classes are added when. There are too many to mention here.
You might also want to use ng-fx
ng-fx github is here https://github.com/AngularClass/ng-fx
ng-fx requires ng-animate
ng-fx can be seen at work on its demo page - http://hendrixer.github.io/
This should get you going :)
I would go with ng-show.
var myApp = angular.module('myApp',[]);
myApp.controller('SomeController', ['$scope', function($scope) {
//this can be a service with a ngResource, an ajax call using $http... this is a baaaaasic dummy example.
$scope.yourScopeFunction = function(){
$scope.data = '';
$scope.data = {};
$scope.data.param1 = 'Hello';
$scope.data.param2= 'Jhon';
}
}]);
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="SomeController">
<div >
<div class="elementClass" ng-click="yourScopeFunction()">Element1</div>
<div class="elementClass" ng-click="yourScopeFunction2()">Element1</div>
</div>
<div class="yourClass" ng-show="data != ''">
<div>{{data.param1}}</div>
<div>{{data.param2}}</div>
</div>
</body>
Then, when the ng-show's expression evaluates to true (the data variable has some content), Angular will attach a new class to your yourClass element called
.ng-hide-add and when it evaluates to false, Angular will add the class .ng-hide-remove.
You just have to make the transition (or animation you want) based on the classes your element will have in both those moments. Like in the documentation example.
Something like:
.yourClass.ng-hide-add{
//some transition or animation css
}
.yourClass.ng-hide-remove{
//some transition or animation css
}
Note that my function on the example does a $scope.data = ''; so the angular ng-show expression evaluates to false and the .ng-hide-remove is added.
By the way, you will need to add Angular Animations library.
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/
In a AngularJS (1.2.7) project, using UI Router (0.2.8), I want to apply the current state as a class to the ui-view element, because that would allow me to apply animations for specific states, e.g. do transition A going from login to start and do transition B going from start to settings.
At the moment I have the $state object on the $rootScope (as mentioned here, which allows me to add a state-based class on the body using ng-class="$state.current.name". However, if I add that to the ui-view element, e.g.
<div ui-view ng-class="$state.current.name"></div>
Then the class is one step behind the actual state. So when going from "login" to "start", the class will be "login" instead of "start".
I think this is a bug, you can read more about the issue here:
https://github.com/angular-ui/ui-router/issues/866
Until it is fixed I suggest such a workaround:
<div ng-class="$state.current.name"><div ui-view></div></div>
I'm reading between the lines of what I think you're trying to do: you want to style a particular view differently depending on the state/route. Here's what I'm doing currently:
1) Set a state attribute on the HTML element:
<html lang="en" ng-app="MyApp" ng-controller="MyAppCtrl" state="{{ state }}">
<div ui-view="nav"></div>
<div ui-view="page" autoscroll="true"></div>
</html>
2) Update the state name whenever the route changes:
app.controller('MyAppCtrl', function($rootScope){
$rootScope.state = 'home';
$rootScope.$on('$stateChangeSuccess', function(event, toState) {
$rootScope.state = toState.name;
});
});
3) Using attribute selectors in the stylesheets:
[state="app.articles"] [ui-view="page"] {
section {
font-size: 1.8rem;
}
}
Instead of adding $state to $rootScope I just put a method on the controller of my root directive that returns the active state name. Then I added to the DOM like so:
<ui-view active-state="{{ctrl.getActiveState()}}"></ui-view>
Then in my CSS I can use selectors like this:
[active-state="someStateName"] { ... }
If you prefer a class it would be just as easy to do this:
<ui-view class="{{ctrl.getActiveState()}}"></ui-view>
...but you would want to replace all "." characters with "-" in your getActiveState() method.
but, i has question about that : <div ng-class="$state.current.name"><div ui-view></div></div>
$state.current.name equal user.login hou to code my css?
By using
<script type="text/template" id="templateid">
<!-- Template content goes here -->
</script>
the code works well.
However if i put the template as an external file like
<script type="text/template" id="templateid" src="template.js"></script>
this wont work.
What is the difference between the above two methods and also how can i get around this issue? Or am i missing something here which maybe obvious?
Even though ive accepted another answer, i went ahead and implemented this particular requirement in a few different ways. Im posting the best and simple method that i found which i think will be useful for people who do not want to use any templating engines like marionette or so.
Using Backbone and Underscore :
Folder Structure
The folder Structure can be as follows :
Root:
├───css
├───js
│ ├───router.js
│ ├───model.js
│ ├───view.js
│ └───config.js
├───templates
│ ├───home.html
│ ├───login.html
│ └───register.html
└───images
└───index.html
Base Template
You will need to have a base template(index.html) within which you will be rendering the different templates. This will ensure that common html contents like hearder, footer, navigation menus etc are not loaded everytime a new page is rendered and thereby increasing the page loading speed drastically.
Sample structure can be as follows :
<html>
<head>
<!--Relevant CSS files-->
</head>
<body>
<div class="template_body">
<div class="header">
<!--HTML content for header-->
</div>
<div class="content" id="template">
<!--This would be where the dynamic content will be loaded-->
</div>
<div class="footer">
<!--HTML content for footer-->
</div>
</div>
<!--Include relevant JS Files-->
</body>
</html>
Note : Please note that you can decide on the structure of template as one wants. What i am using is a more general one so that everyone could relate to it easily.
View
In your view, you can render a particular template to the base template as follows :
var LoginView = Backbone.View.extend({
el: "#template",//Container div inside which we would be dynamically loading the templates
initialize: function () {
console.log('Login View Initialized');
},
render: function () {
var that = this;
//Fetching the template contents
$.get('templates/login.html', function (data) {
template = _.template(data, {});//Option to pass any dynamic values to template
that.$el.html(template);//adding the template content to the main template.
}, 'html');
}
});
var loginView = new LoginView();
Note that the el tag is very important.
To pass values to view, simply pass it like :
var data_to_passed = "Hello";
$.get('templates/login.html', function (data) {
template = _.template(data, { data_to_passed : data_to_passed }); //Option to pass any dynamic values to template
that.$el.html(template); //adding the template content to the main template.
}, 'html');
And in the template :
<%=data_to_passed;%>//Results in "Hello"
You can pass an array, object or even a variable.
Hope this has been helpful. Cheers
If you are simply trying to get the template text by using something like the $("#templateid").html() from various examples, this will work only if the text is really inline in the <script> tag.
In general, it is not possible to get the contents of a remote file using a <script> tag.
If you want to load an external template, you have to use code to explicitly get the contents (for example, using JQuery's $.get() or require.js with the text plugin).
Here is more detail on how to fetch external templates in the context of Backbone:
http://c2journal.com/2012/12/26/backbone-js-external-template-files-the-proper-way/ - pure Backbone + JQuery
http://jsdude.wordpress.com/2012/12/11/requirejs-and-backbone-template-preloading/ - using require.js and its text plugin
Be careful, however - overusing this solution will lead to a lot of additional requests to fetch templates, and a rather sluggish application as a result. In general, it is better for performance to embed the templates in the usual manner (inline in <script> tags).
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({})