Adding Drag & Drop to Backbone Todos with jQuery/UI - backbone.js

I'm totally new to backbone.js and have just started delving into the infamous 'todos' app to get a feel for it. I've noticed that localtodos.com includes a nice drag & drop functionality whereas the backbone example doesn't. The localtodos drag & drop is implemented with Mootools however and I'm looking to use jQuery and jQuery UI instead.
I came across an example which looked to have already implemented it but when I've tried it there's a problem with the data-id attribute. Adding:
<%= id ? 'data-id="'+id+'"' : '' %>
to the template prevents me from entering any todo items. I get this error in the console:
Uncaught ReferenceError: id is not defined
But with the drag and drop implementation included in the initialize function of the AppView template does allow for drag and drop:
this.$("#todo-list").sortable({
update: function(event, ui) {
$('div.todo',this).each(function(i) {
var id = $(this).attr('data-id'),
todo = Todos.get(id);
todo.save({order: i + 1});
});
}
});
If I remove the data-id attribute code then I can enter todo items again but when I refresh the page their order isn't saved (obviously).
I just can't figure out what the problem is with the data-id attribute. A more complete example of the code I'm working with is available as gist here.

Related

How to horizontally scroll in a React Application (ag grid) with UI automation (WebdriverIO /JavaScript)?

We have a grid application exactly similar to this demo application:
https://www.ag-grid.com/example.php
I want to horizontally/vertically scroll to any element in this demo site.
I have tried below methods:
browser.moveTo() : is not working and throwing below error :
stale element reference: element is not attached to the page document
browser.scroll(1665, 147.12345) : Is not doing anything. Pass the step.
browser or element.scrollIntoView() :shows not an existing method. Following error appears:
Property 'scrollIntoView' does not exist on type 'Client> & RawResult'.t
and
Property 'scrollIntoView' does not exist on type 'Client> & RawResult'.t
I am using webdriverio and typescript for UI automation.
wdio version:
"webdriverio": "^4.14.4"
Though none of the existing methods worked, but I solved it by using the "Tab" key. I followed below steps:
I click/select the last visible element in the current screen.
I press "Tab" key (Single or multiple as needed)
This scrolled the page horizontally(which solved my purpose for now).
Try to use scroll with javascript:
browser.execute(function() {
document.querySelector('yourElementLocator').scrollIntoView()
})
Keep in mind that code inside browser.execute does not have access to the outer context. So if you want to pass your element inside you will have to specify this argument after executable code:
browser.execute(function(){}, selector)
https://webdriver.io/docs/api/browser/execute.html
browser.waitUntil(
async () => {
browser.keys(["\ue00F"]);
return await countryEle.isDisplayedInViewport();
},
15000,
undefined,
500
);

"Cannot convert undefined or null to object" when click selected kendo tree item

I used Kendo-angular directive for Kendo tree view. I formed datasource using the following method and view as below. I don't get any error if use Jquery style instead of Angular directive.
var dataSource = new kendo.data.HierarchicalDataSource()
$scope.templateData = dataSource ;
<div kendo-tree-view="tree" k-data-source="templateData" k-on-change="onTemplateSelection(dataItem)"></div>
$("#treeview").kendoTreeView({
spriteCssClass: "sprite",
dataSource: dataSource,
dataTextField: [ "AppName", "Name" ],
select: onSelect
});
Tree loads fine and on-change event fires when we click any item on tree. However, when we click the selected item again then change event doesn't trigger and it triggers error that reads.
Uncaught TypeError: Cannot convert undefined or null to object
at Function.keys (<anonymous>)
at Object.ve.proxyModelSetters (kendo.all.min.js:26)
at init.<anonymous> (kendo.all.min.js:86)
at init.trigger (kendo.all.min.js:25)
at init.select (kendo.all.min.js:59)
at init._click (kendo.all.min.js:58)
It's hard to find the error without having all the code but basically the exception you're getting is quite simple. Somewhere in your code you probably have a block that looks like this:
if (object !== "null") { // The null is a string and not actually null
...
Object.keys(object) ...
...
}
Search your code for 'null' or "null". if you don't find it like this, see if you might have misspelled it as well.
I found the mistake. Once I clicked an item on the tree, I used to remove the class, k-"state-selected" from the item. Next time when I click the same item, it seems it uses the same class("k-state-selected") and data-uid to find the selected item. So as it was not present on that item, it used to give the error mentioned above.

angular-google-maps - listen for infowindow domready event

Listening for the domready event in normal js Google maps is relatively easy
as outlined here :
infoWindow = new google.maps.InfoWindow();
google.maps.event.addListener(infoWindow, 'domready', function() {
// whatever you want to do once the DOM is ready
});
However it doesn't seem obvious how to do it in angular-google-maps.
Is there a solution ?
The solution when using Angular Google Maps involves using the infowindow control object - see docs.
As noted in this issue - where I first posted this solution - you can create an empty infowindow control object within the main controller :
$scope.infowindowControl = {};
Then you scope bind your new object in the ui-gmap-window directive definition like :
<ui-gmap-window
options="windowOptions"
closeClick="closeClick()"
control="infowindowControl"
>
On infowindow open (actually unsure at what point) - it will pass five functions to the empty object you created within $scope. Some of these functions are noted in the documentation but in a rather haphazard and non-defined way :
getChildWindows()
getGWindows()
getPlurals()
hideWindow()
showWindow()
The one that you need is getGWindows() - which you put inside your marker click event.
This will get you an array of the open infowindows and you can now attach an event listener in the standard google maps fashion, like :
var windows = $scope.infowindowControl.getGWindows();
console.log('inside click after getGWindows ', windows);
google.maps.event.addListener(windows[0], 'domready', function() {
console.log('infowindow domready just fired !!');
});
While it's not an ideal, well documented or easy solution (and took me a number of hours to figure out) - and passing functions to an empty object is frankly counter-intuitive - it does seem to work.
Plunker here.

in angular, how do I know when the model is rendered already in html

Currently I am using the angular to build a prototype application, I have a list of person to show, therefor I built a model for the person. when the people 's list is loaded from the http, the model is rendered. This works well
<li class="person" ng-repeat="person in people" >
<h1>{{person.displayname}}</h1>
<div class="knockout">
<div id="person-{{person.displayname}}"></div> // to add d3.js svg
</div>
</li>
now what I need additionally is as soon as the model is rendered, to use d3.js to add another svg layer on top of the original html.
However, as the code shown below, when the model is updated, I tried to add svg for each html, but it doesn't work since at that time, the html of model is not rendered
$http.get(href).success(function(data) {
$scope.people= data.people; // update the model
var svg = d3.select('#person-'+data.people[0].name)
.append("svg");
}).error(function(data, status, headers, config) {
});
currently what I do is to set a timeout for adding the svg for each html element, but it is better for me to get notified as soon as the model 's html is rendered completely
thanks
The problem is that you people array is rendered after the success method is left. That is normal angular behavior, because their is no way in JavaScript to intercept your code. Angular checks for changes when your callbacks finished. At the time you try to append you svg stuff, non of the ids are present in the DOM.
You could use $scope.$watchCollection("people", function () { ... and render your stuff in the callback, but binding functionality to ids works against the way angular works. Try to find an angular wrapper for d3.js that you can use it without ids and as a directive.

"Undefined" when using StackMob.Model instead of Backbone.Model

I'm using Backbone.js and StackMob for a mobile app, and have run into a strange problem that I can't get my head around. According to the StackMob docs, StackMob.Model extends Backbone.Model, and thus has access to the same methods, and should work the same way. As I understand it, it's a simple swap to make.
Here's the situation:
I have a collection that's being populated by an API call to FourSquare, which is being rendered out into the view as a list. Each model in the list is rendered with a data-id attribute:
$("ul#resultsList").append("<li class='result' data-id='" + place.get('id') + "'><span class='listingName'>" + place.get("name") + "</span><span class='listingDetail'>" + place.get("location")["address"] + "</span><span class='listingDetail'>" + place.category + "</span></li>");
(The above code is what's looped through to build the list).
The list shows up fine, and everything on that end works as it should. Then, I have the following attached to the click event on each list item:
saveItem: function(e) {
window.item = $(e.currentTarget).data("id");
window.newplace = this.collection.get(window.item);
console.log("New place: "+ window.newplace); //shows the model in Backbone, undefined in StackMob
App.navigate("#add", {
trigger: true
});
},
This code basically looks at the item clicked, goes and gets the ID, then looks up that model in the collection based on the ID and stores it into window.newplace.
Then, the app navigates over to #add, which instantiates the view with window.newplace as the model:
addItem: function() {
if (!window.newplace) {
window.newplace = new Place({});
}
this.changePage(new AddItemView({model:window.newplace}), "slide");
},
Here's the problem:
This works exactly as it should when the Place model is set up as a Backbone.Model. As soon as I switch it to StackMob.Model, it shows window.newplace as undefined.
I can't imagine why switching these should make any bit of difference, but it seemingly does.
Just for the sake of full clarification, here's the model when using Stackmob.Model:
window.Place = StackMob.Model.extend({
...
});
Any ideas? If it didn't work at all, I'd chalk it up to some really stupid thing, but since it works fine extending Backbone, but not StackMob, I'm really confused.
Thanks!
You need to call StackMob.init(..) first before using StackMob.Model, StackMob.Collection and other StackMob items.
Your external script is likely being executed before you call StackMob.init(..).
Hope that helps!
Erick
Ok, I figured out a workaround for this problem.
Here's the deal. When I was calling this.collection.get(window.item), I was looking for the ID property on the Place object being clicked (since I'm passing that ID into the URL, it's easy to retrieve on click).
With Backbone, that ID was showing up as a root property of the object, at place.id. With StackMob, for whatever reason, it wasn't. It was stored in the Attributes object inside Place (as if you'd called place.set("id")).
So, when I went looking to get it by ID, StackMob was looking for the root property "id" on the object, not finding it, and returning "undefined".
By putting this.id = this.get("id") in the initialization of the model, I was able to get it to the root and successfully retrieve the model.
I'm still unsure why that ID was being set at the root with Backbone, but not StackMob, but regardless, at least there's a solution.

Resources