How do you use trigger.io enhanceInput() with Angular.js? - angularjs

I'm building a mobile app for Android and iOS using Trigger.io. Unfortunately, on Android, the date and time text fields through the webview component do not use the native datepickers and timepickers. Trigger.io has an API call that can allow you to use the native controls, but I'm having a hard time figuring out how to make it work.
I've got it mostly working using an Angular.js directive, which I am not very familiar with. Here is my code:
if (forge.is.android())
{
app.directive("ngNative", function() {
return {
restrict: "A",
require: "?ngModel",
link: function(scope, elm, attrs, ngModel) {
elm.on('blur', function() {
scope.$apply(function() {
ngModel.$setViewValue(elm.val());
});
});
ngModel.$render = function() {
forge.logging.info("Render value: " + ngModel.$viewValue);
elm.val(ngModel.$viewValue || '');
};
ngModel.$setViewValue(elm.val());
attrs.$observe('id', function(id) {
forge.logging.info('Trying to enhance #' + id);
forge.ui.enhanceInput('#' + id);
});
}
};
});
}
The idea is with that code, I should just be able to tag a control with an ngNative attribute, and angular will call the forge.ui.enhanceInput API with the input's ID.
Anyways, this mostly works. However, the $render function doesn't seem to be getting called, and I'm not sure why. So, when the input control gets rendered, it does not populate with the model's data. It does however, update the model's data when I type something in the field.
Has anybody solved this problem already using a directive or other means? Any help would be greatly appreciated.
Thanks.

Related

How to use angular "element" parent or find methods to get access to the parent form?

In a angular js directive which is on the "form input leve" I want a reference to the parent form in order to access the "on submit" event.
Right now, I can query the form by Id and add the submit.
But, I want to achieve it with angula's "element" method.
But it just won't work.
angular.module('app.common').directive('validateField', ['$timeout', function ($timeout) {
return {
restrict: 'A',
require: '^form',
scope: {
error: '=validateField'
},
link: function(scope, el, attrs, form) {
if (document.getElementById('testing')){
var e = angular.element(document.getElementById('testing'))
e.on('submit', function() {
console.log('Works!');
})
};
el.parent('form').on('submit', function() {
console.log('How do i do this?');
})
}
};
}]);
Any ideas how?
UPDATE
I have found a solution, not sure it's an elegant one though:
// Validate field on "form submit".
angular.element(el[0].form).on('submit', function() {
validate();
});
Hmm, this looks 'wrong' to me. Your directive shouldn't need to know about some specific element outside of its own scope in this way. You would normally use some kind of service to synchronise data across components or provide the events/event handling that you need, or have your controller bring the two together.
Without seeing what you're trying to do in the event handler it's hard to know what you're trying to achieve, but it looks like you're just intending to do some validation. Can you not do this using some custom AngularJS validation?
https://docs.angularjs.org/guide/forms#custom-validation
You can use CSS selectors inside angular.element function,
angular.element('#myId'),
angular.element('.myClass')
With that said, you can abstract your directive even further when you are requiring the form controller, by using the $name property of it:
require: '^form',
link: function(scope,el,attrs,form) {
...
if(form.$name) {
var domForm = angular.element("[name=" + form.$name + "]")
domForm.addClass('whee');
}
}

Combining Polymer elements and angular

I have looked at a number of answers for binding Angular-JS scope data to Polymer components. (Use Angular-Bind-Polymer Another, And a third). It seems like this shouldn't be this hard, if the polymer components are truly just DOM. (Note that I'm using Chrome beta (36)).
I tried the Angular-Bind-Polymer suggestion, but no luck. My real interest is extending ngModel to work with Polymer so that I can use the Polymer check boxes, radio buttons, etc. For example, I tried getting paper-checkbox to work, so I tried the following, thinking that it should work:
var ngPaper = angular.module('ng-paper', []);
ngPaper.directive('paper-checkbox', function() {
console.log("Processing directive");
return {
restrict: 'E',
require: '?ngModel',
link: function(scope, element, attr, ctrl) {
console.log("Running linker");
element.on('click', function() {
scope.$apply(function() {
ctrl.$setViewValue(element[0].checked);
});
});
ctrl.$render = function() {
element[0].checked = ctrl.$viewValue;
};
ctrl.$isEmpty = function(value) {
return value != true;
};
ctrl.$formatters.push(function(value) {
return value === true;
});
ctrl.$parsers.push(function(value) {
return value ? true : false;
});
}
};
});
But no.
I then tried using angular-bind-polymer to bind the checked value on the paper-checkbox to a model attribute but didn't have any success.
I feel like if I could figure out how to get one of the form control elements to work, the others should fall quickly in line. Does anyone have a better idea on how to do this or an explanation as to why the directive I wrote isn't getting picked up and applied to the paper-checkbox?
I made this generic work-around that I use to watch changes on check-boxes and most Polymer elements from AngularJS, it's really useful while you find a more proper way, I hope it helps you.
You can also use it to manipulate Polymer Elements (E.g. Toggle).
in your HTML:
<paper-radio-group selected="firstOption">
<paper-radio-button label="First Option" id="firstOption" ng-click="change()"></paper-radio-button>
<paper-radio-button label="Second Option" id="secondOption" ng-click="change()"></paper-radio-button>
</paper-radio-group>
In the corresponding AngularJS controller, requites $scope.
var firstOption= document.querySelector('paper-radio-button[id="firstOption"]');
var secondOption= document.querySelector('paper-radio-button[id="secondOption"]');
console.log(firstOption);
console.log(secondOption);
$scope.change = function()
{
console.log(firstOption);
console.log(secondOption);
}
This way every time the user changes the selection, AngularJS will get notified so it can query the changes, you can scope the data you get back to something more specific, this is particularly useful to toggle Polymer Elements from AngularJS.
Let me know if this works for you, happy coding!

How can I act on a html document once processed by angular?

I am relatively new to Angular.
I have a html document in which angular creates a html table with ng-repeat. When this table has been built, I would like to apply to it a Jquery function. How can I do that ?
function : $("#creneaux").footable()
If I apply the function in the controller when it is instantiated, nothing happens. when I apply it in the javascript console when the page has been displayed, it works.
Firstly, I would move the $("#creneaux").footable() into a directive.
Solution:
Use $timeout without a delay to (a bit simplified) put the action at the end of the browser event queue after the rending engine:
app.directive('tableCreator', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
$("#creneaux").footable();
});
}
};
});
Demo: http://plnkr.co/edit/b05YKhipeVmrVHu2Xzsm?p=preview
Good to know:
Depending on what you need to perform, you can instead use $evalAsync:
app.directive('tableCreator', function($timeout) {
return {
link: function(scope, element, attrs) {
scope.$evalAsync(function() {
$("#creneaux").footable();
});
}
};
});
The difference is that now the code will run after the DOM has been manipulated by Angular, but before the browser re-renders.
This can in certain cases remove some flickering that might be apparent between the rendering and the call to for example the jQuery plugin when using $timeout.
In the case of FooTable, the plugin will run correctly, but the responsiveness will not kick in until the next repaint, since the correct dimensions are not available until after rendering.
Try writing a directive.
app.directive('sample', function() {
return {
restrict: 'EA',
link: function(scope, element, attrs) {
// your jquery code goes here.
},
};
});
Learn to write everything in angular instead jquery. This may help you "Thinking in AngularJS" if I have a jQuery background?

ng-ampere-debounce seems to stop working in AngularJS 1.2

I've been using the jQuery / AngularJS Directive for debouncing inputs in a Firebase-backed app. It came from Lars Gersmann's post, and was working great:
http://orangevolt.blogspot.com.au/2013/08/debounced-throttled-model-updates-for.html
Updating from Angular 1.0.8 to 1.2 seems to break things. Each time the directive fires, instead of pulling the events from the element, the $._data function brings back an undefined, resulting in this error:
TypeError: Object.keys called on non-object at Function.keys (native)
It's defined here:
var map = $._data( element[0], 'events'),
events = $.each( Object.keys( map), function( index, eventName) {
// map is undefined :(
...
}
Did something change in AngularJS, or even jQuery, that wouldn't pull the events of this element like it used to?
(Side note, I'm using jQuery version 1.8.3, which hasn't changed in the Angular upgrade).
Thanks to anyone who can shed some light on this!
You can access those events instead by using unbinds, binds and the Angular $timeout method to create a simpler debounce script. This is based off this post about blocking ngChange events.
This is a rewritten debounce directive that seems to work in Angular 1.2. It unbinds the input then applies changes with $setViewValue after a 1000ms delay. I've also added an instant change on blur. The key to making this work over the original post was setting priority.
angular.module('app', []).directive('ngDebounce', function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
priority: 99,
link: function(scope, elm, attr, ngModelCtrl) {
if (attr.type === 'radio' || attr.type === 'checkbox') return;
elm.unbind('input');
var debounce;
elm.bind('input', function() {
$timeout.cancel(debounce);
debounce = $timeout( function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(elm.val());
});
}, 1000);
});
elm.bind('blur', function() {
scope.$apply(function() {
ngModelCtrl.$setViewValue(elm.val());
});
});
}
}
});
Plus JSFiddle Demo

How to register my own event listeners in AngularJS?

How do I register my own event listeners in an AngularJS app?
To be specific, I am trying to register Drag and Drop (DND) listeners so that when something is dragged and dropped in a new location of my view, AngularJS recalculates the business logic and updates the model and then the view.
Adding an event listener would be done in the linking method of a directive. Below I've written some examples of basic directives. HOWEVER, if you wanted to use jquery-ui's .draggable() and .droppable(), what you can do is know that the elem param in the link function of each directive below is actually a jQuery object. So you could call elem.draggable() and do what you're going to do there.
Here's an example of binding dragstart in Angular with a directive:
app.directive('draggableThing', function(){
return {
restrict: 'A', //attribute only
link: function(scope, elem, attr, ctrl) {
elem.bind('dragstart', function(e) {
//do something here.
});
}
};
});
Here's how you'd use that.
<div draggable-thing>This is draggable.</div>
An example of binding drop to a div or something with Angular.
app.directive('droppableArea', function() {
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('drop', function(e) {
/* do something here */
});
}
};
});
Here's how you'd use that.
<div droppable-area>Drop stuff here</div>
I hope that helps.
Hiding event handling and dom manipulation in a directive is pretty much the the angularjs way. Calling scope.$apply when an event fires tells angular to update the view.
You might consider using jquery-ui like in this sample (see angular wiki of examples
I work with the angular-ui group and there is a simple event wrapper you might find useful.
Nice solution by Ben but keep in mind you will need to access originalEvent and original element. According to Mozilla documentation two conditions must meet https://developer.mozilla.org/en-US/docs/DragDrop/Drag_Operations
draggable is true
Listener for dragstart
So directive might look something like this
app.directive('draggableThing', function () {
return function(scope, element, attr) {
var pureJsElement = element[0];
pureJsElement.draggable = true;
element.bind('dragstart', function(event) {
event.originalEvent.dataTransfer.setData('text/plain',
'This text may be dragged');
//do something here.
});
}
});
A good step by step example is available here http://blog.parkji.co.uk/2013/08/11/native-drag-and-drop-in-angularjs.html

Resources