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!
Related
I am just getting started with angular and ran into the directive below. I read a few tutorials already and am reading some now, but I really don't understand what "require: ngModel" does, mainly because I have no idea what ngModel does overall. Now, if I am not insane, it's the same directive that provides two way binding (the whole $scope.blah = "blah blah" inside ctrl, and then {{blah}} to show 'blah blah' inside an html element controlled by directive.
That doesn't help me here. Furthermore, I don't understand what "model: '#ngModel' does. #ngModel implies a variable on the parents scope, but ngModel isn't a variable there.
tl;dr:
What does "require: ngModel" do?
What does "model : '#ngModel'" do?
*auth is a service that passes profile's dateFormat property (irrelevant to q)
Thanks in advance for any help.
angular.module('app').directive('directiveDate', function($filter, auth) {
return {
require: 'ngModel',
scope: {
model : '#ngModel',
search: '=?search'
},
restrict: 'E',
replace: true,
template: '<span>{{ search }}</span>',
link: function($scope) {
$scope.set = function () {
$scope.text = $filter('date')($scope.model, auth.profile.dateFormat );
$scope.search = $scope.text;
};
$scope.$watch( function(){ return $scope.model; }, function () {
$scope.set();
}, true );
//update if locale changes
$scope.$on('$localeChangeSuccess', function () {
$scope.set();
});
}
};
});
ngModel is an Angular directive responsible for data-binding. Through its controller, ngModelController, it's possible to create directives that render and/or update the model.
Take a look at the following code. It's a very simple numeric up and down control. Its job is to render the model and update it when the user clicks on the + and - buttons.
app.directive('numberInput', function() {
return {
require: 'ngModel',
restrict: 'E',
template: '<span></span><button>+</button><button>-</button>',
link: function(scope, element, attrs, ngModelCtrl) {
var span = element.find('span'),
plusButton = element.find('button').eq(0),
minusButton = element.find('button').eq(1);
ngModelCtrl.$render = function(value) {
updateValue();
};
plusButton.on('click', function() {
ngModelCtrl.$setViewValue(ngModelCtrl.$modelValue + 1);
updateValue();
});
minusButton.on('click', function() {
ngModelCtrl.$setViewValue(ngModelCtrl.$modelValue - 1);
updateValue();
});
function updateValue(value) {
span.html(ngModelCtrl.$modelValue);
}
}
};
});
Working Plunker
Since it interacts with the model, we can use ngModelController. To do that, we use the require option to tell Angular we want it to inject that controller into the link function as its fourth argument. Now, ngModelController has a vast API and I won't get into much detail here. All we need for this example are two methods, $render and $setViewValue, and one property, $modelValue.
$render and $setViewValue are two ways of the same road. $render is called by Angular every time the model changes elsewhere so the directive can (re)render it, and $setViewValue should be called by the directive every time the user does something that should change the model's value. And $modelValue is the current value of the model. The rest of the code is pretty much self-explanatory.
Finally, ngModelController has an arguably shortcoming: it doesn't work well with "reference" types (arrays, objects, etc). So if you have a directive that binds to, say, an array, and that array later changes (for instance, an item is added), Angular won't call $render and the directive won't know it should update the model representation. The same is true if your directive adds/removes an item to/from the array and call $setViewValue: Angular won't update the model because it'll think nothing has changed (although the array's content has changed, its reference remains the same).
This should get you started. I suggest that you read the ngModelController documentation and the official guide on directives so you can understand better how this all works.
P.S: The directive you have posted above isn't using ngModelController at all, so the require: 'ngModel' line is useless. It's simply accessing the ng-model attribute to get its value.
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');
}
}
Using angular I have a set of input elements that become present in the dom after a certain user action, i.e I use ng-if to determine if it should be present or not. I would like the first of these input elements to gain focus and for the text in it to be selected to that the user easily can change all the content in that element.
Searching the web I have seen several posts that say I should use either the focus or select method or both but I haven't been able to get the desired result. Here is a directive I created:
app.directive('selectMe', function() {
return function(scope, element) {
element[0].focus();
element[0].select();
};
})
And here is a plunkr demo. Can anyone tell me why it is not working?
Because when your directive is initializing, their values isn't attached yet, therefore you should set small $timeout.
It works, you can try in plnkr
app.directive('selectMe', function($timeout) {
return function(scope, element) {
$timeout(function() {
var ele = element[0];
ele.focus();
ele.select();
}, 500)
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.
I really love how the new ng-click directive in Angular now automatically includes functionality for touch events. However, I am wondering if it is possible to access that touch-event service from my custom directive? I have lots of directives that require that I bind a click event to the given element, but I'm simply doing that using the typical jquery syntax (ex: element.on('click', function(){ ... })). Is there a way that I can bind an ng-click event to an element within a directive? Without having to manually put a ng-click tag on my element in the HTML of my view...?
I want to be able to harness the power of both click and touch events. I could obviously import a library (such as HammerJS or QuoJS) but I would prefer not to have to do that, especially since Angular is already doing it.
I can access the $swipe service and bind different elements to that, but is there a similar service for ngTouch?
For reference, this is an example of when I would want to do this:
mod.directive('datepicker', ['$timeout', function($timeout){
return {
link: function(scope, elem, attrs){
var picker = new DatePicker();
elem.on('click', function(e){
picker.show();
});
// I would rather do something like:
// elem.on('ngTouch', function(){ ... });
//
// or even:
// $ngTouch.bind(elem, {'click': ..., 'touch': ...});
}
}
}]);
UPDATE: As noted by below, the source code for the ng-click directive is here. Can anyone see a way to harness that code and turn it into a "bindable" service?
I don't think that's quite the right approach. I'd approach this by using a template within your directive and then using ngTouch within that.
mod.directive('datepicker', ['$timeout', function ($timeout) {
return {
template: '<div ng-touch="doSomethingUseful()"></div>',
link: function (scope, elem, attrs) {
var picker = new DatePicker();
scope.doSomethingUseful = function () {
// Your code.
}
}
}
}]);
UPDATE
Full example with additional attributes on the directive element:
http://codepen.io/ed_conolly/pen/qJDcr