jQuery plugins, scope, and binding a created element to the 'change' event - jquery-plugins

So, I'm writing my first plugin. I'm using the advice on the jQuery website, so my plugin setup looks something like this:
(function( $ ){
var methods = {
init : function( options ) {
var $this = $(this);
// do some code.
// Add an element
$this.append('<select id="test"><option value="yes">Yes</option>' +
'<option value="no">No</option></select>');
// Bind the change handler (chose '.on' after reading the documentation for '.delegate')
$this.on('change', '#test', methods['handler'].call($this, $('#test').val()));
},
handler : function( content ) {
alert ('You chose: ' + content);
}
};
$.fn.testbed = function( method ) {
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist' );
}
};
})( jQuery );
I know that the handler itself is working, because I can substitute
function(){ alert ("You did it!");}
for the function call, and it works.
But, the way I'm calling the function now doesn't work. It's how I call other functions from within other methods, but it doesn't work with a handler.
So, my questions are: (1) How do I make it call the function? and (2) is this the best place to set up the handler?

An id should be unique in the page. Having several elements with the same id will give you a different element from what you think when you try to access it. Don't use an id at all, use this in the callback to get the right element.
There is no reason to create a delegate when you are creating one for each event. Bind the event on the select element directly.
The handler for an event should be a function, not the result of a function call.
init : function( options ) {
// Add an element
this.append('<select><option value="yes">Yes</option>' +
'<option value="no">No</option></select>');
// Bind the change handler
$('select', this).on('change', function() {
methods['handler']($(this).val());
});
},

Related

Cypress get attribute value and assign it to variable in function

I am trying to get attribute value and return it from a function.
Here is the code that is working and can be used in a normal test class (into the integration folder).
describe('Example shows how to get attribute value.', () => {
// 'it' is used to create test case. You can add a name of the test case. You can have multiple test cases in one JS class.
it('Get attribute value.', () => {
// Cypress is not able to work with new tabs. It is not possible to switch between tabs. Cypress can manipulate the DOM tree, so we can change the element attributes and open the hyperlink in the same browser tab.
// 'visit()' method is used for navigating to URL address.
cy.visit('https://demoqa.com/links')
cy.xpath('//*[#id="simpleLink"]').then(function (element) {
// 'prop()' method is used to get the attribute value.
const url = element.prop('href')
cy.visit(url)
})
// Assert URL.
cy.url().should('include', 'demoqa.com').should('eq', 'https://demoqa.com/');
})
If I use the code that way - everything is working as expected.
But if I want to re-use the code and create a function like this:
// Give a value of the variable to use it for next function.
functionName = 'addAttribute';
// Declare a Cypress child custom command.
Cypress.Commands.add(functionName, { prevSubject: 'element' }, (subject: any, attributeName: string, attributeValue: string) => {
// Create a try-catch statement. If the function fails - we will recieve the error message.
try {
// Create the function steps after this comment.
cy
.wrap(subject)
.invoke('attr', attributeName, attributeValue)
.should('have.attr', attributeName, attributeValue)
} catch (error) {
// Create the error log and show it to the UI. Show the function name, the class where the function is located and catched error.
let errorMessage = `----------ERROR! It seems that we have an error. Please review the "${functionName}" function from "${__filename.split(__dirname + "/").pop()}" . The error is: ${error}`;
cy.log(errorMessage);
console.log(errorMessage);
}
})
The result is 'object' and I am not sure how to process it.
Here is the rest of the code:
describe("'getAttribute' custom child command example.", () => {
it("example shows how to use 'getAttribute' custom child command.", () => {
cy.visit('https://demoqa.com/buttons');
let attributeValue = cy.element('xpath','(//*[contains(text(),"Click Me")])[3]').getAttribute('class');
cy.log(`The attribute values is: ${attributeValue}`)
});
});
You have to do as below to have a return value:
let attributeValue = '';
cy.element('xpath', '(//*[contains(text(),"Click Me")])[3]')
.getAttribute('class')
.then((attr) => {
attributeValue = attr;
});
cy.log('The attribute values is:' + attributeValue)

ExtJS 6: TreePicker does not fire change event

See fiddle here: https://fiddle.sencha.com/#fiddle/2iig&view/editor
The docs (https://docs.sencha.com/extjs/6.6.0/classic/Ext.ux.TreePicker.html#event-change) list 'change' in the events section but when I set the value or reset the field this event never fires. The 'select' event fires as expected but that only fires when the user selects a field.
EDIT:
Based on Snehal's suggestion below, I was able to accomplish this using the following override. Not sure if there is a simpler way to do it but this was the best I could manage:
Ext.define('MyApp.overrides.TreePicker', {
override: 'Ext.ux.TreePicker',
setValue: function (value) {
var me = this,
record;
me.value = value;
if (me.store.loading) {
// Called while the Store is loading. Ensure it is processed by the onLoad method.
return me;
}
// try to find a record in the store that matches the value
record = value ? me.store.getNodeById(value) : me.store.getRoot();
if (value === undefined) {
record = me.store.getRoot();
me.value = record.getId();
} else {
record = me.store.getNodeById(value);
}
// zeke - this is the only line I added to the original source
// without this the 'change' event is not fired
me.callSuper([value]);
// set the raw value to the record's display field if a record was found
me.setRawValue(record ? record.get(me.displayField) : '');
return me;
}
});
Because setValue function does not call this.callParent(). You can do something like this in setValue function.
setValue: function(value) {
var me = this,
record;
if (me.store.loading) {
// Called while the Store is loading. Ensure it is processed by the onLoad method.
return me;
}
// try to find a record in the store that matches the value
record = value ? me.store.getById(value) : me.store.getRoot();
me.callParent([record.get('valueField')]);
return me;
},

SAPUI5: Execute Code if Binding has Data

Element binding snippet
var oModel = oView.getModel();
var oPromiseMetadataLoaded = oModel.metadataLoaded();
oPromiseMetadataLoaded.then(function() {
var sObjectPath = oModel.createKey("Project", {
ProjectID: sProjectId
});
oView.bindElement("/" + sObjectPath);
// <HERE>
});
Now I want to execute a function (marked with '// ' where it should go) which uses data from the bound Object. When the data is not there yet (the model is obviously an OData model), I need to attach to the dataReceived event, but when when the data is already there, this event won't fire.
What is the most (UI5) idiomatic way to execute code in both cases? Is there a Promise like oModel.metadataLoaded()? Do I need to consider something, e.g. to probably not read data from an object previously bound to the view?
Maybe you can attach to the change-Event?
oView.bindElement({
path: "/" + sObjectPath,
events : {
change: this._onBindingChange.bind(this),
dataRequested: function (oEvent) {
oView.setBusy(true);
},
dataReceived: function (oEvent) {
oView.setBusy(false);
}
}
});
_onBindingChange : function (oEvent) {
if (this.getView().getBindingContext()) {
//HERE
}
else { //Invalid Binding Context };
}

Backbone, rest, populate collection

I'm trying to do my first webapp with backbone/mvc3 and i would like to have some advices to populate a collection.
Here is a part of my collection
window.TaskList = Backbone.Collection.extend({
model: Task,
url: "../../api/Tasks";
},.......
I can use the crud methods to get/update the models but i've the following problem :
When i open the page, my collection is populated (calling the get method serverside) But i would like to have this kind of behavior :
Page 1 : put/delete/get methods => as usual but the collection has to be populated calling the getTasksByWorkshopId serverside method
Page 2 : put/delete/get methods => as usual but the collection has to be populated calling another serverside method to filter the list
...
(ie : i cant filter the collection client side because of the amount of data)
So, my question is : how to keep a generic collection url (as api/Tasks) and populate the collection with another method (do i have to override smth ?)
(sorry for this newbie question)
Thanks in advance
In a comment to the other answer you said that "When the collection is loaded, the url called is /api/Tasks/Workshop/1 (the good one) but, when i want to update a task, the url called is /api/Tasks/Workshop/1/141 instead of /api/Tasks/141."
In order to "update a task" (a task model, I assume) to a different URL, then your Collection & Model should have different URLs. If you define a collection without specifying the model property, the URL used when saving/fetching/deleting a model will be based off of the collection's URL. The same is also true if the collection's model has no defined url property. See below.
Also, JSFiddle example here.
var WorkshopModel = Backbone.Model.extend({
urlRoot: "api/tasks/"
});
var WorkshopCollection = Backbone.Collection.extend({
model: WorkshopModel,
urlRoot: "api/tasks/workshop",
url: function() { return this.urlRoot + '/' + this.id; },
initialize: function(models, options) {
this.id = options.id;
}
});
var c = new WorkshopCollection(null, { id: 1 });
c.fetch(); // GET => api/tasks/workshop/1
var m = c.add({ id: 300, color: 'red' });
m.save(); // PUT => api/tasks/300
m.destroy(); // DELETE => api/tasks/300
m.fetch(); // GET => api/tasks/300
If you remove the urlRoot property from the WorkshopModel, then the URL that the models use will be the collection.url() + '/' + model.id ( api/tasks/workshop/1/300 )
You can do like this :
window.TaskList = Backbone.Collection.extend({
model: Task,
urlRoot: "../../api/Tasks",
url: function() {
if (/*page 1*/) { // you can access this.options where you can pass parameters to distinct the 2 services, when calling the fetch function
return this.urlRoot + // getTasksByWorkshopId URL ;
} else {
return this.urlRoot + // the other service URL ;
}
} ...
}

pass function to another and call by name

<a id="aHw" href="#" callbackName="helloworld">test</a>
...
<script>
function helloworld() { alert('hello world'); }
</script>
...
question ; how can i produce callBack to pass another function
<script>
...
var cbName = $('#aHw').attr('callbackName');
foo( passfunction ); //How???
...
</script>
<script>
function foo(callBack)
{
callBack(); // call hello world.
}
</script>
thanks in advance.
A function in JavaScript is just an object.
The question(s) don't make terribly much sense to me, but consider the following:
function fn1 () {
alert("fn1")
}
function doIt(cb) {
cb()
}
// direct -- to show point in general
// fn1 evaluates to the function-object, which is then passed
doIt(fn1)
// lookups up fn1 by name, then passes that function-object
// the value "fn1" can be any arbitrary string, such as that which
// was stored in the attr:
// e.g. doIt(window[cbName])
doIt(window["fn1"])
// anon function to iterate sameness
// the anon function returns a new function-object
doIt(function () { alert("anon") })
Happy coding.
Ok. So to have an anchor do something on MouseOver, you'd use this code:
<a id="aHw" href="#" onmouseover="doSomething()">test</a>
You can pass a function to another function this way:
function callSomeFunction( fn )
{
fn();
}
callSomeFunction( alert );
Or you can pass an anonymous function to the above:
callSomeFunction( function(){ alert( "Finally! A message!" ); } );
If you're trying to pass the name of a function as a string (which is a fundamentally bad idea and a terrible risk and hard to debug and DON'T DO IT), then you can use eval:
function callNamedFunction( fn )
{
eval(fn)()
}
Or you might be able to get away with:
function callNamedFunction( fn )
{
(window[fn])()
}
foo( Function('return ' + cbName)() )
I think that's what your after..
But if it's in the browser, and you know that the callback is a global object, you could do..
foo(window[cbName])
Well, if nothing else helps, eval() will:
function foo( callBack ) {
eval( callBack + '()' );
}
If you know where the function is defined (e.g window, or custom namespace) you can invoke it by the string name. Otherwise you would have to eval (bad idea). Also, use data-attributes.
test
...
// invoke runs a function by name in the provided context, or window
function invoke(cbname, context){
return (context || window)[cbname].call();
}
// get attribute through the data method
var funcName = $('a').data('callback');
// run callback
var result = invoke(funcName);

Resources