Ng-grid with external data and TypeScript: compile error "Cannot set property 'gridDim' of undefined" - asp.net-mvc-4

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>

Related

Add target blank to a link in a props in Vue.js

I use the ReadMore plugin to crop articles in a page. The plugin provides a props to redirect to a http link when the "read more" is clicked. But I need to display the link in a new tab. The props receives the link in a string.
I tried several ways to add the target blank attribute to this string before passing it to the props. With no success. Like:
http://www.example.com/page-to-see.html target=_"blank"
I used it with or without quotes but in any case, the link works but the attribute is skipped.
Is there a way to intercept this and add a target blank later?
I saw in other questions the use of router-link but I don't know how to manipulate the props content in the first place.
Any clue would be warmly welcomed
Edit: adding more code to give a clearer explanation of the problem I try to solve:
In the template:
<read-more more-str="read more" :text="dwId.texte" :link="dwId.link" less-str="less" :max-chars="540"></read-more>
I get the values from a DB with Axios. The props are specified by the plugin documentation.
The :link must be a string and it's what it gets from the DB. It works. But as I explained, I need to open in a new tab.
Edit: what I tried:
I try to make a computed property that would add the target blank to a string and use it in the read-more props:
computed: {
target: function() {
return this.dwIds.filter((dwId) => {
return dwId.link + target="_blank"
});
},
}
I have two issues here: first , the result is an object and the props requires a string. Furthermore, the way I add the target blank is not correct but I can't find the right syntax yet.
You need to use it as a directive, and override parts of the initial element you're passing. Otherwise there is no way to "intercept" it. Here's the code to the "component" version that won't do the trick for you.

On click function in vue-tables-2 throwing fns.apply is not a function

I'm currently implementing vue-tables-2 (first time) and I've set up a template to show an icon that will fire an event when clicked. However, I'm getting an error that I'm not sure where it's deriving from. The error is the following.
Uncaught TypeError: fns.apply is not a function
at HTMLAnchorElement.invoker (vue.esm.js:1821)
templates: {
edit: function (h, row) {
return </i>
}
The function code itself is as follows.
editSchedulesBtn: function (rowId) {
console.log(rowId)
}
I have found this stackoverflow question have tried implementing it, but no success --> How to bind vue click event with vue tables 2 (JSX)?
Thanks for all assistance in advance.
I see a few problems:
A syntax error:
Instead of
edit: function (h, row) {
return </i>
}
Should be:
edit: function (h, row) {
return </i>
}
Secondly, the this context inside the function refers to the root vue instance, as stated in the docs:
In addition a this context will be available, which refers to the root
vue instance.
So the $parent is obsolete. You might even have to use $children or $refs, depending on your app structure (Explore the tree using the chrome dev tools and you will find the exact "address").
Thirdly, when binding an event with jsx you should not call the method directly, but use the bind method instead (As explained in the answer you have attached):
on-click={this.editSchedulesBtn.bind(this, row.id)} // See the aforementioned answer for ES6 syntax
As an aside, since Vue introduced scoped slots in v2.1, it is best to use those rather than jsx, which requires compilation, and is generally more cumbersome to deal with.

dijit/Tree is not updated when connected to a dojo/store/JsonRest

I have modified the dojo tutorial at http://dojotoolkit.org/documentation/tutorials/1.10/store_driven_tree/demo/demo.html to read from a JsonRest store.
The problem is that the tree display doesn't update when I click "Add new child to selected item" e.g. on the root element, although the update worked in the original tutorial.
I have compared what dojo/store/Memory (from the original tutorial) and dojo/store/JsonRest return after the "put" request:
Memory returns the id of the new object.
JsonRest ends with "return xhr(...)", so it returns a Deferred instead of the new id, which seems not not be understood by the Observable. I can make it work, if I change dojo/store/JsonRest.js to end with:
...
return xhr(...).then(function(data){
return data.id;
};
}
I hope there is a solution without modifying the dojo sources?!
Some more details follow:
This is the definition of my store instead of the original Memory store:
var governmentStore = new JsonRest({
target : "http://localhost:8080/test/gov",
getChildren : function(object) {
return this.query({
parent : object.id
});
}
});
var governmentStore = new Cache(governmentStore,new Memory({}));
(If I remove the Cache and use the JsonRest directly, even the modified JsonRest.js doesn't make the Tree update).
This is the reply from a PUT request to the json server:
{"name":"New Child", "id":0.7243958345}
Please help to allow a dijit/Tree to react on changes of the underlying JsonRest store without messing around with the dojo sources.
Thank you
Dominic
Try wrapping your JsonRest store with an Observable wrapper and seeing if that helps the tree update properly. Also make sure that the model of the tree is functioning properly as that is what should be handling when and where the tree updates by listening to the store.
var memStore = new Memory({});
var store = new Observable(memStore); //Use this store for your tree
var cacheStore = new Cache(governmentStore,memStore);
The idea here is that when you do a PUT, you should be putting into the cacheStore and not the governmentStore. The Cache will do a PUT on the governmentStore but also update the memStore when the PUT is complete which should then trigger the notify in the Observable and pass that information along to the tree.
Using jquery instead of dojo was the solution. I found that I could solve in a few hours of learning jquery all problems that occurred when using dojo. This is mostly due to the quality of the documentation of both libraries and also because dojo seems to have too many bugs to react on new bug reports.

Multiple/nested appllicationHost

Drupal's Nuget sample is turning out to be nice, and the docs helpful..
So far, figured out that index.html contains the host named as applicationHost:
<div id="applicationHost"></div>
and was filled because main.js loaded the shell.js module:
router.mapNav('welcome');
...
app.setRoot('viewmodels/shell', 'entrance');
that, upon view activation routed to the welcome module, therefore welcome view:
activate: function () {return router.activate('welcome');}
Great.
What I'm now trying to figure out is how to have nested 'applicationHost' containers, loading views into either one independendantly.
An example might be
router.mapNav('settings');
router.mapNav('settings/mailserver'); //but this one render within an inner container.
router.mapNav('settings/messages'); //but this one render within an inner container.
...
app.setRoot('viewmodels/shell', 'entrance');
So that settings.html is rendered, with another container within it and it's viewmodel some thing akin to:
//As restauants view comes in, it sets it's inner view to something
activate: function () {return router.activate('settings/mailserver');}
Related to this first question -- if there is a link on another page to settings/messages, or settings/mailserver, how do I ensure that the parent settings is ensured to be visible first?
Thanks very much.
You would like to use Visual Composition.
e.g.
<div>
<div data-bind="compose:'viewmodels/header'"></div>
</div>
Please refer:
http://durandaljs.com/documentation/Using-Composition/
I think you're looking for child routers http://durandaljs.com/documentation/Using-The-Router.html

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.