I implemented a directive including plangular experiment
Here the code:
as.directive('plangular', function ($document, $rootScope, $http) {
// Define the audio engine
var audio = $document[0].createElement('audio');
// Define the player object
var player = {
track: false,
playing: false,
paused: false,
tracks: null,
i: null,
play: function(tracks, i) {
if (i == null) {
tracks = new Array(tracks);
i = 0;
};
player.tracks = tracks;
player.track = tracks[i];
player.i = i;
if (player.paused != player.track) audio.src = player.track.stream_url + '?client_id=' + clientID;
audio.play();
player.playing = player.track;
player.paused = false;
},
pause: function() {
audio.pause();
if (player.playing) {
player.paused = player.playing;
player.playing = false;
};
},
// Functions for playlists (i.e. sets)
playPlaylist: function(playlist) {
if (player.tracks == playlist.tracks && player.paused) player.play(player.tracks, player.i);
else player.play(playlist.tracks, 0);
},
next: function(playlist) {
if (!playlist){
if (player.i+1 < player.tracks.length) {
player.i++;
player.play(player.tracks, player.i);
} else {
player.pause();
};
} else if (playlist && playlist.tracks == player.tracks) {
if (player.i + 1 < player.tracks.length) {
player.i++;
player.play(playlist.tracks, player.i);
} else {
player.pause();
};
};
},
previous: function(playlist) {
if (playlist.tracks == player.tracks && player.i > 0) {
player.i = player.i - 1;
player.play(playlist.tracks, player.i);
};
}
};
audio.addEventListener('ended', function() {
$rootScope.$apply(function(){
if (player.tracks.length > 0) player.next();
else player.pause();
});
}, false);
// Returns the player, audio, track, and other objects
return {
restrict: 'A',
scope: true,
link: function (scope, elem, attrs) {
// RESOLVE THE AUDIO
var params = { url: attrs.src, client_id: clientID, callback: 'JSON_CALLBACK' }
$http.jsonp('//api.soundcloud.com/resolve.json', { params: params }).success(function(data){
// Handle playlists (i.e. sets)
if (data.tracks) scope.playlist = data;
// Handle single track
else if (data.kind == 'track') scope.track = data;
// Handle all other data
else scope.data = data;
});
scope.player = player;
scope.audio = audio;
scope.currentTime = 0;
scope.duration = 0;
// Updates the currentTime and duration for the audio
audio.addEventListener('timeupdate', function() {
if (scope.track == player.track || (scope.playlist && scope.playlist.tracks == player.tracks)){
scope.$apply(function() {
scope.currentTime = (audio.currentTime * 1000).toFixed();
scope.duration = (audio.duration * 1000).toFixed();
});
};
}, false);
// Handle click events for seeking
scope.seekTo = function($event){
var xpos = $event.offsetX / $event.target.offsetWidth;
audio.currentTime = (xpos * audio.duration);
};
}
}
});
And here how I used it in my index.html view.
<section plangular data-src="https://soundcloud.com/a-beautiful-place-1/the-stoners-guide-to-the-galaxy" class="measure-wide wrap mb4">
<div>
<div class="controllButton">
<a href ng-click="player.play(track)" ng-hide="player.playing == track" class="button button-geo anim-popin">
<div class=""><img src="lib/player_js/icons/play.png" /></div>
</a>
<a href="" ng-click="player.pause()" ng-show="player.playing == track" class="button button-geo anim-popin">
<div> <img src="lib/player_js/icons/pause.png" /> </div>
</a>
</div>
<div class="controllScrobbler">
<div class="absolute t0 r0 b0 l0 seekHolder" >
<div class=" t0 r0 b0 l0 overlaySeeker" ng-click="seekTo($event)"></div>
<div class=" t0 b0 l0 seeker" style="width: {{ (currentTime / duration) * 100 }}%"></div>
<div class=" t0 r0 b0 l0 bg-white"></div>
</div>
</div>
</div>
</section>
Now, it works perfectly. What I would like to do is to interact with the object player within the directive from different controllers. The controllers are loaded in the partials of different pages. There is no scope sharing between the directive and the other controllers.
I'm looking to something like scope.plangular.player.MyFunctionInsideTheDirective(my args)
Is it possible? Something like the directive is a service, and the controllers are interact with that.
Any ideas?
Related
For some reason my sorted method here in conjunction with ratelimit is causing an endless loop. I am not sure why it won't stop being hit. If I remove the ratelimit or the sorted there is no endless loop, but if I use both it won't stop.
function Unscheduled() {
var self = this;
self.games = ko.observableArray([]).extend({
sorted: function(l, r) {
return l.length > r.length ? -1 : 1;
},
rateLimit: { timeout: 0, method: "notifyWhenChangesStop" }});
self.changes = ko.observable(0);
self.games.subscribe(function (changes) {
console.log('Array Hit');
self.changes(self.changes()+1);
}, null, "arrayChange");
self.games.push('test');
self.addRemove = function() {
self.games.remove('test1');
self.games.push('test');
}
}
ko.extenders.sorted = function (obs, sortFunction) {
ko.computed(function () {
console.log('sorting');
obs.sort(sortFunction)();
});
};
ko.applyBindings(new Unscheduled());
<script src="https://exposureevents.com/scripts/knockout-3.5.0.min.js"></script>
<div data-bind="text: changes">
</div>
<button data-bind="click: addRemove">
Add/Remove
</button>
Seems that the issue is related to sort method of observableArray. I guess when using that method it generates a change notification then it tries to sort again and goes in this forever loop. When using sort on "raw" array there is no such behavior:
function Unscheduled() {
var self = this;
self.games = ko.observableArray([]).extend({
sorted: function(l, r) {
// return l.length > r.length ? -1 : 1;
return l - r;
},
rateLimit: { timeout: 0, method: "notifyWhenChangesStop" }});
self.changes = ko.observable(0);
self.games.subscribe(function (changes) {
console.log('Array Hit');
self.changes(self.changes()+1);
}, null, "arrayChange");
// self.games.push('test');
self.games.push(0);
self.addRemove = function() {
// self.games.remove('test1');
// self.games.push('test');
self.games.push(Math.random() * 10000 | 0);
}
self.gamesText = ko.pureComputed(() => self.games.reversed().join('\n'));
}
ko.extenders.sorted = function (obs, sortFunction) {
ko.computed(function () {
console.log('sorting');
// obs.sort(sortFunction)();
obs().sort(sortFunction);
});
};
ko.applyBindings(new Unscheduled());
<script src="https://exposureevents.com/scripts/knockout-3.5.0.min.js"></script>
<div data-bind="text: changes">
</div>
<button data-bind="click: addRemove">
Add/Remove
</button>
<pre data-bind="text: gamesText"></pre>
Here is my problem
I am trying to create a button that calls a directive function which then activates the google place 'place_changed' event that is assigned to the directive. If the getPlace() function doesn't return a result e.g. var result = scope.gPlace.getPlace(); then I want force a place prediction by doing the following.
if( result === undefined ) {
result = { name: element.val() }
}
however the problem is that this code will work when the page is first loaded but subsequent attempts will assign the var result to the previous text that was entered. e.g. Type "Adelaide" and click on the button equals successful process however now type melbourne and click on the button will still equal "Adelaide"
'use strict';
angular.module( "ngAutocomplete", [])
.directive('ngAutocomplete', function() {
return {
require: 'ngModel',
scope: {
ngModel: '=',
options: '=?',
details: '=?',
setFn: '&'
},
link: function(scope, element, attrs, controller) {
//options for autocomplete
var opts
var watchEnter = false
//convert options provided to opts
var initOpts = function() {
opts = {}
if (scope.options) {
if (scope.options.watchEnter !== true) {
watchEnter = false
} else {
watchEnter = true
}
if (scope.options.types) {
opts.types = []
opts.types.push(scope.options.types)
scope.gPlace.setTypes(opts.types)
} else {
scope.gPlace.setTypes([])
}
if (scope.options.bounds) {
opts.bounds = scope.options.bounds
scope.gPlace.setBounds(opts.bounds)
} else {
scope.gPlace.setBounds(null)
}
if (scope.options.country) {
opts.componentRestrictions = {
country: scope.options.country
}
scope.gPlace.setComponentRestrictions(opts.componentRestrictions)
} else {
scope.gPlace.setComponentRestrictions(null)
}
}
}
if (scope.gPlace == undefined) {
scope.gPlace = new google.maps.places.Autocomplete(element[0], {});
}
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
var result = scope.gPlace.getPlace();
//hack to make sure we have an object to pass to ensure we can get results from the called function activateGetPlace
if( result === undefined ) {
result = { name: element.val() }
}
console.log("the result", result);
if (result !== undefined) {
if (result.address_components !== undefined) {
scope.$apply(function() {
scope.details = result;
controller.$setViewValue(element.val());
});
}
else {
if (watchEnter) {
getPlace(result)
}
}
}
})
//function to get retrieve the autocompletes first result using the AutocompleteService
var getPlace = function(result) {
var autocompleteService = new google.maps.places.AutocompleteService();
if (result.name.length > 0){
autocompleteService.getPlacePredictions(
{
input: result.name,
offset: result.name.length,
types: opts.types,
componentRestrictions: opts.componentRestrictions
},
function listentoresult(list, status) {
if(list == null || list.length == 0) {
scope.$apply(function() {
scope.details = null;
});
} else {
var placesService = new google.maps.places.PlacesService(element[0]);
placesService.getDetails(
{'reference': list[0].reference},
function detailsresult(detailsResult, placesServiceStatus) {
if (placesServiceStatus == google.maps.GeocoderStatus.OK) {
scope.$apply(function() {
controller.$setViewValue(detailsResult.formatted_address);
element.val(detailsResult.formatted_address);
scope.details = detailsResult;
//on focusout the value reverts, need to set it again.
var watchFocusOut = element.on('focusout', function(event) {
element.val(detailsResult.formatted_address);
element.unbind('focusout')
})
});
}
}
);
}
});
}
}
controller.$render = function () {
var location = controller.$viewValue;
element.val(location);
};
//watch options provided to directive
scope.watchOptions = function () {
return scope.options
};
scope.$watch(scope.watchOptions, function () {
initOpts()
}, true);
scope.activateGetPlace = function() {
google.maps.event.trigger(scope.gPlace, 'place_changed');
}
scope.setFn({theDirFn: scope.activateGetPlace});
}
};
});
var mechanicsearch = angular.module('mechanicsearch', ['ngRoute','ngResource','ngAutocomplete']),
radiusOptions = [];
mechanicsearch.run(function($rootScope) {
$rootScope.$on('handleActiveJobsPanel', function(event, args) {
$rootScope.$broadcast('activateJobsPanel', args);
});
$rootScope.$on('handleActiveFinalise', function(event, args) {
$rootScope.$broadcast('activateFinalisePanel', args);
});
$rootScope.$on('handleActiveSearch', function(event, args) {
$rootScope.$broadcast('activateSearchPanel', args);
});
});
mechanicsearch.filter('htmlToPlaintext', function() {
return function(text) {
return text ? String(text).replace(/<[^>]+>/gm, '') : '';
};
});
// mechFactory service
mechanicsearch.factory('mechFactory', function($resource,$window) {
var mechanics = [];
var jobs = [];
var addMechanic = function(mechanic){
mechanics.push(mechanic);
};
var getAllMechanics = function(){
return mechanics;
};
var removeAllMechanics = function() {
mechanics = [];
}
var addJob = function(job) {
jobs.push(job);
}
var getAllJobs = function() {
return jobs;
}
var removeAllJobs = function() {
jobs = [];
}
return {
getMechanics: function(location,radius) {
return $resource('/ajax/api.cfm?api=mechanic&function=getMechanicByLocation&lat=:lat&lng=:lng&radius=:radius' ).get({lat:location.lat,lng:location.lng,radius:radius});
},
getJobs: function() {
return $resource('/ajax/api.cfm?api=job&function=getJobsAssignedtoWorkshop' ).get();
},
sendMechanicsJobNotifications: function(mechanics, jobs) {
return $resource('/ajax/api.cfm?api=job&function=sendMechanicsJobNotifications&mechanics=:mechanics&jobs=:jobs' ).get({mechanics:mechanics.toString(),jobs:jobs.toString()});
},
addMechanic: addMechanic,
removeAllMechanics: removeAllMechanics,
getAllMechanics: getAllMechanics,
addJob: addJob,
removeAllJobs: removeAllJobs,
getAllJobs: getAllJobs
}
});
mechanicsearch.controller('SearchCtrl', ['$timeout', '$scope', '$window', '$location', '$routeParams', 'filterFilter', 'mechFactory', '$resource', '$element',
function ($timeout, $scope, $window, $location, $routeParams, filterFilter, mechFactory, $resource, $element) {
$scope.place = {};
$scope.place.address = null;
$scope.place.lat = null;
$scope.place.lng = null;
$scope.radius = 25;
$scope.mechanics = [];
$scope.selection = [];
$scope.alert = null;
$scope.showSearchPanel = true;
//Helper method to get selected mechanics
$scope.selectedMechanics = function selectedMechanics() {
filterFilter($scope.mechanics, { selected: true })
};
//allow mechanic checkbox to select/deselect on click
$scope.toggleMechanicSelect = function(mechanic) {
mechanic.selected = !mechanic.selected;
}
$scope.goToJobListing = function() {
$scope.showSearchPanel = false;
mechFactory.removeAllMechanics();
for( var i in $scope.selection ) {
mechFactory.addMechanic($scope.selection[i]);
}
$scope.$emit('handleActiveJobsPanel');
}
// watch mechanics for changes
$scope.$watch('mechanics|filter:{selected:true}', function (nv) {
$scope.selection = nv.map(function (mechanic) {
return mechanic.objectid;
});
}, true);
//watch the returning google autocomplete details object
$scope.$watch('details', function() {
if( $scope.details !== undefined && $scope.details !== null ) {
$scope.place.address = $scope.details.formatted_address;
$scope.place.lat = $scope.details.geometry.location.lat();
$scope.place.lng = $scope.details.geometry.location.lng();
}
});
// watch the $scope.place data for changes
$scope.$watchCollection('place', function() {
if( $scope.place.lat !== null || $scope.place.lng !== null ) {
$scope.getMechanics();
}
});
$scope.$watch('radius', function() {
if( Number.isInteger(parseInt($scope.radius)) ){
$scope.getMechanics();
}
});
$scope.setDirectiveFn = function(directiveFn) {
$scope.directiveFn = directiveFn;
};
$scope.getMechanics = function() {
mechFactory.getMechanics($scope.place, $scope.radius).$promise.then(
function successfulResult (mechanicsData) {
if (!mechanicsData || !mechanicsData.data.length){
$scope.alert = 'Sorry, no mechanic found in "' + $scope.place.address + '" with radius of ' + $scope.radius + '.';
$scope.mechanics = [];
$scope.selection = [];
} else {
$scope.alert = mechanicsData.data.length + ' mechanic(s) found in "' + $scope.place.address + '" with radius of ' + $scope.radius + ' km.';
$scope.mechanics = mechanicsData.data;
$scope.selection = [];
}
}, function failedResult (err) {
$scope.alert = err.message;
});
};
//display panel once we have recieved the event
$scope.$on('activateSearchPanel', function(event, args) {
$scope.mechanics = [];
$scope.selection = [];
$scope.alert = null;
$scope.showSearchPanel = true;
$scope.place = {};
$scope.radius = 25;
});
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-route.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-resource.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCmN0htBqG3DGo04KKKzC9srgIrhP0Dq5o&libraries=places"></script>
<div id="mechanicsearch" data-ng-app="mechanicsearch">
<div data-ng-controller="SearchCtrl" ng-show="showSearchPanel">
<aside class="workshopsearch" >
<form method="post" class="form-inline" role="form">
<div class="row">
<div class="col-sm-6 form-group input-group-lg">
<input type="text" id="geoSearch" ng-model="autocomplete" class="form-control" ng-autocomplete options="{ types: 'geocode', country: 'au', watchEnter: true }" details="details" set-fn="setDirectiveFn(theDirFn)" />
</div>
<div class="col-sm-6 input-group input-group-lg">
<input type="text" class="form-control" name="radius" id="radius" placeholder="Radius" data-ng-model="radius">
<span class="input-group-btn"><button class="btn btn-go" ng-click="directiveFn()">Go</button</span>
</div>
</div>
</form>
</aside>
</div>
</div>
}
Why are you using element.val() when you have an ngModel bound to the input? Why not use scope.ngModel instead?
I have implemented a little script to close magnific popup when a vimeo video finish using froogaloop,
this is my code:
var SlampLightbox = (function(undefined){
var mp; //store Magnific Popup global object
var mp_exixts = function(){
if( $.fn.magnificPopup ){
mp = $.magnificPopup.instance
return true;
}else{
return false;
}
}
var open_from_hash = function(){
var hash = window.location.hash.slice(1); //cache hash
if( hash.length > 1 && hash != '#!'){
var mark_pos = hash.indexOf('?');
if( mark_pos != -1)
hash = hash.substring(0, mark_pos);
var selector = 'a[name='+hash+']';
$(selector).click(); //trigger click event on the element to open magnificPopup
}
}
var open = function($element){
$element.magnificPopup({
delegate: 'a',
type: 'iframe',
tLoading: '',
iframe: {
markup: '<div class="slamp-mfp-iframe-scaler">'+
'<button title="Close (Esc)" type="button" class="slamp-mfp-close">x</button>'+
'<iframe id="vimeoplayer" class="mfp-iframe" frameborder="0" allowfullscreen></iframe>'+
'</div>', // HTML markup of popup, `mfp-close` will be replaced by the close button
patterns: {
vimeo:{
index: 'vimeo.com/',
id: '/',
src: '//player.vimeo.com/video/%id%?autoplay=1&api=1&player_id=vimeoplayer'
}
}
},
callbacks: {
markupParse: function(template, values, item) {
_attachVideoEvent(template, values, item);
}
}
})
}
var _close = function(){
mp.close();
}
var _attachVideoEvent = function(template, values, item){
var playerOrigin = '*';
var player = $f( template.find("iframe")[0] );
if( player.length == 0 )
return;
var onFinish = function(){
_close();
}
player.addEvent('ready', function() {
player.addEvent('finish', onFinish);
});
}
return {
init: function($element){//input must be a jQuery object
if( mp_exixts() ){
open($element);
if( $element.length == 0)
return;
open_from_hash(); //open a video specified in the hash, if any
$(document.body).on('click', '.slamp-mfp-close', _close);
}else{
console.error("MagnificPopup doesn't exists");
return false;
}
},
mp: mp
}
})(undefined);
window.SlampLightbox = SlampLightbox; //global function
you can view it here:
http://codepen.io/anon/pen/oxZpPL
but it works just once, because the second time I click on the img I get a
javascript error:
VM983 froogaloop2.min.js:1 Uncaught TypeError: Cannot read property
'postMessage' of null
but I can't understand why, it's my fault? o a froogaloop bug?
please help me to understand
thanks
Try this codepen ;)
Here when we attach _attachVideoEvent it do some postMessage thing which throws an error and JavaScript execution breaks; We have delayed this binding so that pop open and then we do binding thing. It still throw error but no problem.
/**
* JS module for open elements in lightbox
*
* #dependencies MagnificPopup
**/
var SlampLightbox = (function(undefined) {
var mp; //store Magnific Popup global object
var mp_exixts = function() {
if ($.fn.magnificPopup) {
mp = $.magnificPopup.instance
return true;
} else {
return false;
}
}
var open_from_hash = function() {
var hash = window.location.hash.slice(1); //cache hash
if (hash.length > 1 && hash != '#!') {
var mark_pos = hash.indexOf('?');
if (mark_pos != -1)
hash = hash.substring(0, mark_pos);
var selector = 'a[name=' + hash + ']';
$(selector).click(); //trigger click event on the element to open magnificPopup
}
}
var open = function($element) {
$element.magnificPopup({
delegate: 'a',
type: 'iframe',
tLoading: '',
iframe: {
markup: '<div class="slamp-mfp-iframe-scaler">' +
'<button title="Close (Esc)" type="button" class="slamp-mfp-close">x</button>' +
'<iframe id="vimeoplayer" class="mfp-iframe" frameborder="0" allowfullscreen></iframe>' +
'</div>', // HTML markup of popup, `mfp-close` will be replaced by the close button
patterns: {
vimeo: {
index: 'vimeo.com/',
id: '/',
src: '//player.vimeo.com/video/%id%?autoplay=1&api=1&player_id=vimeoplayer'
}
}
},
callbacks: {
/*
beforeOpen: function(){
$(window).on("navigate", function (event, data) {
var direction = data.state.direction;
console.log( direction );
if (direction == 'back') {
$.magnificPopup.close();
}
});
},
*/
markupParse: function(template, values, item) {
setTimeout(function() {
_attachVideoEvent(template, values, item);
});
},
elementParse: function(item) {
var $item = item.el; //opened jQuery object
if ($item.hasClass("video-thumb")) { //google analytics track event
var video_name = $item.attr("name");
if (history.pushState)
history.pushState(null, null, '#' + video_name);
else
location.replace(('' + window.location).split('#')[0] + '#' + video_name);
if (typeof ga != 'undefined')
ga('send', 'event', 'Lightbox open', 'Video page', video_name);
}
},
close: function() {
if (window.location.hash != '') {
if (history.pushState)
history.pushState(null, null, '#!');
else
location.replace(('' + window.location).split('#')[0] + '#!');
}
}
}
})
}
var _close = function() {
mp.close();
}
var _attachVideoEvent = function(template, values, item) {
var playerOrigin = '*';
var player = $f(template.find("iframe")[0]);
if (player.length == 0)
return;
var onFinish = function() {
_close();
}
player.addEvent('ready', function() {
player.addEvent('finish', onFinish);
});
}
return {
init: function($element) { //input must be a jQuery object
if (mp_exixts()) {
open($element);
if ($element.length == 0)
return;
open_from_hash(); //open a video specified in the hash, if any
$(document.body).on('click', '.slamp-mfp-close', _close);
} else {
console.error("MagnificPopup doesn't exists");
return false;
}
},
mp: mp
}
})(undefined);
window.SlampLightbox = SlampLightbox; //global function
$(document).ready(function() {
SlampLightbox.init($('.video_header'));
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.1.0/magnific-popup.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.1.0/jquery.magnific-popup.min.js"></script>
<script src="https://f.vimeocdn.com/js/froogaloop2.min.js"></script>
<div class="video_header">
<a class="thumb-video" name="video" href="https://vimeo.com/159375756">
<img class="img-responsive" height="250" src="http://test.slamp.it/wp-content/themes/slamp/partials/templates/bob-wilson/img/timeline/Robert-Wilson-project-SlampHQ_thumb.jpg">
</a>
</div>
I am new to angularjs the below directive is to support decimal and commas, it works fine when a change is made to the field how ever the data in the fields are not validated when the page loads
var app = angular.module('myApply.directives', [], function () {
});
app.directive('numericDecimalInput', function($filter, $browser, $locale,$rootScope) {
return {
require: 'ngModel',
priority: 1,
link: function($scope, $element, $attrs, ngModelCtrl) {
var replaceRegex = new RegExp($locale.NUMBER_FORMATS.GROUP_SEP, 'g');
var fraction = $attrs.fraction || 0;
var listener = function() {
var value = $element.val().replace(replaceRegex, '');
$element.val($filter('number')(value, fraction));
};
var validator=function(viewValue) {
ngModelCtrl.$setValidity('outOfMax', true);
ngModelCtrl.$setValidity(ngModelCtrl.$name+'Numeric', true);
ngModelCtrl.$setValidity('rangeValid', true);
if(!_.isUndefined(viewValue))
{
var newVal = viewValue.replace(replaceRegex, '');
var newValAsNumber = newVal * 1;
// check if new value is numeric, and set control validity
if (isNaN(newValAsNumber)) {
ngModelCtrl.$setValidity(ngModelCtrl.$name + 'Numeric', false);
} else
{
if (newVal < 0) {
ngModelCtrl.$setValidity(ngModelCtrl.$name + 'Numeric', false);
}
else {
newVal = newValAsNumber.toFixed(fraction);
ngModelCtrl.$setValidity(ngModelCtrl.$name + 'Numeric', true);
if (!(_.isNull($attrs.maxamt) || _.isUndefined($attrs.maxamt))) {
var maxAmtValue = Number($attrs.maxamt) || Number($scope.$eval($attrs.maxamt));
if (newVal > maxAmtValue) {
ngModelCtrl.$setValidity('outOfMax', false);
} else {
ngModelCtrl.$setValidity('outOfMax', true);
}
if(!(_.isNull($attrs.minamt) || _.isUndefined($attrs.minamt)))
{
var minAmtValue = Number($attrs.minamt)|| Number($scope.$eval($attrs.minamt));
if((newVal > maxAmtValue) || (newVal < minAmtValue)){
ngModelCtrl.$setValidity('rangeValid', false);
}
else
{
ngModelCtrl.$setValidity('rangeValid', true);
}
}
}
else if((!(_.isNull($attrs.minamt) || _.isUndefined($attrs.minamt))))
{
var minAmtValue = Number($attrs.minamt)|| Number($scope.$eval($attrs.minamt));
if(newVal < minAmtValue)
{
ngModelCtrl.$setValidity('outOfMin', false);
}
else
{
ngModelCtrl.$setValidity('outOfMin', true);
}
}
else {
ngModelCtrl.$setValidity('outOfMax', true);
}
}
}
return newVal;
}
};
// This runs when the model gets updated on the scope directly and keeps our view in sync
ngModelCtrl.$render = function() {
ngModelCtrl.$setValidity('outOfMax', true);
ngModelCtrl.$setValidity(ngModelCtrl.$name+'Numeric', true);
$element.val($filter('number')(ngModelCtrl.$viewValue, fraction));
};
$element.bind('change', listener);
$element.bind('keydown', function(event) {
var key = event.keyCode;
// If the keys include the CTRL, SHIFT, ALT, or META keys, home, end, or the arrow keys, do nothing.
// This lets us support copy and paste too
if (key == 91 || (15 < key && key < 19) || (35 <= key && key <= 40))
return;
});
$element.bind('paste cut', function() {
$browser.defer(listener);
});
ngModelCtrl.$parsers.push(validator);
ngModelCtrl.$formatters.push(validator);
}
};
});
Could some one please let me know as what I am missing .
Thanking you in advance.
Have you declared your app (doesn't show this in your code)? ie:
var app = angular.module('app', []);
Do you have ng-app in your HTML document anywhere?
<html lang="en-GB" ng-app="app">
Check out the documentation to get started with modules (Angular is very well documented, well worth reading): https://docs.angularjs.org/guide/module
app.directive('numericDecimalInput', function($filter, $browser, $locale,$rootScope) {
return {
require: 'ngModel',
priority: 1,
link: function($scope, $element, $attrs, ngModelCtrl) {
...
validator($element.val());
}
};
});
I want to implement "pull down to refresh" in Angular. A lot of the JS libraries don't work with Angular, but I found this one:
https://github.com/mgcrea/angular-pull-to-refresh
However, it doesn't work. I see the text "pull down to refresh" on top, so directive is installed properly. But that's it, nothing happens when I scroll down.
var feedApp = angular.module("feedApp", ['mgcrea.pullToRefresh']);
....
<div id="feed-list-content" data-role="content">
<ul class="feed-list" data-role="listview" pull-to-refresh="onReload()">
<li ng-repeat="summaryItem in summaryItems | orderBy: 'created':true" class="feed-item">
....
</li>
</ul>
</div>
Same questions here: Pull to refresh in Angular Js and here: Angular JS - Pull to refresh
but no solution to my problem.
The onReload method is neither called.
I'm testing on both Android Emulator and Android device. Can't test on iOS, is it caused by Android?
I modified this plugin and solved my problem.
Try like this:
directive("pullToRefresh", function ($compile, $timeout, $q) {
return {
scope: true,
restrict: "A",
transclude: true,
template: '<div class="pull-to-refresh"><i ng-class="icon[status]"></i> <span ng-bind="text[status]"></span></div><div ng-transclude></div>',
compile: function compile(elem, attrs, transclude) {
return function postLink(scope, iElem, iAttrs) {
var body = $('body');
var scrollElement = iElem.parent();
var ptrElement = window.ptr = iElem.children()[0];
scope.text = {
pull: "pull to refresh",
release: "release to refresh",
loading: "refreshing..."
};
scope.icon = {
pull: "fa fa-arrow-down",
release: "fa fa-arrow-up",
loading: "fa fa-refresh fa-spin"
};
scope.status = "pull";
var shouldReload = false;
var setScrolTop = false;
var setStatus = function (status) {
shouldReload = status === "release";
scope.$apply(function () {
scope.status = status;
});
};
var touchPoint = 0;
iElem.bind("touchmove", function (ev) {
var top = body.scrollTop();
if (touchPoint === 0) touchPoint = ev.touches[0].clientY;
var diff = ev.touches[0].clientY - touchPoint;
if (diff >= 130) diff = 130;
if (diff < 80 && shouldReload) {
setStatus("pull");
setScrolTop = true;
}
if (top <= 0) {
scrollElement[0].style.marginTop = diff + "px";
if (diff > 80 && !shouldReload) setStatus("release");
}
});
iElem.bind("touchend", function (ev) {
if (setScrolTop) {
setScrolTop = false;
body.scrollTop(0);
}
if (!shouldReload) {
touchPoint = 0;
scrollElement[0].style.marginTop = "0px";
return;
}
ptrElement.style.webkitTransitionDuration = 0;
ptrElement.style.margin = '0 auto';
setStatus("loading");
var start = +new Date();
$q.when(scope.$eval(iAttrs.pullToRefresh))
.then(function () {
var elapsed = +new Date() - start;
$timeout(function () {
body.scrollTop(0);
touchPoint = 0;
scrollElement[0].style.marginTop = "0px";
ptrElement.style.margin = "";
ptrElement.style.webkitTransitionDuration = "";
scope.status = "pull";
}, elapsed < 400 ? 400 - elapsed : 0);
});
});
scope.$on("$destroy", function () {
iElem.unbind("touchmove");
iElem.unbind("touchend");
});
}
}
};
});