TextAngular fileDropHandler documentation - angularjs

We have just upgraded our textangular to 1.2.2, as this now supports drag and drop.
have seen defaultFileDropHandler within the textAngualrSetup, how ever, struggling to find any documentation to support this or how to use it.
defaultFileDropHandler:
/* istanbul ignore next: untestable image processing */
function (file, insertAction)
{
debugger;
var reader = new FileReader();
if(file.type.substring(0, 5) === 'image'){
reader.onload = function() {
if(reader.result !== '') insertAction('insertImage', reader.result, true);
};
reader.readAsDataURL(file);
return true;
}
return false;
}
Basically, we want to allow users to drag multiple pdf's, word docs etc and to upload on submit.
We could prob get this working in a fashion adding in functionality into defaultFileDropHandler within the settings page,
we implement ta by :-
<div text-angular data-ng-model="NoteText" ></div>
however, is there a cleaner way to achieve this?

Sorry about the lack of docs!
Basically the defaultFileDropHandler is triggered when the HTML element.on("drop") event is fired.
Implementing this via the textAngularSetup file is fine, but will be globally applied to all instances. To apply a handler for just one instance of textAngular use the ta-file-drop attribute which should be the name of a function on the scope with the same signature as defaultFileDropHandler. For Example:
JS In Controller
$scope.dropHandler = function(file, insertAction){...};
HTML
<div text-angular data-ng-model="NoteText" ta-file-drop="dropHandler"></div>

Both great answer, thank you!
I would just like to put the full code out to cover the global case since the code was only a snippet...
app.config( function( $provide ) {
$provide.decorator( 'taOptions', [ '$delegate', function( taOptions ) {
taOptions.defaultFileDropHandler = function( file, insertAction ) {
// validation
if( file.type.substring( 0, 5 ) !== "image" ) {
// add your own code here
alert( "only images can be added" );
return;
}
if( file.size > 500000 ) {
// add your own code here
alert( "file size cannot exceed 0.5MB" );
return;
}
// create a base64 string
var reader = new FileReader();
reader.onload = function() {
reader.result && insertAction( "insertImage", reader.result, true );
};
reader.readAsDataURL(file);
return true;
};
return taOptions;
}]);
});

Related

Enable and disable CKEditor Inline editing using Angular

How can I enable or disable ckeditor.inline on a div based on a click of a button/link.
This is how I would achieve it in jquery but can't figure it out using angular.
$('.toggle-edit').click(function(e){
var id_editedDiv = $(this).data('editTarget');
var editedDiv = '#' + id_editedDiv;
if( $(editedDiv).attr('contenteditable') == 'true' )
{
$(editedDiv).attr('contenteditable','false');
CKEDITOR.instances.id_editedDiv.destroy();
$(this).text('Start Editing');
}
else
{
$(editedDiv).attr('contenteditable','true');
CKEDITOR.inline( id_editedDiv );
$(this).text('Finish Editing');
}
});
This is how I achieved the result.
https://plnkr.co/edit/YUOYGa?p=preview
But now I need to figure out how to bind the model to the CKEditor so that my changes are updated in the model when I hit save.
Destroys the editor instance, releasing all resources used by it. If the editor replaced an element, the element will be recovered.
In your controller:
alert( CKEDITOR.instances.editor1 ); // e.g. object
CKEDITOR.instances.editor1.destroy();
alert( CKEDITOR.instances.editor1 ); // undefined
More detail
You can use button with ng-click attribute for example:
<button type="button" class="inlineEditButton"
ng-controller="CKEditorController"
ng-click="changeInlineEditindState()"
</button>
in CKEditorController you should define method:
$scope.changeInlineEditindState = function() {
// your code
}
You can do something like:
var content = '';
After instances creation add listener:
ck.on('instanceReady', function () {
ck.setData(content);
});
On save button click:
content = ck.getData();

Rendering a portion of code (tablesorter with selectize) everytime I load a React component

My app is rendering a portion of code everytime I load a component. This is the code:
https://jsfiddle.net/rLvfa8rn/
I'm trying to implement this http://jsfiddle.net/Unspecified/qrqJv/1/ on my tablesorter table.
The problem is with the portion of lines 71-121, there's a dropdown of Selectize.js rendering everytime I call the page:
Selectize.define( 'clear_selection', function ( options ) {
var self = this;
var title = options.title || 'Sin filtro';
//Overriding because, ideally you wouldn't use header & clear_selection simultaneously
self.plugins.settings.dropdown_header = {
title: title
};
this.require( 'dropdown_header' );
self.setup = (function () {
var original = self.setup;
return function () {
original.apply( this, arguments );
this.$dropdown.on( 'mousedown', '.selectize-dropdown-header', function ( e ) {
self.setValue( '' );
self.close();
self.blur();
return false;
});
}
})()
});
I put all the code because maybe the problem is another.
Well, all the problem was in the var selectize({
var selectize({
hideSelected: false,
dropdownParent: 'body'
the option: dropdownParent: 'body' was the problem, it's a know bug of selectize I guess. Removing that option works fine.

How do I change AngularJS ng-src when API returns null value?

In working with the API from themoviedb.com, I'm having the user type into an input field, sending the API request on every keyup. In testing this, sometimes the movie poster would be "null" instead of the intended poster_path. I prefer to default to a placeholder image to indicate that a poster was not found with the API request.
So because the entire poster_path url is not offered by the API, and since I'm using an AngularJS ng-repeat, I have to structure the image tag like so (using dummy data to save on space):
<img ng-src="{{'http://example.com/'+movie.poster_path}}" alt="">
But then the console gives me an error due to a bad request since a full image path is not returned. I tried using the OR prompt:
{{'http://example.com/'+movie.poster_path || 'http://example.com/missing.jpg'}}
But that doesn't work in this case. So now with the javascript. I can't seem to get the image source by using getElementsByTagName or getElementByClass, and using getElementById seems to only grab the first repeat and nothing else, which I figured would be the case. But even then I can't seem to replace the image source. Here is the code structure I attempted:
<input type="text" id="search">
<section ng-controller="movieSearch">
<article ng-repeat="movie in movies">
<img id="myImage" src="{{'http://example.com/'+movie.poster_path}}" alt="">
</article>
</section>
<script>
function movieSearch($scope, $http){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://example.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
imgSrc = document.getElementById('myImage').src;
ext = imgSrc.split('.').pop();
missing='http://example.com/missing.jpg';
if(ext !== 'jpg'){
imgSrc = missing;
}
});
}
</script>
Any ideas with what I'm doing wrong, or if what I'm attempting can even be done at all?
The first problem I can see is that while you are setting the movies in a async callback, you are looking for the image source synchronously here:
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
// This code will be executed before `movies` is populated
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
However, moving the code merely into the callback will not solve the issue:
// THIS WILL NOT FIX THE PROBLEM
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
// This will not solve the issue
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
// ...
});
This is because the src fields will only be populated in the next digest loop.
In your case, you should prune the results as soon as you receive them from the JSONP callback:
function movieSearch($scope, $http, $timeout){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
$scope.movies.forEach(function (movie) {
var ext = movie.poster_path && movie.poster_path.split('.').pop();
// Assuming that the extension cannot be
// anything other than a jpg
if (ext !== 'jpg') {
movie.poster_path = 'missing.jpg';
}
});
});
});
}
Here, you modify only the model behind you view and do not do any post-hoc DOM analysis to figure out failures.
Sidenote: You could have used the ternary operator to solve the problem in the view, but this is not recommended:
<!-- NOT RECOMMENDED -->
{{movie.poster_path && ('http://domain.com/'+movie.poster_path) || 'http://domain.com/missing.jpg'}}
First, I defined a filter like this:
In CoffeeScript:
app.filter 'cond', () ->
(default_value, condition, value) ->
if condition then value else default_value
Or in JavaScript:
app.filter('cond', function() {
return function(default_value, condition, value) {
if (condition) {
return value;
} else {
return default_value;
}
};
});
Then, you can use it like this:
{{'http://domain.com/missing.jpg' |cond:movie.poster_path:('http://domain.com/'+movie.poster_path)}}

Parsing The Response From Fetch Backbone

I am not able to get the response string from the fetch method in my view render method.
The Collection Class Goes here.
Collection = (function(){
var Events;
Events = Backbone.Collection.extend({
url:function(){
//alert(urlOptions);
return this.urlParam;
// alert('API call :'+urlOptions);
},
initialize: function(models, options){
this.urlParam = options.urlParam || "";
},
parse: function( response ){
var parsed = Jath.parse(
[ '//notes', {
} ], response );
console.log(parsed);
return parsed;
}
});
return {
newInstance : function(models,options) { return new Events(models,options); }
};
})();
The View Goes Here
View = (function() {
'use strict';
var
htmlTemplate = _.template( $('#eventGridTemplate' ).html() ), // See templatesSearch.jsp
expanded = true, // By default the Events Grid extends to the bottom of the browser window.
BackboneView, applyStyles;
/**
* Apply CSS specific to this view
* Unfortunately, this View needs to modify its parent wrapper element.
* Otherwise the layout will break when it's resized. See templatesSearch.jsp.
* #param {Object} $elmt
* #param {Boolean} expand
*/
applyStyles = function( $elmt, expand ) {
var
top = '2px',
left = '2px',
pos = 'absolute',
right = '2px';
if ( expand ) {
$elmt.css({
"position" : pos,
"top" : top,
"left" : left,
"right" : right,
"bottom" : "2px"
});
$elmt.parent().css( 'bottom', '2px' );
} else {
$elmt.css({
"position" : pos,
"top" : top,
"left" : left,
"right" : right,
"bottom" : "50%"
});
$elmt.parent().css( 'bottom', '50%' );
}
};
// See 'http://backbonejs.org/#View-constructor' for more info
BackboneView = Backbone.View.extend({
onAiringsBtn : function( event ) {
// If the Events Grid container was expanded, contract it.
// If it was contracted, expand it.
expanded = expanded ? false : true;
applyStyles( this.$('div'), expanded );
},
initialize : function() {
this.render();
},
render : function() {
// this.$el is the jQuery version of this.el
// Which is populated by options.el
// Which is part of the options object passed into the constructor
//alert('Start Date:' +$('#datepicker').datepicker('getDate'));
var eventsCollection = Collection.newInstance([],{urlParam:'http://localhost:8080/adtglobal/2.0/api/events?startDate=2013-11-05T00:00:00-0400&endDate=2013-11-06T00:00:00-0400'});
//console.log(eventsCollection.url());
eventsCollection.fetch({
success : function(eventsCollection , response){
console.log(eventsCollection.toJSON());
alert(eventsCollection.toJSON());
}
});
this.$el.html( htmlTemplate );
applyStyles( this.$('div'), true );
}
});
//-------------------------------------------------------------------------------------------------------------------
// Public API
//-------------------------------------------------------------------------------------------------------------------
return {
newInstance : function(options) { return new BackboneView(options); }
};
})();
I get The response as success and i see the xml in the browser console, but how do i parse it here ??
The response is here
<Events xmlns="urn:api:2.0">
<PageNavigation showing="45"></PageNavigation>
<Event id="400515625" href="400515625" source="SDR">
<OrgHierarchy>
<level id="56" typeId="100" title="Soccer" description="Sport" href="../sporthierarchy?levelId=56&levelTypeId=100"></level>
<level id="2902" typeId="101" title="UEFA" description="Confederation" href="../sporthierarchy?levelId=2902&levelTypeId=101" parentId="56" parentType="100"></level>
</OrgHierarchy>
<EventType id="1">Standard Event</EventType>
<League id="1860">UEFA > Polish Orange Ekstraklasa</League>
<EventTitleText>Ruch Chorzow vs. Zawisa Bydgoszcz</EventTitleText>
<CompetitionType id="1">Team vs Team</CompetitionType>
<EventCompetitors>
<Teams>
<HomeTeam id="73960" href="../teams/73960">Ruch Chorzow</HomeTeam>
<AwayTeam id="107278" href="../teams/107278">Zawisa Bydgoszcz</AwayTeam>
</Teams>
</EventCompetitors>
</Event>
</Events>
well, it seems you are not using Jath correctly.
you need to define a template that matches your XML.
I have not used Jath before, but I will try to create a simple template for you:
//in your parse
var template = [ "//Events", {id: "#id", eventType: 'EventType'}];
return Jath.parse(template, response );
try this first and see if you have something in your collection.
it seems you have nested resources in your response, so you will have to complete the template.
or, if you don't have to use Jath, try something simpler: here's an example jsfiddle, you should be able to see the parsed JSON in the console.
http://jsfiddle.net/5j57T/
EDIT: xml to json code is from: http://davidwalsh.name/convert-xml-json
NOTE: you need to parse that json again into an array of objects you need, in order to make your app work!
=====================
EDIT: another example (works better than the previous fiddle)
http://jsfiddle.net/5j57T/1/
uses a jquery library http://www.fyneworks.com/jquery/xml-to-json/
there's only one issue, since there's only one event in your events, it cannot be parsed to an array by default, you will have to do that manually. If, there's always only one event per response, you can do something like this:
parsed = [$.xml2json(xmlDoc.getElementsByTagName('event')[0])]
or you can just do
parsed = $.map(xmlDoc.getElementsByTagName('event'), function(eventXml) {
return $.xml2json(eventXml);
});
working jsfidde: http://jsfiddle.net/5j57T/2/
==========================
EDIT:
I think by default, backbone is expecting a JSON response from the server. You will have to overwrite it's fetch() function so support XML. so in your collection definition, add this:
fetch: function (options) {
options = options || {};
options.dataType = "xml";
return Backbone.Collection.prototype.fetch.call(this, options);
}
here's a working jsfiddle: http://jsfiddle.net/5j57T/4/
Ignore the 'data' and 'type' options I passed to fetch(), and the url, all of that are just to make Ajax request work on jsfiddle. You shouldn't have those in your app.
In this demo, when the XML is returned, it's parsed into the collection correctly. You should be able to see your collection in the console.
======
If all of your backend services return XML only. I'd suggest overwrite the Backbone.sync function to make it work for other methods like save().

wait for backbone template to be appended to the dom

I am trying to bind events to elements that are placed by appending a backbone template:
appendEditTemplateAndSetEvents: function() {
var associatedCollection = App.Helpers.findAssociatedCollection(this.allCollections, this.associatedCollectionId);
var template = this.setEditTemplateForElement(associatedCollection.type);
var modalBody = this.$el.find('.modal-body');
modalBody.empty();
var firstModel = associatedCollection.at(0);
if(template.mainTemplate !== null) {
modalBody.append($('#edit-form-element-frame').html());
//each mode in collection
associatedCollection.each(function(model){
if(model.get('positionInContainer') === 1) {
firstModel = model;
}
console.log(model.attributes);
modalBody.find('.elements-in-editmodal-wrapper').append(template.mainTemplate(model.toJSON()));
});
}
if( template.templateValidation.length !== 0 ) {
modalBody.append('<hr><h3>Validateregels</h3>');
_.each(template.templateValidation, function(val, index) {
modalBody.append(val(firstModel.toJSON()));
});
}
//set listeners and handlers that apply when a edit modal is open
this.validationEventsForEditModal(firstModel);
this.editErrorMessagesInModal(firstModel);
},
Now the problem is that when the last two functions are called the html of the templates isn't appended yet so the the events are binded to an object with a length of 0.
Does anyone have a decent solution for this async problem? I tried $.Defferred but that did not work, but maybe someone get's it working.
I solved this by using this.$el.find(...) in the functions:
this.validationEventsForEditModal(firstModel);
this.editErrorMessagesInModal(firstModel);
I don't know if it's still an async problem, but this solves it.

Resources