Angular and mixing jQuery UI - Why not? - angularjs

I have the following code...
<div ng-controller="CalendarCtrl">
<input type="text" ng-model="model.selectedDate" ng-change="onCalendarChange()" id="calendar" />
</div>
<script>
var app = angular.module("myApp", []);
app.controller("CalendarCtrl", function($scope) {
var currentDate = new Date();
$scope.model = { selectedDate: (currentDate.getMonth() + 1) + "/" + currentDate.getDay() + "/" + currentDate.getFullYear() };
console.log($scope.model);
$scope.onCalendarChange = function() {
console.log(currentDate);
};
});
$(document).ready(function() {
$("#calendar").datepicker();
});
</script>
This code appears to work beautifully. The change event is being called and the new selectedDate is displayed correctly.
Yet I keep seeing posts where developers are using all kinds of hoops (mainly directives) to get the datepicker to work in Angular.
Am I missing something here?

Using JQuery like this means that you are not declaratively expressing what your app does in the HTML which is kind of the point of Angular.
From the angular homepage:
AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.
Your also going to run into a lot of trouble down the road with code like
$(document).ready(function() { $("#calendar").datepicker(); });
As Angular has no idea when this has finished or what has changed. If you start using stuff like this you will need a strong understanding of how dirty checking and the digest cycle work in Angular.
Your date picker won't play nice with other directives either. For example if you put this in an ng-if and hide and show it then the date picker will not be there anymore.
Have you looked into libraries like Angular UI Bootstrap or Angular Strap. They both provide date pickers that work out of the box with Angular.

Related

Using datepicker plugin in Onsen UI 2

In my app, I want to use native datepicker and tried to use this plugin, but failed to make it work. I just recently started working with Onsen UI 2 and haven't experience with using the plugins, so I might be missing something.
I followed the steps of installing and usage of the plugin. My code so far.
index.html:
<ons-list-item ng-click="ctrl.showPicker();">
Set the time
</ons-list-item>
where 'ctrl' is my controller.
index.js
this.showPicker = function() {
var options = {
date: new Date(),
mode: 'date'
};
function onSuccess(date) {
alert('Selected date: ' + date);
}
function onError(error) { // Android only
alert('Error: ' + error);
}
var datePicker = new DatePicker();
datePicker.show(options, onSuccess, onError);
}
Unfortunetely, clicking the list item shows me this error:
ReferenceError: DatePicker is not defined
As I said, I don't have experience using the plugins in Onsen UI 2, so far I was using Cordova framework (javascript and jquery). Can anyone tell me, what can I do to make the plugin work? Thanks in advance.
Your angular code looks ok. The only problem is that you don't have a DatePicker variable.
Looking at docs of the plugin which you're using it seems that it doesn't export a DatePicker function.
In the docs they are using just datePicker.show, so maybe they are just exporting datePicker.
So basically all you need to do is remove the following line:
var datePicker = new DatePicker();
And you should be fine (if you installed your plugin correctly).

AngularJS + CSS animations to trade out inspector panes

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.

How to make ui-sref work inside JQuery loaded HTML DOM

I am new to Angular JS. What I am doing is to bind ui-sref on JQuery loaded data.
All the JQuery plugins and rest of Angular is working perfectly fine. What I have for now looks like:
app.controller("FeedController", ['$scope', '$http', '$compile', function($scope, $http, $compile) {
var feed = this;
feed.years = [];
feed.getYears = function() {
$http.get('/timeline/years').success(function(data) {
feed.years = data;
});
};
feed.getYears();
$scope.$watch('sliderWrapper', function() {
applyTreemap(); // jquery treemap layout plugin
applyKnob(); // jquery knob plugin
});
// I was trying to compile externally loaded DOM by that plugin here.
// Didn't figure out how to do it.
$scope.refresh = function() {
// #slider is main content wrapper
$compile( $("#slider").html())($scope);
};
}]);
Please don't suggest to use AngularJS instead of JQuery. Actually this is a Treemap Layout plugin and already integrated into existing website.
Okay so $compile works as in my code but there are some problems I faced. Here's one. Consider the following code.
<div id="slider">
<div ng-repeat="slide in slides">
<!-- html loaded by jquery ajax will go here -->
</div>
</div>
In angular I was doing
$compile( $("#slider").html())($scope);
So, I was compiling html of #slider in angular and it already has angular bindings besides ajax loaded content. So angular compiler will re-render them and you will run into problems.
So keep in mind that you never $compile html that already has angular bindings.
So I solved my problem by putting
href="#/path/to/state"
instead of doing
ui-sref="home.child()"
into ajax loaded conent.
Sometimes you know something and its not in your mind when you are stuck. :-D

AngularJS load tabbed content directive dynamicly

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.

Best way of loading and destroying in angular JS 1.2

I have a scenario where I need to dynamically load an Angular JS application. I have based the code on this:-
https://stackoverflow.com/a/15252490/1545858
Now, I have code that works really well with angular js 1.1.5, but in 1.2.1, no such luck.
Here is the JS code:-
$("#startMeUp").click(function() {
// Make module Foo
angular.module('Foo', []);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function($scope) {
$scope.data = {};
$scope.data.name = 'KDawg';
$scope.destroy = function() {
$scope.$destroy();
$('#Ctrl').remove();
};
$scope.$on("$destroy", function () {
console.log("EXTERMINATE");
});
});
// Load an element that uses controller Ctrl
$('<div ng-controller="Ctrl" id="Ctrl"> ' +
'<input type="text" ng-model="data.name"></input>' +
'{{data.name}}' +
'<button ng-click="destroy()">Destroy Me</button></div>').appendTo('#container');
// Bootstrap with Foo
angular.bootstrap($('#Foo'), ['Foo']);
});
And here is the HTML:-
<button id="startMeUp">Start Me Up!</button>
<div id="Foo">
<div id="container">
</div>
</div>
Now, if you start and destroy and start again with angular js 1.1.5, everything works fine, but in angular js 1.2.1 it does not work in on the second start. Any thought on how to make it work in 1.2.1?
Here is the js fiddle:-
http://jsfiddle.net/Y9wj2/
As charlietfl says, you don't need to bootstrap more than once. In fact, using angular.js 1.2.1, the error generated that breaks everything is telling you exactly that:
[ng:btstrpd] App Already Bootstrapped with this Element ''
You should think carefully about whether you really need this controller to be dynamic. If you can just use something like ng-include to load the extra content then you will have a much easier time and no need to worry about compiling the content.
If you find you really do need to take this HTML and load it from outside of angular context then you can use the $compile service. Bootstrap the app once somewhere first, preferably using ng-app and grab the injector.
var injector = angular.bootstrap($('#Foo'), ['Foo']);
or
<div id="Foo" ng-app="Foo"></div>
var injector = $('#Foo').injector();
Now you can insert the HTML however you like and then compile and link it using
injector.invoke(['$compile', '$rootScope', function($compile, $rootScope) {
$compile(insertedJqLiteNode)($rootScope);
});

Resources