I've wrapped bootstrapTable (https://github.com/wenzhixin/bootstrap-table) into a directive, like this:
Vue.directive('bootstraptable', {
priority: 1000,
params: ['url', 'resource-name'],
bind: function () {
var _self = this;
$(this.el)
.bootstrapTable({
pagination: true,
pageSize: 15,
pageList: [],
sidePagination: 'server',
url: this.params.url,
queryParams: function (params) {
return params;
},
cookie: true,
cookieExpire: '24h',
cookieIdTable: this.params.resourceName + '-table',
locale: 'it-IT'
}).on('load-success.bs.table', function (e, data) {
$('[data-toggle="tooltip"]').tooltip();
_self.vm.$compile(_self.vm.$el);
});
},
update: function (value) {
$(this.el).val(value)
},
unbind: function () {
$(this.el).off().bootstrapTable('destroy')
}
});
The JSON returned from the server contains a button with a v-on directive so I have to recompile the injected HTML rows to have the button directives properly working.
Anyway, it seems that the following code isn't working:
_self.vm.$compile(_self.vm.$el);
Am I missing something obvious?
The $compile method needs to be called on the elements that have to be compiled, not on the vm root element.
I changed the line:
_self.vm.$compile(_self.vm.$el);
with:
_.each($('[recompile]'), function(el){
_self.vm.$compile(el);
});
and added the attribute "recompile" to all the HTML elements that need to be recompiled.
This seems to be working as expected, do not hesitate to answer if there is a more conventional way to do that.
Related
I need to pass an object to some jQuery plugin. The plugin is applied to multiple elements. The data passed is obtained from the element the plugin is applied to.
How can I pass such an object to a Query plugin which includes data from the element applied to (i.e. $(this).parent().parent().data('id'))?
EDIT. without iterating over the results and applying the plugin each time.
$('#main').find('tr a.upload').fileupload({
formData: {id:$(this).parent().parent().data('id'),foo:'bar'}, //id is not included
start: function() {
//Doesn't work
$(this).fileupload({
formData: {example: 'test'}
});
},
done: function(e, data) {
console.log(this); //Am able to access element
},
});
Try this:
$('#main').find('tr a.upload').fileupload({
formData: function(form) {
return {
id: form.parent().parent().data('id'),
foo:'bar'
};
},
done: function(e, data) {
console.log(this); //Am able to access element
},
});
The function is received the form as a parameter, not as the this context.
I'm writing custom Angular directives for a new application and unit testing them using Jasmine. However, I can't for the life of me figure out how to get full code coverage (or even 80%) on the Kendo Grid Datasource.
I have a custom Angular Kendo grid directive that looks like this:
function customKendoGrid() {
return {
scope: {
hiPageSize: "="
},
template: "<div kendo-grid k-options='gridOptions' k-ng-delay='gridOptions'></div>",
controller: "hiKendoGridCtrl"
};
}
I did this so I could put a custom object on the scope for the grid directive. My controller looks like this:
function hiKendoGridCtrl($scope, $http, hiKendoGridSvc) {
var initialData = {
page: 1,
pageSize: 2,
type: "initial"
};
if(angular.isUndefined($scope.initialLoad)){
$scope.initialLoad = true;
}
var firstPageData = hiKendoGridSvc.getFirstPage(initialData);
firstPageData.then(function (result) {
var columnSet = result.ColumnSet;
var dataModel = result.Model;
var GridModel = kendo.data.Model.define(dataModel);
var firstPage = result.Data;
var totalResults = result.Total;
$scope.gridOptions = {
dataSource: {
schema: {
data: "Data",
total: function () { return totalResults; }, // NOT COVERED
model: GridModel
},
transport: {
read: function (options) {
if ($scope.initialLoad) {// NOT COVERED
$scope.initialLoad = false;// NOT COVERED
options.success({ Data: firstPage });// NOT COVERED
} else {
var requestData = {// NOT COVERED
page: options.data.page,
pageSize: options.data.pageSize,
type: "page"
};
$http({ method: 'POST', url: 'Home/DataSourceResult', data: requestData }).success(
function (data) {
options.success(data);// NOT COVERED
}).error(
function (data, status, headers, config) {
console.log(data);// NOT COVERED
console.log(status);// NOT COVERED
console.log(headers);// NOT COVERED
console.log(config);// NOT COVERED
});
}
}
},
serverPaging: true,
pageSize: $scope.hiPageSize
},
scrollable: true,
pageable: {
input: true,
numeric: false,
refresh: true
},
editable: true,
columns: columnSet,
sortable: true,
groupable: true
};
});
}
Explanation of above
An initial call is made to the server to get all grid configuration (schema, columns, first page of data, and total). All subsequent calls go to the same URL with different post parameters just retreiving a page of data from the server.
My problem is that I can't seem to find ways to traverse the code paths shown above as "NOT COVERED" in a comment.
I invoke both the grid and the controller in separate unit tests, but can't seem to get a Kendo Grid to compile and invoke the different paths above.
My current two tests for the controller and the directives are:
Controller
beforeEach(inject(function($http){
ctrl = $controller("hiKendoGridCtrl", {
$scope: $scope,
$http: $http,
hiKendoGridSvc: hiKendoGridSvcMOCK
});
$scope.$digest();
}));
And with that I can assert all kinds of things for the controller, including that the correct '$scope.gridOptions' are defined.
Directive Test
// I set up the scope as new rootScope and set compile = $compile in the beforeEach of this test.
it("should output the correct HTML", function () {
catchPOST.respond({
data : responseDataMOCK
});
element = '';
element = compile(element)(scope);
scope.$digest();
expect(element[0].innerHTML).toContain('');
}
But this does not catch the above descriptions in the controller above.
I have also tried something that I consider quirky, but something similar to:
// Execute the above directive test compiling my custom directive to a kendo directive
it("Should have the correct scope", function() {
var gridElement = '<div kendo-grid k-options="gridOptions"></div>';
gridElement = compile(gridElement)($scope);
console.log($scope);
console.log(gridElement);
});
So I thought perhaps I could get the GridOptions on scope and then compile the kendo directive manually, but this doesn't resuly in gridElement having any innerHTML at all.
So... the question is:
How can I add new tests/change existing tests to get full code coverage?
How could I/Should I change my code to make it more etstable? I'm hesitant to do this since it took A LOT of effort to get the grid working correctly with a dynamic configuration.
Thanks!
To begin this question a little differently than most SO questions, here's a working bit of code:
thingsToLoad: [{
id: 'thing1',
controller: function(el) {
el.my_project_controller1({});
}
}, {
id: 'thing2',
controller: function(el) {
el.my_project_controller2({});
}
}],
init: function() {
var self = this;
$.each(self.thingsToLoad, function(index, tool) {
tool.controller(self.find('#'+tool.id));
});
}
Figuring that, because functions are first class objects, I should be able to do this:
thingsToLoad: [{
id: 'thing1',
controller: my_project_controller1
}, {
id: 'thing2',
controller: my_project_controller2
}],
init: function() {
var self = this;
$.each(self.thingsToLoad, function(index, tool) {
self.find('#'+tool.id).tool.controller({});
});
}
However, it will stop at thingsToLoad at the first declaration of a controller. The error is: Uncaught ReferenceError: my_project_controller1 is not defined Why does this happen? I can't quite figure it out.
el.my_project_controller1 is not the same as my_project_controller1.
The first is a property of an object, the second is a standalone variable. The error tells you everything; that variable is not defined.
I use backbone.boilerplate for creating a simple application.
I want to create module that can show collections of sites. Each sites has id and title attributes (example [{ id: 1, title: "github.com" }, { id: 2, title: "facebook.com" }].
My router:
routes: {
"": "index",
"sites": "sites"
},
sites: function () {
require(['modules/sites'], function (Sites) {
var layout = new Sites.Views.Layout();
app.layout.setView('#content', layout);
});
}
So, my sites module has layout, which do this:
Sites.Views.Layout = Backbone.Layout.extend({
template: "sites/layout",
className: 'container-fluid',
initialize: function () {
_.bindAll(this);
this.collection = new Sites.Collections.Sites();
this.collection.fetch({
success: this.render
});
},
beforeRender: function () {
var siteList = new Sites.Views.SiteList({
collection: this.collection
});
this.setView('.content', siteList);
},
});
Sites.Views.SiteList = Backbone.View.extend({
template: 'sites/list',
beforeRender: function () {
this.collection.each(function (model) {
var view = new Sites.Views.SiteItem({
model: model
});
this.insertView('tbody', view);
}, this);
}
});
Sites.Views.SiteItem = Backbone.View.extend({
template: 'sites/item',
tagName: 'tr',
serialize: function () {
return {
title: this.model.get('title')
};
}
});
ok. and now my question: help me please to choose best way to render one site view when user click on element of collection. I want that it is works like gmail: one screen for all letters and all screen for one letter when it choosed. Maybe you have link with example of similar application. I am waiting for your advices.
Looking at your pastebin code it seems like you have a basic understanding of Backbone, which is certainly all you need to get started.
That being said, you might find this article/tutorial helpful. It walks through the process of building inter-connected views (in the tutorial they are related <select> elements) which use AJAX to update their values:
http://blog.shinetech.com/2011/07/25/cascading-select-boxes-with-backbone-js/
I am trying to dynamically build a extjs form and when I try to add the dynamically built MixedCollection object to the form I get a TypeError: e.mixins.elementCt is undefined error.
<div id="form-#pageSpecificVar" class="grid-container even"></div>
<script>
Ext.define('HeaderForm', {
extend: 'Ext.form.Panel',
initComponent: function () {
var me = this;
Ext.applyIf(me, {
id: Ext.id(),
defaultType: 'textfield',
items: [{
xtype: 'container',
items: [{
xtype: 'textfield',
fieldLabel: 'Test'
}]
}]
});
me.callParent(arguments);
}
});
// Define our data model
Ext.define('HeaderModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'FieldA', type: 'int' }
]
});
var store = Ext.create('Ext.data.Store', {
model: 'HeaderModel',
proxy: {
type: 'ajax',
actionMethods: { create: 'POST', read: 'GET', update: 'POST', destroy: 'POST' },
url: '#Url.Content("~/Test/Header")',
timeout: 1200000,
listeners: {
load: function () {
}
}
}
});
store.load({
scope: this,
callback: function (records, operation, success) {
var form = new HeaderForm();
var formItems = new Ext.util.MixedCollection();
Ext.each(records[0].fields.items, function (item) {
console.log(item);
formItems.add(new Ext.form.DisplayField({
fieldLabel: 'Test'
}));
}, this);
console.log(formItems);
form.add(formItems);
form.loadRecord(records[0].data);
form.render('form-#pageSpecificVar');
}
});
</script>
Another thing I don't understand is, when I put the function inside the load listener, nothing happens. So I had to resort to using the callback event.
Update:
form.add method takes a component or component array, so instead of adding MixedCollection type I refer to formItems.items to add the array of displayfields components.
But for some reason the store listeners load is not getting triggered when store.load is executed, does anyone see a problem with that?
Nevermind this... I figured out... I placed the listener instead of the proxy instead of the store.
Q2
Also something weird is that during the callback method of store.load, the records is not return with the loaded values.
Nevermind this... I figured out... It was the json object I'm passing. Forgot to take it out of the error/data structure for form.
Thanks
MixedCollection isn't an accepted parameter for add, you need to use an array. This info is in the docs.