addStyleClass for ad-hoc styles? - rendering

When writing a control, prior to it's rendering you can add css classes to it's html representation's root dom node:
FooLayout.prototype.init = function() {
this.addStyleClass('fooCssClass');
};
This will work assuming writeClasses is executed during rendering:
oRenderManager.writeClasses();
--
There is another RenderManager function writeStyles which can add in-line styles to the html string buffer:
oRenderManager.addStyle("color", "green");
oRenderManager.writeStyles();
But there doesn't seem to be an analogous function to addStyleClass for adding these at the control level to be picked up during rendering.
Is there a way I can hook into writeStyles outside of the renderer?

I have to ask: What styling cannot be applied on control level with a class that can be done with a specific style attribute?
Anyway, the "solution" (which I do not like) would be to add a delegate to the class and do something on onAfterRendering:
oControl.addDelegate({
onAfterRendering: function() {
this.$().css("opacity", "0.5");
}.bind(oControl)
});
Here is an example: http://jsbin.com/seqemaqedo/1/edit?js,output
But as I said, I would advice against it because using a good name for a class uses less code and is much easier to read:
oControl.addStyleClass("semiTransparent");
Update:
After our discussion in the comments, here is an example of how to wrap a control that does not support setting width and height.
You still have to write CSS that references the inner control structure, but in this case it is very unlikely that it will change:
http://jsbin.com/lohocaqixi/3/edit?html,js,output

Related

Ng-grid with external data and TypeScript: compile error "Cannot set property 'gridDim' of undefined"

Update #1: after the fix I commented about, now my app starts but the grid is not rendered except for its bounding box and filter button and popup. Yet, I get no error from the console, and as far as I can arrive with the debugger, I can see that data got from the server are OK. If I use Batarang, I can see the scope corresponding to my model, correctly filled with items. I updated the downloadable repro solution accordingly. Could anyone explain why ng-grid is not updating here?
I'm starting to play with ng-grid and TypeScript and I'm finding issues as soon as my test app starts up. See the bottom of this post for a link to a full test solution. Surely I have made tons of errors even in these few files, but I'd like to have something to start with and learn more step by step.
The MVC app has two client-side applications:
app.js for the default view (Home/Index). No typescript here, and the whole code is self-contained in this single file. The code is derived from the paging example in the ng-grid documentation and tries to stay as simplest as possible.
MyApp.js for the more realistic sample in another view (Home/Model). This sample uses services, models and controllers and its JS code is compiled from TypeScript. To keep things simple, I'm just storing these components under Scripts/App, in folders for Controllers, Models and Services, and each file contains just a single class or interface. The generated JS files are manually included in the view.
app.js works, except that it has issues with filtering. I posted about these here:
Server-side filtering with ng-grid: binding issue?
MyApp.js has startup issues with ng-grid. As soon as the app starts, a TypeError is thrown in the grid binding:
TypeError: Cannot set property 'gridDim' of undefined
at ngGridDirectives.directive.ngGridDirective.compile.pre (http://localhost:55203/Scripts/ng-grid-2.0.7.js:2708:37)
at nodeLinkFn (http://localhost:55203/Scripts/angular.js:4392:13)
at compositeLinkFn (http://localhost:55203/Scripts/angular.js:4015:15)
at nodeLinkFn (http://localhost:55203/Scripts/angular.js:4400:24)
at compositeLinkFn (http://localhost:55203/Scripts/angular.js:4015:15)
at publicLinkFn (http://localhost:55203/Scripts/angular.js:3920:30)
at resumeBootstrapInternal (http://localhost:55203/Scripts/angular.js:983:27)
at Object.$get.Scope.$eval (http://localhost:55203/Scripts/angular.js:8057:28)
at Object.$get.Scope.$apply (http://localhost:55203/Scripts/angular.js:8137:23)
at resumeBootstrapInternal (http://localhost:55203/Scripts/angular.js:981:15) <div ng-grid="gridOptions" style="height: 400px" class="ng-scope"> angular.js:5754
The only similar issue I found by googling is https://github.com/angular-ui/ng-grid/issues/60, but it does not seem to be related to my case as there the grid options were setup too late.
The server side just has an API RESTful controller returning server-paged, sorted and filtered items.
You can find the full repro solution here (just save, unzip and open; all the dependencies come from NuGet); see the readme.txt file for more information:
http://sdrv.ms/167gv0F
Just start the app and click MODEL in the upper right corner to run the TypeScript app throwing the error. The whole app is composed of 1 controller, 1 service and 1 model.
For starters like me, it would be nice to have a simple working example like this one. Could anyone help?
This error means gridOptions has not yet been defined by the time that Angular attempts to parse ng-grid="yourArray", where yourArray is the same array supplied to gridOptions. I had the same problem after refactoring a previously working ng-grid.
So gridOptions must be defined before the element which has ng-grid="yourArray" attribute applied to it (rather than within that element's own controller).
I resolved this by defining gridOptions in an outer element somewhere (on global/app scope, for instance).
P.S. Maybe there is a better way, but this has worked for me.
Where you are adding data to your grid?
If you are writing $scope.myGrid={data:"someObj"}; in a success call then it won't work.
See the below reason:(which is listed in https://github.com/angular-ui/ng-grid/issues/60)
You can't define the grid options in the success call. You need to define
them on the scope in your controller and then set the data or column
definitions, etc... from the success call.
What you have to do?, First is to see how this made ​​your project and revizar if your queries or data access, the beams through a service, if so this I must add the file that manages routes app, the client side.
remain so.
'use strict';
angular.module('iseApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
**'ngGrid',**
'campaignServices',
'dialinglistServices',
'itemServices'
])
.config(function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
As you are adding your ng-grid in a directive, you have to make sure the grid options are loaded before it tries to parse your html.
You could set a boolean in your link function :
scope.isDirectiveLoaded=true;
And then, in your template, use a ng-if :
<div ng-if="isDirectiveLoaded">
<div ng-grid="myGrid"/>
</div>
I got to the same issue, empty grid was rendered.
The way I got to it in the end was to setup my this.gridOptions in the constructor of the controller, within the component. In the options everything is referenced with $ctrl like this. So the data references $ctrl.gridData. gridData is specified as a property in my component controller. $ctrl is not defined as a property.
This was done in the constructor before the data was loaded. this.gridData was defined after in the constructor and then populated later in another function. The options were defined first, I think this is important from some things I read.
For the event hooks pass null instead of $scope.
this.gridOptions = {
enableGridMenu: true,
minRowsToShow: 25,
rowHeight: 36,
enableRowHashing: true,
data: '$ctrl.gridData',
rowTemplate: this.$rootScope.blockedRowTemplate,
onRegisterApi: ($ctrl) => {
this.gridApi = $ctrl;
this.gridApi.colMovable.on.columnPositionChanged(null, (colDef, originalPosition, newPosition) => {
this.saveState();
});
this.gridApi.colResizable.on.columnSizeChanged(null, (colDef, deltaChange) => {
this.saveState();
});
this.gridApi.core.on.columnVisibilityChanged(null, (column) => {
this.saveState();
});
this.gridApi.core.on.sortChanged(null, (grid, sortColumns) => {
this.saveState();
});
this.gridApi.core.on.filterChanged(null, (grid, sortColumns) => {
this.saveState();
});
}
};
In the row template I was referencing functions defined in my component. Before conversion to a component I referenced functions like this:
ng-click="grid.appScope.jumpToExport(row.entity);"
After conversion to the component I needed to add the $ctrl before the function name like this
ng-click="grid.appScope.$ctrl.jumpToExport(row.entity);"
And this is how the component is referenced in the html
<div ui-grid="$ctrl.gridOptions" ng-if="$ctrl.gridData.length != undefined && $ctrl.gridData.length > 0" class="data-grid" ui-grid-save-state ui-grid-resize-columns ui-grid-move-columns></div>

Dynamically add regions to Marionette layout

I have a layout, but cannot define all of its regions in advance because they are not known.
So later on an ItemView is created and I'd like to create a new region in the layout using the view's ID as the region's name so I can then say:
layout.dynamicRegionName.show(newItemView);
But there is cyclic dependency here.
I haven't rendered the view yet, so I cannot make a reference to its DOM element to be used in the layout's call to .addRegion()
I cannot render it, precisely because I want it to get attached to the DOM tree through the dynamically added region by calling its .show()
#DerickBailey In the Marionette.Layout docs in github I believe there is an error in the example that has: layout.show(new MenuView());
but technically this is close to what we'd need here i.e. to be able to do:
layout.addRegion(VAR_WITH_NEW_REGION_NAME, aViewInstance);
and have this add a new Region into the layout rendering inside it directly the view instance.
Am I missing some other obvious way to achieve this? Is that a known missing functionality? Is there a reason not to have it?
I'm aware of this previous Q: "Dynamically add/remove regions to a layout"
but don't see any clear/definite answer to it.
Marionette v1.0 (v1.0.2 is latest, right now) supports dynamic regions in Layouts.
var MyLayout = Marionette.Layout.extend({
template: "#some-template"
});
var layout = new MyLayout();
layout.render();
layout.addRegion("someRegion", "#some-element");
layout.someRegion.show(new MyView());
In one of my projects, I faced a similar issue. I needed to create a form dynamically, i.e the form would contain different field views that could not be determined prior runtime. I needed the fields to be Marionette views because they had pretty complicated behaviour.
The way I have done it in Marionette 1.4 in CoffeeScript:
class Module.AdditionalOptionsLayout extends Marionette.Layout
tagName: 'form'
initialize: (options = {}) ->
#_fieldViews = options.fieldViews || []
onRender: ->
#_showFields #_fieldViews
_showFields: (fieldViews) ->
fieldViews.forEach (fieldView) => #_addRegion().show fieldView
_addRegion: ->
regionClass = _.uniqueId('field-region__')
#$el.append $("<div class=\"#{regionClass}\"></div>")
#addRegion regionClass, '.' + regionClass
Please, let me know if it needs further explanation or I can clarify this in JS. I am also aware that it is a late answer, however, hope somebody could find it still useful. Also, note - the answer is relevant only for Marionette 1.x

Pass an object to a widget in template

I have a Dojo UI widget that has a widget embedded within it. I need to pass an object to this embedded widget for it to set itself up correctly, but I'm not sure how to do it.
I have been templating in the embedded widget in the template for the wrapper widget, for example:
...<div class="thing"
data-dojo-type="mycompany.widgets.ComplexEmbeddedWidget"
data-dojo-props="stuff: '${stuff}'"></div>...
but this doesn't seem to work, I guess the data is passed as a string maybe?
I'm pulling out this data by setting it to a property in the embedded widget and then referencing it in my postMixInProperties function.
Doubtless this is the wrong approach, what should I be doing to set up an embedded widget such as this?
I think if you are going to use this approach, you want to convert the javascript object json before it is passed to the templated embedded widget.
You can easily do this by requiring 'dojo/json' and doing
this.stuff=jsonModule.stringify(this.stuffAsObject);
As you have already discovered, if you are setting more complex properties, programmatic instantiation is probably the way to go.
If your mycompany.widgets.ComplexEmbeddedWidget is desperate to have the object 'stuff' set allready once it is initialized (in constructor), then im not sure this approach will do, however a simple fix could be removing the ' quotes around ${stuff}?
What happens is basically that you derive the widget with dijit/_TemplatedMixin. This in turn, during buildRendering, calls _stringRepl on 'this' (the widget). I am not completely certain of the flow, since youre working with WidgetsInTemplate..
lets as example, set a widgets attribute to an array via markup:
<div
data-dojo-type="dijit.form.Select"
data-dojo-props="options:[ 'val1', 'val2']">
</div>
As you see, no quotes around the value - or it will render as a string. Lets then change your ComplexEmbedded template to
dojo.declare("exampleName", [_WidgetsInTemplateMixin, _TemplatedMixin], {
templateString: '<div class="outerWidgetDomNode">
...
<div class="thing"
data-dojo-type="mycompany.widgets.ComplexEmbeddedWidget"
data-dojo-props="stuff: ${stuff}"></div>
...
'
});
To instantiate the ComplexEmbeddedWidget.stuff with an object, this needs to be a string. _Templated uses dojo.string.substitute, which probably would fail if given deep nested object.
Markup example:
<div data-dojo-type="exampleName" data-dojo-props="stuff: '{ json:\'Representation\', as:\'String\'}'"></div>
Or via programmatic
var myObj = { obj:'Representation', as:'Object' };
var anExampleName = new exampleName({
stuff: dojo.toJson(myObj) // stringify here
}, 'exampleNode');
Lets know how goes, ive been wanting to look into the presendence of flow with this embedding widgets into template stuff for a while :)
You can programmatically insert widgets. This seems to be be the way to go if the inserted widget requires JavaScript objects to be passed to it.

MVC3 WebGrid Formatting or Styling Column Headers

I'm using the new MVC3 WebGrid. So far so good, just having issues styling/formatting the column headers. The best I've got is a workaround that applies the same css class from the first row of the WebGrid to the table header.
var headerCells = $("#grid tr:eq(0) th");
var firstRowCells = $("#grid tr:eq(1) td");
$.each(firstRowCells, function (index, value) {
$(headerCells[index]).addClass($(firstRowCells[index]).attr("class"));
});
This example obviously lacks a check to make sure there are rows or indeed the specifed element id, but it applies the css class from the first row to the header row meaning you can style independently of each other.
td.my-column-style { width:100px }
th.my-column-style { text-align:right;}
Is there a built in way of styling the column header elements (not just using the headerStyle property)?
No, as of now there is no built-in way to style the header cells independently, only the header row via the headerStyle property.
I think your workaround is good enough.
I know this is an old question, but this may be useful to viewers who stumble across it.
The :nth-child css pseudo selector is your friend, if you don't want to rely on javascript to copy the classes. It is easy to add a class to your webgrid using the tableStyle property, and then you can style the individual headers with the following bit of css:
.webgridclass tr th:nth-child(1){
background:#ff0;
}
.webgridclass tr th:nth-child(2){
background:#f60;
}
Unfortunately, this is not supported in IE8 and earlier IE, but it does have full support in all proper browsers (newer than FF3).
We can do this using of Javascript code as below.
JsFiddle Example
$("table tr th:nth-child(n)").addClass("col-md-1");

Can we have nested targets in Dojo?

I have two divs nested under a parent div and I want all these to be source as well as targets for dojo.dnd.
I want to be able to add nodes to the div over which the content was dropped and also allow the user to move this in between the 3 divs.
Something like this -
http://www.upscale.utoronto.ca/test/dojo/tests/dnd/test_nested_drop_targets.html
This is I gues implemented in older version of Dojo and doesn' seem to work with 1.4
Is the support for nested targets removed? Is there any way to achieve this?
Nested sources/targets are not supported currently. In most cases you can work around this restriction by using independent sources/targets, yet positioning them as you wish with CSS.
I used a workaround for this case. I create another DIV element which positioned at the same place of the nested target with same width and height but with higher z-Index value. Then the new DIV element covers the nested target. When user is trying to drop on the nested target, he actually drops to the above new DIV element. As long as the new DIV element is not nested in the parent drop target, Dojo's dnd operation works well. I usually put the new DIV element as a child of the body element.
What you need to do is to create the new DIV in onDndStart and destroy it in onDndCancel, then everything should work well.
Dojo version 1.10 still does not support nested Dnd.
CSS positioning and overlay div's didn't work for me. But I noticed that dragging an element out of a dndContainer into a parent dndContainer doesn't trigger onMouseOverEvent for the parent.
In case someone is still using dojo and has the same problem, here is my approach to solve this:
Declare your own dndSource e.g. nestedDndSource.js
define([
"dojo/_base/declare",
"dojo/dnd/Source",
"dojo/dnd/Manager"
], function(declare,dndSource, Manager){
var Source = declare("dojo.dnd.Source", dndSource, {
parentSource: null,
onOutEvent: function(){
if(this.parentSource != undefined)
Manager.manager().overSource(this.parentSource)
Source.superclass.onOutEvent.call(this);
}
});
return Source;
})
Use that nestedDndSource for the children instead of dojos and make sure to provide the dndSource of the parent as parentSource-Parameter:
var parentDndSource = new dojoDndSource(parentNode, {..});
var childDnDSource = new nestedDndSource(childNode,{
parentSource: parentDndSource,
onDropExternal: ...
});
Working example: https://jsfiddle.net/teano87/s4pe2jjz/1/