So, I found that array elements can be accessed in Handlebars using:
{{myArray.2.nestedObject}} and {{myArray.0.nestedObject}}
..to get the third and first elements for instance. (handlebars-access-array-item)
Is there a way to get the last element from an array?
I tried creating a helper for it:
Handlebars.registerHelper("lastElement", function(array) {
return array.last(); //Array.prototype extension
});
...and calling it as follows in the template:
{{lastElement myArray}} or even {{lastElement myArray.lastElement nestedArray}}
Sadly, this doesn't work. Helper functions return strings apparently. What I need is a way to be able to do this even with multi-dimensional arrays.
Should work, I've tested it.
Template:
{{last foo}}
Data:
{foo : [1,2,3,4,5,6]}
Helper:
Handlebars.registerHelper("last", function(array) {
return array[array.length-1];
});
The above piece of code works fine in all the cases. But if the array passed if a null array, possibility of the handlebar function throwing error is there. Instead perform a null check and then return the value accordingly.
Related
I'm trying to create an HtmlHelper for jstree nodes. Part of my objective is to encapsulate the "select_node" logic into a re-usable format. For our purposes, we can expect to use a jquery "GET" which will call into an MVC controller method. That URL needs to be dynamic and is passed in via an html custom data attribute. The resultant behavior upon success also needs to be dynamic and is also passed in via the same means. My problem is that, despite the passed in URL being interpreted and called properly (the controller method is hit), I cannot get the "success" portion to work properly. Below are code examples with corresponding comments. The non-working scenario (the last commented attempt with "data.node.li_attr.success") would be the ideal as we would need to pass in multiple lines.
writer.WriteLine("</ul>");
writer.WriteLine("</div>");
writer.WriteLine($"<script>");
writer.WriteLine($"$({treeParamModel.TreeName}).jstree({{"); // base tree definition
// "core" config standard to every jstree
writer.WriteLine("'core':{");
writer.WriteLine($"'multiple':{treeParamModel.IsMultiSelectAllowed.ToString().ToLower()}");
writer.WriteLine("},");
// "types" plugin
writer.WriteLine("'types':{");
writer.WriteLine("'default':{");
writer.WriteLine($"'icon':'{treeParamModel.IconPath}'");
writer.WriteLine("}");
writer.WriteLine("},");
writer.WriteLine("'plugins' : ['types']"); // define which plugins to bring in
writer.WriteLine("})");
writer.WriteLine($".on('select_node.jstree', function (e, data) {{");
writer.WriteLine("$.get({url: data.node.li_attr.get_url,");
writer.WriteLine("success: function (result) { alert(data.node.li_attr.success)}"); // works (correctly displays variable content.)
//writer.WriteLine("success: function (result) { $(data.node.li_attr.success)}"); // WHY? "Uncaught Error: Syntax error, unrecognized expression: alert('hello')"
writer.WriteLine("});");
writer.WriteLine("});");
writer.WriteLine("</script>");
The MVC view passes in the following:
#{
var treeName = "tree";
var success = "alert('hello');";
}
Update
My colleague and I have found that we can ALMOST achieve our desired result by using the "eval()" function.
writer.WriteLine("success: function (result) { eval(data.node.li_attr.success)}");
works as expected with the exception that it stops evaluating content after the first space.
I'm using Angular 4 to develop an app which is mainly about displaying data from DB and CRUD.
Long story short I found that in Angular 4 the component html doesn't like displaying loosely typed object (leaving the space blank while displaying other things like normal with no warning or error given in console) even if it can be easily displayed in console.log output, as shown in a string.
So I made a function in the service file to cast the values into a set structure indicating they're strings.
So now something like this works:
HTML
...
<div>{{something.value}}</div>
...
Component.ts
...
ngOnInit() {
this.route.params.subscribe(params => {
this.pkey = params['pkey'];
this.service.getSomethingById(this.pkey)
.then(
something => {
this.something = this.service.convertToStructure(something);
},
error => this.errorMessage = <any>error);
});
}
...
Code of the function convertToStructure(something)
convertToStructure(someArr: myStructure): myStructure {
let something: myStructure = new myStructure();
something.value = someArr[0].value;
return something;
}
But as I dig into other files for copy and paste and learn skills from what my partner worked (we're both new to Angular) I found that he did NOT cast the said values into a fixed structure.
He thought my problem on not being able to display the values (before I solved the problem) was because of me not realizing it was not a plain JSON object {...} but an array with a single element containing the object [{...}] .
He only solved half of my problem, cause adding [0] in html/component.ts was not able to make it work.
Component.ts when it did NOT work
...
ngOnInit() {
this.route.params.subscribe(params => {
this.pkey = params['pkey'];
this.service.getSomethingById(this.pkey)
.then(
something => {
console.log(something[0].value); //"the value"
this.something = something[0]; //html can't find its value
},
error => this.errorMessage = <any>error);
});
}
...
HTML when it did NOT work
...
<div>{{something[0].value}}</div> <!--Gives error on the debug console saying can't find 'value' of undefined-->
...
And of course when I'm using the failed HTML I only used this.something = something instead of putting in the [0], and vice versa.
So I looked into his code in some other page that display similar data, and I found that he used *ngFor in html to extract the data and what surprised me is that his html WORKED even if both of our original data from the promise is identical (using the same service to get the same object from sever).
Here's what he did in html:
...
<div *ngFor="let obj of objArr" ... >
{{obj.value}}
</div>
...
His html worked.
I'm not sure what happened, both of us are using a raw response from the same service promise but using for loop in html makes it automatically treat the value as strings while me trying to simply inject the value fails even if console.log shows a double quoted string.
What's the difference between having the for loop and not having any for loop but injecting the variable into html directly?
Why didn't he have to tell Angular to use the set structure indicating the values are strings while me having to do all the trouble to let html knows it's but a string?
The difference here is as you said that your JSON is not simple object , its JSON Array and to display data from JSON array you need loop. So, that is why your friends code worked and yours did not. And please also add JSON as well.
How do I iterate over this array in array in AngularJS Javascript code:
[[4,5,6,4,8.7]]
An array is an array.
Use Array.prototype.forEach for iteration.
In this case, each item is an array, and therefore you have an inner forEach as well.
Angular also has a method for forEach. It's called angular.forEach.
You can use it if you want to or need to support ancient browsers.
[[4,5,6,4,8.7]].forEach(function(arr){ arr.forEach(function(item){ console.log(item) }) } );
P.s. to avoid the smartass that is going to say that Array.prototype.forEach wont work on IE8, then it doesn't.
You can use 'angular.forEach'.
angular.forEach([[4,5,6,4,8.7]], function(arr){
angular.forEach(arr, function(value){
console.log(value);
})
});
I have an Ember app consuming a rails based webservice.
On the Rails side, I have some enums, they are simply arrays.
Now, I would like to retreive those enums in the Ember app, and render them for select values.
The webservice returns a JSON response :
get '/grades.json'
{"grades":["cp","ce1","ce2","cm1","cm2"]}
On the Ember side, I created a GradesRoute like this :
App.GradesRoute = Ember.Route.extend({
model: function () {
return Em.$.getJSON('api/v1/grades.json')
}
}));
Then, I think I need it in the controllers where these enums are in use:
App.StudentsController = Ember.ArrayController.extend({
needs: ['grades'],
grades: Ember.computed.alias('controllers.grades')
}
));
So at least I thought I could iterate over the grades in the students template.
{{#each grade in grades}}
{{grade}}
{{/each}}
But I get no output at all... debugging from the template and trying templateContext.get('grades').get('model') returns an empty array []
Any idea on how I could load and access this data ?
So I ended up with ApplicationRoute, which is the immediate parent of StudentsRoute, so needs is relevant in this case.
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
Em.$.getJSON('api/v1/enums.json').then(function(data){
controller.set('grades', data['grades']);
controller.set('states', data['states']);
}
}
});
Now I can create an alias for each enums I need to use accross my app.
App.StudentsController = Ember.ArrayController.extend({
needs: ['application'],
grades: Ember.computed.alias('controllers.application.grades'),
states: Ember.computed.alias('controllers.application.states')
});
I'm still not confident enough to be sure this is the way to go, any suggestion is welcome !
You just have some of your paths mixed up. In StudentsController, controllers.grades refers to the actual controller, not it's model. The following code should clear things up as it's a bit more explicit in naming.
App.StudentsController = Ember.ArrayController.extend({
needs: ['grades'],
gradesController: Ember.computed.alias('controllers.grades'),
grades: Ember.computed.alias('gradesController.model.grades')
});
Also, be aware that using needs only works if your grades route is a direct parent of your students route. If it's not a direct parent, you won't get back the data you want.
Consider a REST URL like /api/users/findByCriteria which receives POSTed JSON that contains details of the criteria, and outputs a list of Users.
How would one call this with Restangular so that its results are similar to Restangulars getList()?
Restangular.all('users').post("findByCriteria", crit)... might work, but I don't know how to have Restangular recognize that the result will be a list of Users
Restangular.all('users').getListFromPOST("findByCriteria", crit)... would be nice to be able to do, but it doesn't exist.
Doing a GET instead of a POST isn't an option, because the criteria is complex.
Well,
I experience same problem and I workaround it with plain function, which return a plain array of objects. but it will remove all Restangular helper functions. So, you cant use it.
Code snippet:
Restangular.one('client').post('list',JSON.stringify({
offset: offset,
length: length
})).then(
function(data) {
$scope.clients = data.plain();
},
function(data) {
//error handling
}
);
You can get a POST to return a properly restangularized collection by setting a custom handler for OnElemRestangularized in a config block. This handler is called after the object has been Restangularized. isCollection is passed in to show if the obect was treated as a collection or single element. In the code below, if the object is an array, but was not treated as collection, it is restangularized again, as a collection. This adds all the restangular handlers to each element in the array.
let onElemR = (changedElem, isCollection, route, Restangular: restangular.IService) => {
if (Array.isArray(changedElem) && !isCollection ) {
return Restangular.restangularizeCollection(null, changedElem, changedElem.route);
}
return changedElem;
};
RestangularProvider.setOnElemRestangularized(onElemR);