Dynamic views based on content in an Ember.js CMS - javascript

I'm very new to both Ember and web development. Let's say that I'm building a site that displays news articles. I'd like some interaction with Wikipedia links in each article so that when you click on them, a little box will pop up in place with the summary (like the Instapaper app). I'd also like some interaction within the box, expanding and collapsing the summary, etc. What is the most Ember-like way to do this?
I think that with jQuery it would be trivial to add a click handler to the links to add the box to the DOM, but I'd really like the box to be an Ember view. I thought I might have been able to create a view dynamically and append it to a DOM element but it seems that it isn't allowed, and you can only append to ContainerViews.
How should I approach this?
Update on what I've tried
Essentially, I have this as a template for each article so there are paragraphs and a container on the right of each paragraph for the boxes:
{{#each paragraph}}
<div class='paragraph-container'>
<p {{bind-attr id=paragraph_id}}>{{html_text}}</p>
<div class='paragraph-reference-container' {{bind-attr id=paragraph_reference_id}}></div>
</div>
{{/each}}
And an associated view. paragraph_reference_id and paragraph_id are computed properties so I can set the IDs properly here. I also have a ReferenceView for the boxes, which is uninteresting since it's just placeholder content and code at the moment. I was racking my brain trying to figure this out, and ended up trying something like this to append the view as was in the documentation:
App.ArticleView = Ember.View.extend({
templateName: 'article',
classNames: ['articles'],
didInsertElement: function () {
var container = this.container;
Ember.run.scheduleOnce('afterRender', this, function () {
var em = this;
em.$('.paragraph-container').each(function () {
var referenceContainerID = $('.paragraph-reference-container', this).first().prop('id');
em.$('a.wikipedia-link', this).click(function () {
var referenceView = container.lookup('view:reference');
referenceView.appendTo('#' + referenceContainerID);
});
});
});
}
});
But I got the "Uncaught Error: Assertion Failed: You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead." error. Apparently this is no longer supported as mentioned above. I don't really know what the "Ember" way is to do this and still use views (which I'd really like to do) instead of plain HTML and jQuery.

Related

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 drag and drop html content

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
}
});
}

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.

How can I pass around domNodes with bindings to other controllers/directives?

I'm really new to Angular, but learning quickly. However, the one problem that I haven't been able to find an answer for yet is how I can pass around HTML snippets with bindings attached.
e.g. With jQuery I could do something like
var $div = $('<div id="test"><button>CLICK ME!</button></div>');
$div.delegate('button', 'click', function () { alert('CLICKED') });
Then I could pass around this $div variable to other objects. For instance I would use this pattern to separate page specific content from the code of a modal Singleton that encapsulated general functionality.
e.g.
var name = "The Dude";
var $div = $('<div id="test"><button>CLICK ME!</button></div>');
$div.delegate('button', 'click', function () { alert('Hi, ' + name) });
Modal.open({ content: $div});
How can I achieve something similar with Angular?
You should not pass around template code in the controllers, that's not the "angular way". If you have to do DOM manipulation it should stay in a directive. A lot of jQuery developers fall into this trap of thinking like you do with jQuery. But anyways here's how I would solve this problem:
I'm writing this in a directive so it's more modular. You could move this into a controller and it'll work just fine.
Code in plnkr here
In the directive you have a template which contains your code and you can have a ng-click binding which can control a click action to open the modal.
Now as far as the content in the modal is concerned, making the actual template code modular so you can decouple the content from the template depends on if/what angular plugins you're using. They all do something similar but the names may be a little different. For example, if you're using ui-bootstrap(which is awesome by the way, if you dont use any plugins I'd highly recommend it) there's a resolve function that takes a variable as an argument and you can display that in the view. That's exactly what you want to send custom content to the modal(eg. name)

Ember.js Navigation Controller - am I on the right path?

I'm currently building a reporting platform for internal use and decided to use Ember.js. So far it's been both a good and bad experience; the bad is mostly related to the documentation and how most things I've researched online have changed with the latest revisions of ember.
We are using twitter bootstrap and the navigation portion of bootstrap has the active class on the li element instead of the a element. Naturally my first inclination was to just use jquery as a hack and just change the active class manually which felt totally wrong and decided to find the 'right' way.
So I ended up building a custom view for the navigation, see below: (note: I am using browserify)
// NavigationView.js
module.exports = Ember.View.create({
templateName: 'navbar',
// Bind the 'selected' property on this view to the controllers 'selected' property.
selectedBinding: 'AnalyticsApp.NavigationController.selected',
// a single sub item for the nav
NavViewElement: Ember.View.extend({
// Change the tag name to be a LI tag since bootstrap requires active class
// to exist on that, instead of the default (ember) anchor tag when using {{linkTo}}
tagName: 'li',
// Bind the 'active' class to the computed property; checking if this nav
// element is the current active tab.
classNameBindings: ['isActive:active'],
// This computed property will check if this nav item is active
isActive: function() {
return this.get('item') === this.get('parentView.selected');
}.property('item', 'parentView.selected')
})
});
Now, setting up the view was pretty straight forward, to use it to render a nav element I can use this:
{{#view view.NavViewElement item="network" }}
{{#linkTo 'network'}}
<i class="icon-sitemap"></i>
<span>Networks</span>
{{/linkTo}}
{{/view}}
In all the routes in the setupController method I am setting the 'selected' tab like so
AnalyticsApp.NavigationController.set('selected', 'network');
Now here is where I am unsure regarding my implementation and I would really appreciate if someone could tell me if I'm way off target or if I am on the right path.
I am using the NavigationController (below) to be the central store for the navigation logic, it is actually an ObjectController that I've extended and chained .create() on.
AnalyticsApp.NavigationController = Ember.ObjectController.extend({
selected: null
}).create();
I tried extending a standard controller, but that doesn't expose the set / get methods. Is using an ObjectController for this type of setup the right type?
Thanks for taking the time to read, and I appreciate any and all feedback.
-Ryan S.
Since my comment was usefull, I'll convert it to an answer. So, since your NavigationController is used app wide, try just to create the controller like so:
AnalyticsApp.NavigationController = Ember.ObjectController.create({selected:null});
Hope it helps

Categories