My jquery skills are not excellent. What I usually do is take plugins then eventually figure out what I need to do.
What I am doing right now is that I have a slideshow script, and I want it so that when the next slide becomes active / visible, a function is run. What that function does is check for any ID that has a type of css (the type being set in an array), and then adding a class to it. The reason I'm doing it like this is because it needs to expand do different things depending on what the type is.
I have some general code, but I really don't know enough to figure out what I'm doing. Here is what I have thus far:
The function:
function doAnimate(index) {
var item = $animations[index];
for (var i = 0; i < item.length; i++) {
var obj = item[i];
if (obj['type'] = "css") {$(this).addClass('in');}
};
}
The Array:
var $animations = [
[{
ID: "s5-01",
type: "css"
}],
[{
ID: "s5-02",
type: "css"
}],
[{
ID: "s5-03",
type: "css"
}],
[{
ID: "s5-04",
type: "css"
}],
[{
ID: "#5-05",
type: "css"
}]
];
This is the slideshow script and where it should run:
carousel.owlCarousel({
... cut stuff out that isn't important
pagination:true,
afterMove: function(elem) {
doAnimate();
}
});
And here is how this particular one is set up in the HTML
<div class="slide" id="slide-05">
<img id="s5-01" src="img1.png" />
<img id="s5-02" src="img2.png" />
<img id="s5-03" src="img3.png" />
<img id="s5-04" src="img4.png" />
<img id="s5-05" src="img5.png" />
</div>
So I have it like that, and the browser console says: Uncaught TypeError: Cannot read property 'length' of undefined
I am basically kind of lost, and in need of some assistance to get this working. I'm sure it is something basic that I just can't see, or perhaps I have it set up all wrong.
Any advice would be greatly appreciated!
I believe it should be $('.animations:eq(index)') not $animations[index]
Since $animations is not a valid selector.
Check this http://api.jquery.com/eq-selector/ for more info
There are a couple of things you need to fix in order for this to work.
JavaScript Array
// Array
var $animations = [
[{
'id': "s5-01",
'type': "css"
}],
[{
'id': "s5-02",
'type': "css"
}],
[{
'id': "s5-03",
'type': "css"
}],
[{
'id': "s5-04",
'type': "css"
}],
[{
'id': "s5-05",
'type': "css"
}]
];
Then your function:
JavaScript Function
// Animate function
function doAnimate(index) {
// Check array
for (var i = 0, len = $animations.length; i < len; i++) {
// If Array ID & Type match element ID
if( $animations[i][0].id == index &&
$animations[i][0].type == 'css' ) {
// Add class
$('img#'+index).addClass('in');
}
}
}
For this to work you must pass to doAnimate() the element ID, check my jsFiddle example: http://jsfiddle.net/sfhbX/
Another solution using $.each()(http://api.jquery.com/jQuery.each/). I also simplified your array so that I can do this: $animations[01].id and get s5-02 instead of $animations[1][0].id to get same value.
Array:
var $animations = [
{
'id': "s5-01",
'type': "yyy"
},
{
'id': "s5-02",
'type': "css"
},
{
'id': "s5-03",
'type': "css"
},
{
'id': "s5-04",
'type': "css"
},
{
'id': "s5-05",
'type': "css"
}
]
Same HTML stuff. Here's the JavaScript:
function doAnimate(index) {
var item = $animations[index];
$.each($animations, function(idx, val) {
// console.log(val); //logs the value of val to console at index `idx`
//check the `id` val at index idx if equal to the passed item.id
if (val.id === item.id && val.type === 'css') {
$("#"+item.id).addClass('in');
return false;
}
//and just to test if not equal to `css`
if (val.id === item.id && val.type !== 'css') {
$("#"+item.id).addClass('yy');
return false;
}
});
}
Demo fiddle here: http://jsfiddle.net/KpmNL/
You need to change this line:
if (obj['type'] = "css") {$(this).addClass('in');}
There are two problems with that. The first is that your if condition uses = (assignment) rather than == or === (comparison). The second is that you're using this, but what this refers to isn't the element you want; you instead want to select based on the id property of obj, so:
if (obj['type'] === "css") {$('#' + obj.id).addClass('in');}
Then in this section of code:
carousel.owlCarousel({
// cut stuff out that isn't important
pagination:true,
afterMove: function(elem) {
doAnimate();
}
});
That call to doAnimate() needs to be changed to pass an index for an element in your $animations array.
Related
document_table_Settings : function ()
{
return{
rowsPerPage: 5,
showNavigation: 'auto',
showColumnToggles: false,
fields: [
{key: 'para',label: 'Para',sortable: false},
{key: 'desc', label: 'Description',sortable: false},
{
key: 'rowId', label: 'Delete',sortable: false, fn: function (rowId, object) {
var html = "<button name='Del' id=" + rowId + " class='btn btn-danger'>Delete</button>"
return new Spacebars.SafeString(html);
}
},
{
key: 'rowId', label: 'Edit',sortable: false, fn: function (rowId, object) {
var html = "<button name='edit' id=" + rowId + " class='btn btn-warning'>Edit</button>"
return new Spacebars.SafeString(html);
}
}
]
};
}
I want to show description entries having show more and show less feature .As the description is long enough. so after 100 character it shows button to toggle.
If I understand you correctly, you are trying to only show the first 100 characters of the 'Description' column in the Reactive Table and then add some mechanism so that the user can click or rollover to see the entire 'Description' text.
There are a few ways to achieve this and I have provided two options below (in order of simplicity).
For a low tech rollover option, truncate the text to only show the first 100 characters, add an ellipsis (...) to the end of your text, then use the title property in a span element to show the full text on rollover.
First you will need to define a 'truncate' Template helper (I would make this a global helper so that you can use anywhere in your app).
Template.registerHelper('truncate', function(strValue, length) {
var len = DEFAULT_TRUNCATE_LENGTH;
var truncatedString = strValue;
if (length && length instanceof Number) {
len = length;
}
if (strValue.length > len) {
truncatedString = strValue.substr(1, len) + "...";
}
return truncatedString;
});
Then create a new Template for the column.
<template name="field_description">
<span title="{{data.description}}">{{truncate data.description}}</span>
</template>
And finally, change your Reactive Table configuration to use a Template.
fields: [
...,
{ key: 'desc', label: 'Description', tmpl: Template.field_description }
...,
];
For a slightly more complicated option, you can take a similar approach but add a clickable link that would show more or less detail. To get it to work you have to define a few Reactive Vars, define an event handler, and change your 'Description' Template accordingly. Here is a rough solution that should work.
Change your template like so.
<template name="field_description">
<span>{{truncatedDescription}}
{{#if showLink}}
{{linkState}}
{{/if}}
</span>
</template>
Then add the necessary logic to your field_description template (including an event handler).
import { Template } from 'meteor/templating';
import './field-description.html';
Template.field_descirption.onCreated(function() {
const MAX_LENGTH = 100;
this.description = new ReactiveVar(Template.currentData().description);
this.showMore = new ReactiveVar(true);
if (this.description.get().length > MAX_LENGTH) {
this.description.set(Template.currentData().description.substr(1, MAX_LENGTH));
}
this.showLink = () => {
return Template.currentData().description.length > MAX_LENGTH;
};
this.toggleTruncate = () => {
if (this.showMore.get()) {
this.description.set(Template.currentData().description);
this.showMore.set(false);
} else {
this.description.set(Template.currentData().description.substr(1, MAX_LENGTH));
this.showMore.set(true);
}
};
});
Template.field_descirption.helpers({
truncatedDescription: function() {
return Template.instance().description.get();
},
showLink: function() {
return Template.instance().showLink();
},
linkState: function() {
if (Template.instance().showMore.get()) {
return 'show more';
} else {
return 'show less';
}
};
});
Template.field_descirption.events({
'click .js-more-less'(event, instance) {
instance.toggleTruncate();
},
});
Lastly, make sure your Reactive Table config is still setup to use a Template for the field.
fields: [
...,
{ key: 'desc', label: 'Description', tmpl: Template.field_description }
...,
];
Note that the second option makes use of Meteor's Reactivity to solve the problem. Let me know if you need additional explanation on how the 2nd solution works.
That should do it!
I'm trying to set up a form on my site, and want to use some dynamic dropdown.
I found Selectize.js, which seems like a good solution, however I'm struggling to find out how to get the ID's from the selected option when I post the form.
As in user selects "Banana" and selectize should return 2 as value for the post
The obvious answer would of course be to change valueField to 'id' however that messes up the createFilter so that's a no go..
I've made a jsfiddle with what I have so far: http://jsfiddle.net/imfpa/Lh3anheq/16/
HTML:
<form>
<select id="item-type" placeholder="Choose type...">
</select>
</form>
javascript:
function hasOwnPropertyCaseInsensitive(obj, property) {
var props = [];
for (var i in obj) if (obj.hasOwnProperty(i)) props.push(i);
var prop;
while (prop = props.pop()) if (prop.toLowerCase() === property.toLowerCase()) return true;
return false;
}
var REGEX = '[a-zA-ZæøåÆØÅ][a-zA-ZæøåÆØÅ ]*[a-zA-ZæøåÆØÅ]';
$('#item-type').selectize({
persist: true,
valueField: 'text',
labelField: 'text',
searchField: ['text'],
options: [
{id: '1', text: 'Apple'},
{id: '2', text: 'Banana'},
{id: '3', text: 'Orange'},
{id: '4', text: 'Cherry'},
],
createFilter: function(input) {
var match, regex;
regex = new RegExp('^' + REGEX + '$', 'i');
match = input.match(regex);
if (match) {
console.log(match[0]);
return !hasOwnPropertyCaseInsensitive(this.options, match[0]);
}
return false;
},
create: true
});
jsFiddle demo (http://jsfiddle.net/json/Lh3anheq/35/)
Okay, based on our discussion in the comments above, you want the selectize.js to return the id of the selected item, and also let users create unique items.
You are right about the id: you just need to replace the valueField: 'text' with valueField: 'id'.
Now we need to fix the decision making in your function hasOwnPropertyCaseInsensitive.
The first argument in this function is an object of objects. If you see the console output, for this.options of your selectize element, you will see roughly the following structure (valueField is already replaced with id here):
{
idOfItem1: {
id: idOfItem1,
text: textOfItem1
},
idOfItem2: ...
}
Here is what the web inspector prints out for console.log(this.options):
So, we can iterate over all objects and still have the display value in the field text, and this is exactly the string that we need to compare against the user input for uniqueness.
function hasOwnPropertyCaseInsensitive(options, userValue) {
var exists = false;
for (var option in options) {
if (options.hasOwnProperty(option)) {
if (options[option].text.toLowerCase() === userValue.toLowerCase()) {
exists = true;
break; // break from the loop when the match is found. return true works as well.
}
}
}
return exists;
}
Note! The id of an element created by a user will be the same as the display value. I.e. if I add a new element to the list, e.g. Test, it will look like this:
{
Test: {
id: "Test",
text: "Test"
}
}
Please see the jsFiddle demo (http://jsfiddle.net/json/Lh3anheq/35/) and let me know if I missed something.
I'm using jsTree to show a tree with checkboxes. Each level of nodes is loaded on-demand using the json_data plugin.
If a node's descendent is checked, then that node should be in an "undetermined state" (like ACME and USA).
The problem is, the tree starts out collapsed. ACME looks unchecked but should be undetermined. When I finally expand to a checked node, jsTree realizes the ancestors should be undetermined.
So I need to be able to put a checkbox in the undetermined state without loading its children.
With jsTree you can pre-check a box by adding the jstree-checked class to the <li>. I tried adding the jstree-undetermined class, but it doesn't work. It just puts them in a checked state.
Here's my code:
$("#tree").jstree({
plugins: ["json_data", "checkbox"],
json_data: {
ajax: {
url: '/api/group/node',
success: function (groups) {
var nodes = [];
for (var i=0; i<groups.length; i++) {
var group = groups[i];
var cssClass = "";
if(group.isSelected)
cssClass = "jstree-checked";
else if(group.isDecendantSelected)
cssClass = "jstree-undetermined";
nodes.push({
data: group.name,
attr: { 'class': cssClass }
});
}
return nodes;
}
}
}
})
My Question
How do I set a node to the undetermined state?
I had the same problem and the solution I found was this one:
var tree = $("#tree").jstree({
plugins: ["json_data", "checkbox"],
json_data: {
ajax: {
url: '/api/group/node',
success: function(groups) {
var nodes = [];
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
var checkedState = "false";
if (group.isSelected)
checkedState = "true";
else if (group.isDecendantSelected)
checkedState = "undetermined";
nodes.push({
data: group.name,
attr: { 'checkedNode': checkedState }
});
}
return nodes;
},
complete: function () {
$('li[checkedNode="undetermined"]', tree).each(function () {
$(this).removeClass('jstree-unchecked').removeClass('jstree-checked').addClass('jstree-undetermined');
});
$('li[checkedNode="true"]', tree).each(function () {
$(this).removeClass('jstree-unchecked').removeClass('jstree-undetermined').addClass('jstree-checked');
});
$('li[checkedNode="false"]', tree).each(function () {
$(this).removeClass('jstree-checked').removeClass('jstree-undetermined').addClass('jstree-unchecked');
});
}
}
}
});
Hope it helps you!
Maybe this changed in the meanwhile...
But now (version 3.0.0) the really simple solution works:
{
id : "string" // will be autogenerated if omitted
text : "string" // node text
icon : "string" // string for custom
state : {
opened : boolean // is the node open
disabled : boolean // is the node disabled
selected : boolean // is the node selected
undetermined : boolean // is the node undetermined <<==== HERE: JUST SET THIS
},
children : [] // array of strings or objects
li_attr : {} // attributes for the generated LI node
a_attr : {} // attributes for the generated A node
}
Learned directly from the source code at: https://github.com/vakata/jstree/blob/6507d5d71272bc754eb1d198e4a0317725d771af/src/jstree.checkbox.js#L318
Thank you guys, and I found an additional trick which makes life a little better, but it requires a code change in jstree.js. Looks like an oversight:
Look at the get_undetermined function, and scan for the keyword break. That break should be a continue.
If you make that one change, then all you need to do is provide the state (for the main object and its children), and jstree will automatically take care of cascading upwards for undetermined state. It was bailing out early from the scripting and failing to catch all the undetermined nodes properly, requiring the above ugly workarounds for styling and such.
Here's my config (no special attrs or complete() function required) using AJAX:
var tree = $('#jstree').jstree({
"core": {
"themes": {
"variant": "large"
},
'data': {
'url': function (node) {
return "{{API}}/" + node.id + "?product_id={{Product.ID}}"
},
'dataType': 'json',
'type': 'GET',
'success': function (data) {
if (data.length == 0) {
data = rootStub
}
return {
'id': data.id,
'text': data.text,
'children': data.children,
'state': data.state,
}
}
}
},
"checkbox": {
// "keep_selected_style": false,
"three_state": false,
"cascade": "undetermined"
},
"plugins": ["wholerow", "checkbox"],
});
I am running a weird problem when I try to set Grid Filter list dynamically.
Let me explain by my code snippets
I have a column with filter list is defined as
{
text : 'Client',
dataIndex : 'topAccount',
itemId : 'exTopAccount',
filter: {
type: 'list',
options:[]
}
}
I initialize list from store in 'viewready'
viewready: function(cmp,eOpts){
cmp.getHeaderCt().child('#exTopAccount').initialConfig.filter.options = clientsStore.collect('topAccount');
}
===> WORKS GOOD
Now, I have to build the new client store based on the records when user moves to next page. Therefore I build the store in the 'change' event of paging
listeners: {
'change' :function( toolbar, pageData, eOpts ) {
var store = Ext.StoreManager.get('ExceptionRecords');
clientsStore.removeAll(true);
store.each(function(record){
if(clientsStore.findRecord('topAccount',record.data.topAccount.trim()) == null ) {
clientsStore.add({topAccount: record.data.topAccount.trim()})
}
})
Ext.getCmp('exceptionGridContainer').view.refresh;
Ext.getCmp('exceptionGridContainer').view.getHeaderCt().doLayout;
console.log(clientsStore);
Ext.getCmp('exceptionGridContainer').view.getHeaderCt().child('#exTopAccount').initialConfig.filter.options = clientsStore.collect('topAccount');
}
}
I can now see the new data in clientsStore . But Grid filter list is not updated. still showing old data. I tried refresh,layout etc. Nothing helps
Any help will be appreciated
Thanks
Tharahan
Just changing the value of a property does not affect the component rendered or computed state. The menu is created when the list is first initialized. The first time you do that, it works because that's before the initialization, but the second time, that's too late.
If you can grab a reference to the instantiated ListFilter, I think you could force the recreation of the menu this way:
listFilter.menu = listFilter.createMenu({
options: [ ... ] // new options
// rest of the filter config
});
So, supposing you have a reference to your target grid, you could change the options for the column with dataIndex of "topAccount" by a call similar to this:
var listFilter = grid
.findFeature('filters') // access filters feature of the grid
.get('topAccount'); // access the filter for column
listFilter.menu = listFilter.createMenu({
options: [ ... ] // new options
// rest of the filter config
});
--- Edit ---
OK, complete example. Tested, working.
Ext.widget('grid', {
renderTo: Ext.getBody()
,height: 400
,features: [{
ftype: 'filters'
,local: true
}]
,columns: [{
dataIndex: 'a'
,text: 'Column A'
,filter: {
type: 'list'
,options: ['Foo', 'Bar']
}
},{
dataIndex: 'b'
,text: 'Column B'
},{
dataIndex: 'c'
,text: 'Column C'
}]
,store: {
fields: ['a', 'b', 'c']
,autoLoad: true
,proxy: {
type: 'memory'
,reader: 'array'
,data: [
['Foo', 1, 'Bar']
,['Bar', 2, 'Baz']
,['Baz', 1, 'Bar']
,['Bat', 2, 'Baz']
]
}
}
,tbar: [{
text: 'Change list options'
,handler: function() {
var grid = this.up('grid'),
// forget about getFeature, I read the doc and found something!
filterFeature = grid.filters,
colAFilter = filterFeature.getFilter('a');
// If the filter has never been used, it won't be available
if (!colAFilter) {
// someone commented that this is the way to initialize filter
filterFeature.view.headerCt.getMenu();
colAFilter = filterFeature.getFilter('a');
}
// ok, we've got the ref, now let's try to recreate the menu
colAFilter.menu = colAFilter.createMenu({
options: ['Baz', 'Bat']
});
}
}]
});
I was solving similar problem and answers to this question helped me a lot. Local List filter menu is in fact lazy loaded (only created when clicked) and I needed to set filter menu to be reloaded if the grid store has been reloaded with different data. Solved it by destroying of menu after each reload, so on next click menu is recreated:
var on_load = function() {
var grid_header = me.gridPanel.filters.view.headerCt
if (grid_header.menu) {
grid_header.menu.destroy();
grid_header.menu = null;
}
}
I have the the repeater.js in which m getting the values on load.that is setupItem method..
I want to get the same value on click of an button..This problem m getting in case of both list and repeater...data are loading onload but on button its not working...can anyone help....
enyo.kind({
name: "enyo.sample.RepeaterSample",
classes: "enyo-fit repeater-sample",
components: [
{kind: "Repeater", onSetupItem:"setupItem", components: [
{name:"item", classes:"repeater-sample-item", components: [
{tag:"span", name: "personNumber"},
{tag:"span", name: "personName"}
]}
]},
//{kind: "onyx.Button", content:"Fetch", ontap:"setupItem"}
],
create: function() {
this.inherited(arguments);
this.$.repeater.setCount(this.people.length);
this.peopleChanged();
},
published: {
people: []
},
peopleChanged: function() {
for(var i=0;i < 5;i++){
this.people[i]="art "+i;
}
this.$.repeater.setCount(this.people.length);
},
setupItem: function(inSender, inEvent) {
var index = inEvent.index;
var item = inEvent.item;
alert(this.people[index]);
var person = this.people[index];
//item.$.personNumber.setContent((index) + ". ");
item.$.personName.setContent(person);
//item.$.personName.applyStyle("color", person.sex == "male" ? "dodgerblue" : "deeppink");
},
});
I think you may be confusing what needs to happen here. The setupItem method should only be called in response to the onSetupItem event that is thrown by the list when you .setCount() on it or .build() it.
So, the handler for ontap of your button should set the contents of the array, then either change the count on the list (with setCount()) or call .build() on it directly.