I have a rallygrid that is configured to display two models: PortfolioItem/Feature and PortfolioItem/Rollup. I want to color them in the grid to differentiate them. I am not garunteed that they will alternate in the grid, or anything like that. I just want to apply a subtle color to the rollups to differentiate them visually.
Can anyone think of an easy way to achieve this?
I have tried:
viewConfig: {
getRowClass: function(record, index, rowParams, store) {
console.log('record',record); // nothing logged in console
console.log('index',index);
return 'colorCodeGrid'; // class never added
}
},
[EDIT]
viewConfig: {
stripeRows: false, // rows are no longer striped
getRowClass: function(record, index, rowParams, store) {
console.log('record',record); // still nothing logged in console
console.log('index',index);
return 'colorCodeGrid'; // class never added
}
},
It is strange to me that the viewConfig does correctly un-stripe the rows, but the getRowClass never gets called. I thought maybe just the viewConfig as a whole was not being used in the case of a rallygrid.
Your approach above with the viewConfig should work- I am going to file a defect on this. The root cause is that the Rally.ui.grid.GridView is blowing away the getRowClass function in its constructor (for internal browser testing purposes- ugghh) rather than checking if there was one supplied and calling that as well.
You can see it the source for the constructor here: https://developer.help.rallydev.com/apps/2.0rc1/doc/source/GridView.html#Rally-ui-grid-GridView
You should be able to work around it by just re-overriding the function before the view is rendered.
[EDIT by asker]
Added the following to the grid, and it worked:
listeners: {
beforerender: function(cmp) {
console.log('beforerender');
console.log('view',cmp);
cmp.view.getRowClass = function(record, index, rowParams, store) {
console.log('record',record); // still nothing logged in console
console.log('index',index);
return 'colorCodeGrid'; // class never added
};
}
},
UPDATE:
I just fixed this in the nightly build, so this should no longer be an issue in public sdk builds beginning with the next public release after 2.0rc2.
Related
I don't quite get the idea behind enquire.js' "setup" handler.
Case:
I want to load content through ajax once when you're not in a small viewport (lt 600px).
Naturally I would do enquire.register('(min-width: 600px)', { setup: myFunction });.
Problem:
Now I tested this multiple times but the setup handler also gets fired when you're in a small screen, which totally eliminates the benefit of the setup handler imo, because you would want to only load the ajax content once you enter a viewport bigger than 600px, wouldn't you?
See example jsfiddle.
Conclusion:
So actually I wouldn't even need the setup handler because I simply could load the content outside the enquire register and would have the same effect. (Which of course isn't what I want...)
Can someone tell me if I just misunderstood the purpose of setup or is there something I'm missing?
Combine with the deferSetup flag to defer the setup callback until the first match. This example illustrates the feature:
enquire.register(someMediaQuery, {
setup : function() {
console.log("setup");
},
deferSetup : true,
match : function() {
console.log("match");
},
unmatch : function() {
console.log("unmatch");
}
});
You can see a working example here: http://wicky.nillia.ms/enquire.js/examples/defer-setup/
I've got an app that I was upgrading to SDK version rc2 from p5. It overrides the getRowClass() function in the viewConfig to change the row color setting the class if there is a tool tip displayed as per the code below...but this code for some reason seems to be broken in rc1 and rc2.
In p5, this function is called once per row (which I can see when it hits the console.log), but does not seeem to be called in rc1/rc2.
Can anybody confirm whether this is a defect is rc2, or a feature that is no longer being supported?
var grid = {
xtype: 'rallygrid',
showPagingToolbar: false,
disableColumnMenus: false,
store: this.gridDataStore,
viewConfig: {
getRowClass: function(record) {
var toolTip = record.get('ToolTip');
console.log('checking tooltip', record);
return toolTip !== null ? 'special-row' : 'normal-row';
},
listeners: { render: this._createToolTip }
},
columnCfgs: this.columnCfgs,
border: 1
};
This is due to a defect in the grid where we are blindly overriding the getRowClass function on the viewConfig w/o checking to make sure there wasn't one there already. Hopefully this defect will be fixed soon. Check out my answer to this other, very similar question: https://stackoverflow.com/a/17891138/728184
You should be able to have your getRowClass function win out by setting it in the beforerender event listener (thereby re-clobbering the one that we put on there, which is really only useful for automated testing anyway and not required in any way for the grid to function correctly).
UPDATE:
I just fixed this in the nightly build, so this should no longer be an issue in public sdk builds beginning with the next public release after 2.0rc2.
This was originally posted on discuss.emberjs.com. See:
http://discuss.emberjs.com/t/what-is-the-proper-use-of-store-filter-store-find-for-infinite-scrolling/3798/2
but that site seems to get worse and worse as far as quality of content these days so I'm hoping StackOverflow can rescue me.
Intent: Build a page in ember with ember-data implementing infinite scrolling.
Background Knowledge: Based on the emberjs.com api docs on ember-data, specifically the store.filter and store.find methods ( see: http://emberjs.com/api/data/classes/DS.Store.html#method_filter ) I should be able to set the model hook of a route to the promise of a store filter operation. The response of the promise should be a filtered record array which is a an array of items from the store filtered by a filter function which is suppose to be constantly updated whenever new items are pushed into the store. By combining this with the store.find method which will push items into the store, the filteredRecordArray should automatically update with the new items thus updating the model and resulting in new items showing on the page.
For instance, assume we have a Questions Route, Controller and a model of type Question.
App.QuestionsRoute = Ember.Route.extend({
model: function (urlParams) {
return this.get('store').filter('question', function (q) {
return true;
});
}
});
Then we have a controller with some method that will call store.find, this could be triggered by some event/action whether it be detecting scroll events or the user explicitly clicking to load more, regardless this method would be called to load more questions.
Example:
App.QuestionsController = Ember.ArrayController.extend({
...
loadMore: function (offset) {
return this.get('store').find('question', { skip: currentOffset});
}
...
});
And the template to render the items:
...
{{#each question in controller}}
{{question.title}}
{{/each}}
...
Notice, that with this method we do NOT have to add a function to the store.find promise which explicitly calls this.get('model').pushObjects(questions); In fact, trying to do that once you have already returned a filter record array to the model does not work. Either we manage the content of the model manually, or we let ember-data do the work and I would very much like to let Ember-data do the work.
This is is a very clean API; however, it does not seem to work they way I've written it. Based on the documentation I cannot see anything wrong.
Using the Ember-Inspector tool from chrome I can see that the new questions from the second find call are loaded into the store under the 'question' type but the page does not refresh until I change routes and come back. It seems like the is simply a problem with observers, which made me think that this would be a bug in Ember-Data, but I didn't want to jump to conclusions like that until I asked to see if I'm using Ember-Data as intended.
If someone doesn't know exactly what is wrong but knows how to use store.push/pushMany to recreate this scenario in a jsbin that would also help too. I'm just not familiar with how to use the lower level methods on the store.
Help is much appreciated.
I just made this pattern work for myself, but in the "traditional" way, i.e. without using store.filter().
I managed the "loadMore" part in the router itself :
actions: {
loadMore: function () {
var model = this.controller.get('model'), route = this;
if (!this.get('loading')) {
this.set('loading', true);
this.store.find('question', {offset: model.get('length')}).then(function (records) {
model.addObjects(records);
route.set('loading', false);
});
}
}
}
Since you already tried the traditional way (from what I see in your post on discuss), it seems that the key part is to use addObjects() instead of pushObjects() as you did.
For the records, here is the relevant part of my view to trigger the loadMore action:
didInsertElement: function() {
var controller = this.get('controller');
$(window).on('scroll', function() {
if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
controller.send('loadMore');
}
});
},
willDestroyElement: function() {
$(window).off('scroll');
}
I am now looking to move the loading property to the controller so that I get a nice loader for the user.
I am working on an application and was doing something like this:
dojo.ready(
function(){ require['dojo/parser','dijit/registry','dojo/on'],function(.....){
//find a dijit and wrap it in event handling code.});
I was getting an error indicating that dojo was trying to register a widget with an id that was already in use. To solve the problem I entered this line of code:
//before finding the dijit destroy the existing registry.
However, logically this prevents the next line from working because now no widget exists to which I can connect an event. How can I recover the dijit ids?
The best solution is to find out why your code is trying to register a widget with an id that is already in use and change it to not to do so.
The #mschr's solution should work, but I would advise again using it, as it can break your code in many other places and you are likely to spend hours investigating strange behavior of your application.
Anyway, if you are willing to do it that way and automatically destroy widgets with the same ID, do not override registry.add() method. You could do it, but it does not mean, you should do it (especially in programming). Employ dojo/aspect instead to call a function that will destroy the widget with the same ID before registry.add() is called:
require([
"dojo/aspect",
"dijit/registry"
], function(
aspect,
registry
) {
aspect.before(registry, "add", function(widget) {
if(registry.byId(widget.id)) {
registry.byId(widget.id).destroy();
// this warning can save you hours of debugging:
console.warn("Widget with id==" + widget.id + " was destroyed to register a widget with the same id.");
}
return [widget];
});
});
I was myself curious how to accomplish #mschr solution without that override, so I created an jsFiddle to experiment: http://jsfiddle.net/phusick/feXVT/
What happens once you register a dijit is the following; it is referenced by the dijit.registry._hash:
function (widget) {
if (hash[widget.id]) {
throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
}
hash[widget.id] = widget;
this.length++;
}
Now, every now and then you would have a contentpane in which you would put a widget programatically (programatically, hence dojo.parser handles cpane.unload and derefences / destroys parser-instantiated widgets).
When this happens, you need to hook onto some form of 'unload', like, when your call cpane.set('content' foo) or cpane.set('href', bar). Hook is needed to destroy and unregister the instances you keep of widgets - otherwise you would have a memoryleak in your program.
Normally, once an object has no references anywhere - it will get cleaned out of memory however with complex objects such as a widget might be, 'class-variables' often have reference to something _outside _widget scope which flags the widget unsafe to delete to the garbage collector... Once you get this point, you will know to perform proper lifecycles, yet not before the concept is fully understood..
What you could do is to override the dijit.registry with your own handler and have any widgets that are doublets destroyed automatically like so:
// pull in registry in-sync and with global scoped
// accees (aka dijit.registry instead of dj_reg)
require({
async:false,
publishRequireResult:true
}, [
"dijit.registry"
], function(dj_reg) {
dijit.registry.add = function(widget) {
// lets change this bit
if (this._hash[widget.id]) {
this._hash[widget.id].destroy(); // optinally destroyRecursively
this.remove(widget.id)
}
this._hash[widget.id] = widget;
this.length++;
}
});
I'm trying to use a dijit.form.Select as the editor for my dijit.InlineEditBox. Two problems / unexpected behavior seem to occur:
Inconsistently, the InLineEditBox doesn't have the initial value set as selected
Consistently, after selecting a choice, the value that should be hidden is shown instead of the label.
The width isn't set to 130px
Here's working code: http://jsfiddle.net/mimercha/Vuet8/7/
The jist
<span dojoType="dijit.InlineEditBox" editor="dijit.form.Select"
editorParams="{
options: [
{label:'None',value:'none'},
{label:'Student',value:'stu'},
{label:'Professor',value:'prof',selected:true},
],
style:'width:1000px;',
}"
editorStyle="width: 1000px;"
>
</span>
Any help is greatly appreciated! Thanks!
Okay, after a few MORE hours struggling with the mess that is dijit.InlineEditBox, I think I have the solution to the remaining issue (#2).
EDIT: My first solution to #2 is still flawed; the implementation at http://jsfiddle.net/kfranqueiro/Vuet8/10/ will never return the actual internal value when get('value') is called.
EDIT #2: I've revamped the solution so that value still retains the real (hidden) value, keeping displayedValue separate. See if this works better:
http://jsfiddle.net/kfranqueiro/Vuet8/13/
First, to recap for those who weren't on IRC:
Issue #1 was happening due to value not being properly set as a top-level property of the InlineEditBox itself; it didn't pick it up properly from the wrapped widget.
Issue #3 was happening due to some pretty crazy logic that InlineEditBox executes to try to resolve styles. Turns out though that InlineEditBox makes setting width particularly easy by also exposing it as a top-level numeric attribute. (Though IINM you can also specify a percentage as a string e.g. "50%")
Now, issue #2...that was the killer. The problem is, while InlineEditBox seems to have some logic to account for widgets that have a displayedValue attribute, that logic is sometimes wrong (it expects a displayedValue property to actually exist on the widget, which isn't necessarily the case), and other times missing entirely (when the InlineEditBox initializes). I've worked around those as best I could in my own dojo.declared extensions to InlineEditBox and the internal widget it uses, _InlineEditor - since generally it's a good idea to leave the original distribution untouched.
It's not pretty (neither is the underlying code I dug through to understand and come up with this), but it seems to be doing its job.
But man, this was rather interesting. And potentially pertinent to my interests as well, as we have used this widget in our UIs as well, and will be using it more in the future.
Let me know if anything backfires.
hm...
<span dojoType="dijit.InlineEditBox" editor="dijit.form.Select"
editorParams="{
options: [
{label:'None',value:'none'},
{label:'Student',value:'stu'},
{label:'Professor',value:'prof',selected:true},**<<<<** and this comma is for?
],
style:'width:1000px;',**<<<<** and this comma is for?
}"
editorStyle="width: 1000px;"
>
</span>
Also, when using dijit.form.Select, selected value is not attr "selected" but value.
And if you enter prof inside <span ...blah > prof </span> than your proper selected option will be selected ;)
Dijit select checks for VALUE, not attr.
This may be fixed in recent Dojo - see http://bugs.dojotoolkit.org/ticket/15141 - but using 1.7.3 I found this worked:
In my app directory, at the same level as dojo, dijit and dojox, I created a file InlineSelectBox.js which extends InlineEditBox with code to set the HTML on the associated domNode from the value of the Dijit, and which wires up that code to the onChange() event:
define(["dijit/InlineEditBox",
"dijit/form/Select",
"dojo/on",
"dojo/_base/declare",
"dojo/_base/array"
],
function(InlineEditBox, Select, on, declare, array){
return declare(InlineEditBox, {
_setLabel: function() {
array.some(this.editorParams.options, function(option, i){
if (option.value == this.value) {
this.domNode.innerHTML = option.label;
return true;
}
return false;
}, this);
},
postMixInProperties: function(){
this.inherited(arguments);
this.connect(this, "onChange", "_setLabel");
},
postCreate: function(){
this.inherited(arguments);
this._setLabel();
}
});
});
Then, in my view script:
require(["dojo/ready",
"app/InlineSelectBox",
"dijit/form/Select"
],
function(ready, InlineSelectBox, Select){
ready(function(){
// Add code to set the options array
var options = [];
// Add code to set the initial value
var initialValue = '';
var inlineSelect = new InlineSelectBox({
editor: Select,
editorParams: {options: options},
autoSave: true,
value: initialValue
}, "domNodeToAttachTo");
});
});
I was dealing with this situation a few months ago, and not finding a resolution i made my own algorithm.
I put a div with an event on Onclick that build programatically a Filtering Select on that div with the store i want to use.
function create(id,value){
var name = dojo.byId(id).innerHTML;
dojo.byId(id).parentNode.innerHTML = '<div id="select"></div>';
new dijit.form.FilteringSelect({
store: store,
autoComplete: true,
invalidMessage:"Invalid Selection",
style: "width: 80px;",
onBlur: function(){ },
onChange: function(){ },
required: true,
value: value,
disabled: false,
searchAttr: "name",
id: "status"+id,
name: "status"
},"select");
dijit.byId('status'+id).focus();
}
I used the onBlur event to destroy the widget and the onchange to save by xhr the new value.
The focus is below because the onBlur was not working properly.
note: the function is not complete.