Backbone: how to manipulate elements outside the element associated with View - javascript

My html structure is:
<div id="header">
</div>
<div id="screen">
</div>
My Backbone View is associated with screen. How to hide/show element in header?

The direct answer is:
There's nothing special backbone will do for you here. Do it like you would do had backbone not existed at all.
The correct answer is:
If you need to manipulate UI elements outside of your view model, your view model is wrong - fix it.

According to the backbone structure & logic - your headerview or screenview should throw an event, in which header view should listen to. If that event happens - then headerview should hide itself.

Related

Getting The Source Element From Click Event Handler In Angular

I am still new to Angular and I'm struggling to get the DOM Element of an Angular Click Listener.
What I have is the following component HTML:
<div *ngFor="let menuItem of menu; index as itemId" class="menuItem">
<div class="menuItem__top" (click)="itemClicked($event, !!menuItem.submenu)">
<!-- Additional divs inside... -->
</div>
</div>
I would like to toggle a class of "menuItem__top" when it is clicked. My approach was to use a click event listener but I can't get the source Element to apply the class on.
itemClicked(event, hasSubmenu){
console.log(this) //is the component
let target = event.target || event.srcElement || event.currentTarget;
if(hasSubmenu){
console.log(target);
}
}
It could be done by getting the target of the $event but this way I would need to check the target and move with closest(".menuItem__top") up to the correct source element.
Is there an easy way in Angular to pass the source element of the click listener to the click function or a way to get it inside the click function?
In vanilla JS it would be as easy as using "this" inside the click function, but in Angular this is bind to the component. (In this case, it would be ok to loose the binding to the component if this is the only way.)
I thought about two ways:
Assigning a dynamic reference containing some string and the itemId, passing the itemId and retrieving the reference object based on the itemId in the listener.
Using a #HostListener to listen on every "menuItem__top" click and toggling the class every time.
What do you think is the best way? I feel like I am missing something simple here.
Go the other way around. People are used to jQuery and the way it works (selecting elements already present in the DOM, then adding them a class). So in Angular, they try to do the same thing and grab the element that was clicked, to apply it a class. Angular (and React and others) work the other way around. They construct the DOM from data. If you want to modify an element, start with modifying the data it was generated from.
This <div class="menuItem__top"> that you click on is constructed from let menuItem. All you have to do is add a property, say "isClicked" to menuItem.
<div *ngFor="let menuItem of menu; index as itemId" class="menuItem">
<div class="menuItem__top"
[class.isClicked]="menuItem.isClicked"
(click)="menuItem.isClicked = true">
<!-- Additional divs inside... -->
</div>
</div>

What is initialised first - controller or partial/html?

Hi please explain reason for following three scenarios as I am unable to know why is this happening -
1)
<div ng-controller="myctrl">
<p>something for DOM manipulation</p>
</div>
2)in route I write
('someroute',{
templateUrl : "mytemplate",
controller : "myctrl"
});
mytemplate:
<div>
<p>something for dom manipulation</p>
</div>
3)
<div ng-include="mytemplate" ng-controller="myctrl"></div>
with template being same as above
The controllers in all the above scenarios are same, and in all of them I am just trying to select p tag of DOM by writing angular.element('p'). But this seems inconsistent. It works very well in 2nd scenario, it never works in 3rd scenario and I am not sure about 1st sccenario. Can someone explain which method is best for dom selection/manipulation, as I have to add a class to this 'p' tag on hover.
I am not understanding which gets initialized first- controller or partial?
Manipulating DOM inside controllers is discouraged. Quote from Best Practice - Dom Manipulations:
Dom Manipulations should not exist in controllers, services or anywhere else but in directives.
If you only need to style elements on hover, using the p:hover CSS selector would be enough without touching the DOM. ng-class and ng-mouseover can help you if you really want the class.
For more complex scenarios, you may want to write your own directive. You can check the article above for a guide to do that.
Load order from the first case: HTML first. Directives like ngController are loaded after parsing the HTML. Therefore the HTML already exists when the controller is loaded.
Load order for the second case: I'm not sure about it. You may check documentation for ngRoute or uiRouter depending on the router you are using.
Execution order for the third case: Controller first. The directive ngController have higher priority than the ngInclude directive. Therefore, the controller is loaded first.
Quote from ngController documentation :
This directive executes at priority level 500.
Quote from ngInclude documentation :
This directive executes at priority level 400.

Show sibling element on mouseover using angular

I have many repeated content elements in a single view. Within each content element, there's an anchor. When a user mouses over this anchor, I want to toggle a class on a sibling element within that particular content element.
Here's a simple example of what I want to do:
<div class="content-element">
<div ng-class="visibleClass">
I should have class 'visible' when the user mouses over the link within content-element div.
</div>
<a ng-mouseover="" ng-mouseleave="" href="#">Mouseover</a>
</div>
I initially wrote a controller to handle this, but the controller's $scope is tied to the entire view, not a single content-element, so this turned out to not be a graceful solution.
There are many 'content-elements' that are not generated with angular, but are just repeated in the template.
I'm fairly new to angular and trying to wrap my head around this new way of thinking. I can definitely solve this problem easy writing some javascript (capture the event, get the target, get the sibling, etc.) but this doesn't seem like the proper way to do it with angular.
So... what's the appropriate angular way to do this? Should I be writing a custom directive?
Simply create a directive with a new scope and have something like this in the HTML:
<div class="content-item">
<div class="" ng-class="{someClass:hovered}">My transparency should change.</div>
<a ng-mouseover="hovered = true">Mouseover me.</a>
</div>
PLUNKER
Note that if you use ngRepeat, it creates isolate scopes automatically and you don't need the directive.
Directive which founds siblings of the element on mouseover event. You can do what you want with the siblings then:
app.directive('mousiee',function(){
return{
restrict: 'A',
link: function(scope,elem,attrs){
var siblings;
elem.on('mouseover',function(){
siblings = $(elem.parent()).siblings();
console.log(siblings);
});
}
};
});
http://plnkr.co/edit/gWkNpiHMUEUBwuug9C3q?p=preview
(Note that I've added jQuery to your index.html)

Exclude DOM elements from knockout apply binding?

I want to target my knockout viewmodel to certain section of the dom as thus:
ko.applyBindings(MyViewModel,$('#Target')[0]);
However I do NOT want it to apply to all the doms below it. The reason for this is that the whole SPA thing isn't working very well - can't keep up with the jumbo sized viewmodel that results from including every potential interaction into one giant object. Hence, the page is composed of multiple partial views. I want each partials to instantiate its own ViewModel and provide interface for the parent to interact with.
Some sample dom
<div id="Target">
<!--Everything here should be included except-->
<div data-bind="DoNotBindBelowThis:true">
<!--Everything here should NOT be included by the first binding,
I will specifically fill in the binding with targetted
ApplyBind eg. ko.applyBindings(MyOtherViewModel, $('#MyOtherTarget')[0])
to fill the gaps-->
<div id="MyOtherTarget">
</div>
</div>
</div>
Again how can I exclude an entire dom tree below the div tagged with DoNotBindBelowThis from applyBindings?
Take a look at the blog post here: http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html
Basically, you can create a custom binding like:
ko.bindingHandlers.DoNotBindBelowThis = {
init: function() {
return { controlsDescendantBindings: true };
}
};

backbone not rendering the $el but can reference element directly

This is killing me, being reading the examples on this site but can't figure out why it works like this.
I want to pass back values to my view, which has buttons that you can use to change the values.
If I use the following
this.$el.empty().html(view.el)
View.el contains the correct html, but those not render on the screen. If I use the following
$("#handicap").html( view.el);
The values get displayed on screen but the events no longer get picked up eventhough if I put an onclick function in the html code it kicks off.
Ideally I would like to get this.$el.empty().html(view.el) working. It has to do with context but can't see why.
I have created a jsbin here http://jsbin.com/iritex/1/edit
If I have to use $("#handicap").html( view.el), do I need to do something special to unbind events. I have tried undelegate everything but that didn't do the trick either.
thanks
A Backbone View's el property will always contain a reference to a valid DOM object. However, that DOM object may or may not be in your display tree. It's up to you to make sure it's in the display tree when you need it to be. This functionality lets Backbone maintain the state of it's View element without it being rendered to the screen. You can add and remove a view from the screen efficiently, for example.
There are a few ways to get your View's element into the display tree.
1) Associate the view with an existing DOM element on the page by passing in a jquery selector to the initializer as the "el" property.
var view = new MyView({el: '#MyElementSelector'});
2) Associate the view with an existing DOM element on the page by hardcoding the jQuery selector it into the view's "el" property.
var MyView = Backbone.View.extend({
el: '#MyElementSelector'
});
3) Render it to the page from within another view
var view = new MyView();
view.render();
this.$el.empty().html(view.el);
If you're interested, I show examples in a Backbone Demo I put together.
You need to put both views into the DOM. Wherever you create the view that above is this needs to be inserted into the DOM. If you do that, then the first line will work fine this.$el.empty().html(view.el).

Categories

Resources