angularjs drag and drop html content - javascript

I am trying to create a live HTML editor with AngularJS, where user can drag and drop some DOM elements to a container and it will render the final HTML page, for example, there is a button (or whatever) that says "Drag me to create an input", then user will drag that item into the container and an input field will be rendered...
I've been trying ngDraggable module but isn't what I needed...
I think a way is to hold the HTML code into some $scope variable, and when the user drops the button into the container, $scope.input which contains <input type="text" placeholder="Something"/> will be rendered inside container...
My final step is to enable inline edit to thoose HTML elements generated, in order to let the user create some content agains that final template.
When the user finish, the whole HTML code will be saved somewhere or downloaded...
I don't need something complex, just a few predefined elements that can be dropped...
I hope I've explained it correctly.
There is some example found at http://nboychev.com/tests/angular/Drag%20Drop%20iFrame%20using%20Angular%20JS%20Directive.html that does exactly what I want (see source), BUT:
It's using jQueryUI (Which I don't want to use, but if its needed then I will)
It isn't working on my project. I must say that I'm using RequireJS, but everything works on my angular app but the directives mentioned on the link above. Draggable is not working, I do not have any errors and I've checked that the code is being executed, doing some console.log() stuff, but still not working...
But it contains what I want, draggable stuff with HTML injected that renders content on an iframe (or a div, or whatever) and that new content is also droppable, to insert more stuff inside that code. Give it a try, and guide me a little. Thanks

I think it's better if you try using something as jui
On the other hand you can try implementing your own directive:
<br>
<h4>Drop Area</h4>
<div droppable jui-options="{addClasses: false}" class="drop-area"></div>
<div class="read-out">
<span class="text-info"><strong>Draggable ID</strong></span>: {{obj.id}}<br><br>
<span class="text-info"><strong>Content</strong></span>: <span ng-bind-html="obj.content"></span><br><br>
<span class="text-info"><strong>Actual Content</strong></span>: {{obj.content}}<br><br>
</div>
</div>
http://codepen.io/m-e-conroy/pen/jCdhu

Sounds like a fun project.
This is a simple link function using JqueryUI and Angular. You'll need an HTML element in scope with class "draggable". Use destroy() when finished.
Adding HTML to the DOM from the scope, check out this solution by Josh David Miller: http://jsfiddle.net/paulocoelho/fBjbP/2/
Or for simple HTML elements for visual display only you could try using ngBindHtml, ngRepeat and ngSanitize. Then adding / removing HTML elements to and from an array should display them in a container.
link: function($scope, $elem, $attr){
$elem.find(".draggable").draggable({
start: function(event, ui){
$(ui.item).addClass("shadow");
},
stop: function(event, ui){
$(ui.item).removeClass("shadow");
// DO SOME ANGULAR STUFF HERE
}
});
}

Related

How to capture clicked element using $(this) inside of a vue(js) instance

I am reworking an old app of mine and I am having issues with dom manipulation and basic selections within a vue instance.
Essentially I have information in a database that I load in via ajax.
Each record in the db has 2 sections. The header tab(title, time, date etc) and the body of the record(notes, ideas, etc)
When loaded, the header shows normally to the user but if they want to see what that note contains, they have to click on the header for the bottom to appear.
consider the following html:
<vuejs for loop>
<div v-bind:id='item._id' class="tabW" v-on:click="blueTabClick" >
<div class="blueTabMainColor">
<!-- header stuff here -->
</div>
<div class="notesOpenedW">
<!-- interior informaton here, HIDDEN BY CSS -->
</div>
</div>
<vuejs for loop ender>
This HTML is essentially inside a Vue for/loop directive, and generates however many "tabs(tabW)" as needed based on how much info I have in the DB
All I want the user to do is to be able to click whichever tab(tabW) they want information on, and for the notes show underneath(notesOpenedW).
I stripped my entire app and js and tried to keep it as simple a test as possible and even with the below, I still can't get anything.
here is my JS(JQ):
$(document).ready(function(evt){
$(".blueTabMainColor").click(function(){
$(this).next(".notesOpenedW").fadeToggle();
});
});
With this basic code, when I put it inside a Vue instance, via:
methods: {
blueTabClick: function (evt) {
evt.preventDefault();
$(".blueTabMainColor").click(function(){
//alert("you clicked me");
$(this).next(".notesOpenedW").fadeToggle();
});
}
}
It doesn't work, but if I take it out of the Vue instance, it works just fine.
how can I get this to work? or am I going about it the wrong way?
Vue will not cohabit happily with JQuery. You're $(this) will not work because you're not even in the document at that point, you're in pure js, virtual DOM, another universe. Then, if it did, the event listener you call may not exist. You will need to fundamentally transition this code to Vue if you want it to work, I fear.
You can achieve this by setting a ref on "notesOpenedW".
https://v2.vuejs.org/v2/api/#ref
I would strongly recommend to wrap this behaviour in a dedicated component
That would have the following content :
<div class="tabW" v-on:click="blueTabClick" >
<div class="blueTabMainColor">
<!-- header stuff here -->
</div>
<div class="notesOpenedW" ref="notesToggleDiv">
<!-- interior informaton here, HIDDEN BY CSS -->
</div>
</div>
And the method :
methods: {
blueTabClick: function () {
$(this.$refs.notesToggleDiv).fadeToggle();
}
}
Be aware that when using Vue, manipulating directly the dom is usually a bad idea.
As i showed you, it is possible to use jQuery with Vue if you absolutely need it (or cannot afford to rework more deeply your application).
Edit : Just found this article that i think would help you a lot :
https://www.smashingmagazine.com/2018/02/jquery-vue-javascript/?utm_campaign=Revue%20newsletter&utm_medium=Newsletter&utm_source=Vue.js%20Developers

How to manipulate the DOM with Onsen UI

I'm a total newbie to Onsen UI and I managed to make my first little app (static that is) with a few pages, popovers, lists, etc.
But when I try to add dynamic stuff in there, it does not want to cooperate.
When I click my side menu, it calls menu.setMainPage and in the callback I want to modify the content of the list (lets say iterate a JSON request and add a ons-list-item for each of them). However, they do not look styled with Onsen UI icing.
I guess it's because the menu.setMainPage has already parsed the ons-page and showed it in the browser.
Is there a way to do a load page, update the dom, and then pass it to be displayed?
I have a simila problem with an popover that contains a list. I want to add items in that list, but my jQuery append never work. Same reason I suppose.
Thanks!
Sounds like you're not running ons.compile() on the dynamic elements. The custom elements must be compiled after they've been added to the DOM to get the correct style and behavior.
I made a short example to illustrate it:
ons.bootstrap();
var addItem = function() {
var $myList = $("#my-list"),
$item = $("<ons-list-item>").text(Math.random());
$myList.append($item[0]);
ons.compile($item[0]);
};
If you attach the addItem function to a click handler you can add items dynamically to an <ons-list>.
This is a running example on codepen:
http://codepen.io/argelius/pen/gbxNEg

angularjs directive: not replace nor transclude

I need to append a directive's template AFTER an input field. The original input field needs to remain - I can't just create a duplicate of it. My thought for this was to, in the controller, use jQuery to add a DIV after the input field, and add an attribute for the directive to the div. However, in practice, that doesn't work - the div is created and added, but the directive doesn't activate.
The problem, I know, is that the jQuery-added div is not yet recognized by the angularjs controller - it appears AFTER angularjs runs over the controlled html.
I know that part of the problem is that you're not supposed to use jQuery in the controller, but I honestly can't think of another way to do it. Is there some way to cause the angularjs controller to look at this new div?
The original HTML looks like the following.
<input name="generatedString_1234567890">
I run jquery over the page to add a controller to the body. The relevant code looks similar to this:
jQuery('body').attr('ng-controller','MainCtrl');
angular.module('app',['DataTools']);
angular.element(document).ready(function(){
angular.bootstrap(document, ['app']);
});
Inside the angularjs, I run a resource to get a json string containing the relevant changes to the DOM, in terms of attributes that need to get added to the inputs and certain understood flags that require specific coding. The relevant task I'm trying to accomplish looks like this, where $(this) is the input field for the DOM.
jQuery(document).find('input.FormField,select.FormField').each(function(){
// Inside a case statement based on certain flags
var d = $('<div/>');
d.attr("ng-model", 'adors.'+label);
// Relevant code that adds attributes to the div - including the required directives
$(this).hide().after(d);
}
I am trying to create code that looks more like this (VERY GENERIC):
<input name="generatedString_1234567890" ng-hide="true" ng-model="input.uniqueKey">
<div special-input="time" ng-model="input.uniqueKey">
<select ng-repeat="hour in hours">
<select ng-repeat="minute in minutes">
</div>

jQuery UI Dialog: Dialog Element Disappearing from the DOM

I'm working on a project and I am attempting to create a modal dialog "pop-up" to capture data in a form. I haven't worked with jQuery UI's Dialog widget previously, but I've worked with others and it seemed straight forward.
I created the following very simple code snippet to test as I went along:
<div class="app-email">
<div>
<a href="#"
class="app-email-opener">
Click to add or edit your e-mail settings.
</a>
</div>
<div class="app-email-modal">
Oh, Hai.
</div>
</div>
$('.content').on({
click: function () {
console.log('I was totes clicked.');
var parent = $(this).parents('.app-email');
console.log(parent);
var target = parent.find('.app-email-modal');
console.log(target);
$(target).dialog('open');
}
}, '.app-email-opener');
$('.app-email-modal').dialog({
autoOpen: false,
modal: true,
show: false
});
For reference: the class 'content' is a higher level block to catch delegated events without having to go all the way up the DOM.
The issue I'm running into is that the div with class="app-email-modal" seems to flash onto the page and then disappear from the DOM completely. jQuery, therefore, isn't able to find it and do anything because at that point it simply doesn't exist.
The overall project is in ASP.NET MVC 4, using Visual Studio 2013.
Any information would be greatly appreciated.
So, finally discovered what's happening via this previously answered question:
Jquery Dialog - div disappears after initialization
//EDIT
For any possible future usefuless -
What was happening was that jQuery UI will move any DOM elements specified as Dialogs to the bottom of the page, rather than keep them in the location specified in the HTML markup. So, in my case, I was looking for things by class, but only within the scope of the app-email-openers parent app-email div.
To remedy this, I used templating (in my case, Razor) to add unique ids to each app-email-modal div, and added a data- attribute to associate the link with the specific unique id. This way they jQuery UI can move the elements as it sees fit, but there still easily accessible.
//END EDIT
I feel like that functionality should be better spelled out in the documentation. Even their own example doesn't operate like this.
Corollary: I attempted to use the appendTo option to have the DOM elements not be shifted to the bottom of the page, but they're still moved to the bottom. So, there's that.

Show pop-ups the most elegant way

I have this AngularJS app. Everything works just fine.
Now I need to show different pop-ups when specific conditions become true, and I was wondering what would be the best way to proceed.
Currently I’m evaluating two options, but I’m absolutely open to other options.
Option 1
I could create the new HTML element for the pop-up, and append to the DOM directly from the controller.
This will break the MVC design pattern. I’m not happy with this solution.
Option 2
I could always insert the code for all the pop-ups in the static HTML file. Then, using ngShow, I can hide / show only the correct pop-up.
This option is not really scalable.
So I’m pretty sure there has to be a better way to achieve what I want.
Based on my experience with AngularJS modals so far I believe that the most elegant approach is a dedicated service to which we can provide a partial (HTML) template to be displayed in a modal.
When we think about it modals are kind of AngularJS routes but just displayed in modal popup.
The AngularUI bootstrap project (http://angular-ui.github.com/bootstrap/) has an excellent $modal service (used to be called $dialog prior to version 0.6.0) that is an implementation of a service to display partial's content as a modal popup.
It's funny because I'm learning Angular myself and was watching some video's from their channel on Youtube.
The speaker mentions your exact problem in this video https://www.youtube.com/watch?v=ZhfUv0spHCY#t=1681 around the 28:30 minute mark.
It comes down to placing that particular piece of code in a service rather then a controller.
My guess would be to inject new popup elements into the DOM and handle them separate instead of showing and hiding the same element. This way you can have multiple popups.
The whole video is very interesting to watch as well :-)
Create a 'popup' directive and apply it to the container of the popup content
In the directive, wrap the content in a absolute position div along with the mask div below it.
It is OK to move the 2 divs in the DOM tree as needed from within the directive. Any UI code is OK in the directives, including the code to position the popup in center of screen.
Create and bind a boolean flag to controller. This flag will control visibility.
Create scope variables that bond to OK / Cancel functions etc.
Editing to add a high level example (non functional)
<div id='popup1-content' popup='showPopup1'>
....
....
</div>
<div id='popup2-content' popup='showPopup2'>
....
....
</div>
.directive('popup', function() {
var p = {
link : function(scope, iElement, iAttrs){
//code to wrap the div (iElement) with a abs pos div (parentDiv)
// code to add a mask layer div behind
// if the parent is already there, then skip adding it again.
//use jquery ui to make it dragable etc.
scope.watch(showPopup, function(newVal, oldVal){
if(newVal === true){
$(parentDiv).show();
}
else{
$(parentDiv).hide();
}
});
}
}
return p;
});
See
http://adamalbrecht.com/2013/12/12/creating-a-simple-modal-dialog-directive-in-angular-js/
for a simple way of doing modal dialog with Angular and without needing bootstrap
Edit: I've since been using ng-dialog from http://likeastore.github.io/ngDialog which is flexible and doesn't have any dependencies.
Angular-ui comes with dialog directive.Use it and set templateurl to whatever page you want to include.That is the most elegant way and i have used it in my project as well.
You can pass several other parameters for dialog as per need.

Categories