I sometimes find myself declaring the same data to multiple templates. For example:
Template.auction_page.auctionDurations = function () {
return [ 30, 60, 120 ];
};
Template.auction_editor.auctionDurations = function () {
return [ 30, 60, 120 ];
};
I can make it better by using a global:
Template.auction_page.auctionDurations = function () {
return global.auctionDurations;
};
Template.auction_editor.auctionDurations = function () {
return global.auctionDurations;
};
But is there any way to get rid of the declarations altogether? In other words, is there any way to share some global data to multiple templates by default?
Found a good solution (with the help of a Helper!).
Your global:
global = _.extend({}, {
regions: [ "Americas", "Europe", "Asia" ]
}
The helper:
Handlebars.registerHelper("global", function(name) {
return global[name];
});
Now all your templates can make use of it:
<select>
{{#each global "regions"}}
<option>{{this}}</option>
{{/each}}
</select>
The use of a helper function is a pretty good general purpose solution. For completeness, you can also do a simple assignment:
Template.auction_page.auctionDurations = Template.auction_editor.auctionDurations;
You can use Session for this:
Template.auction_page.auctionDurations = function() {
return Session.get("auctionDurations");
}
Template.auction_editor.auctionDurations = function() {
return Session.get("auctionDurations");
}
A nice bonus of using Session is that, since it's a reactive data source, setting it will cause all Templates that depend on it to be re-rendered. So your auction durations will update as soon as you call Session.set("auctionDurations", [ 30, 60, 120 ]);
Related
Aim:
I'd like to have two models(sets of data) passed to the custom control with a predefined search field, in which later on I can execute filtering.
I'm a newbie in OpenUi5, so I might be doing something wrong and stupid here. I've started with a simplified task of passing data from the frontend to my custom control and experiencing troubles.
Background of the simplified idea:
Create a custom control with an aggregation foo , the value to it will be provided from the view.
Also create another aggregation element _searchField which will be populated with the data provided from the view.
Fire the onSuggestTerm everytime user types in a _searchField.
Custom control code:
function (Control) {
var DropDownListInput = Control.extend('xx.control.DropDownListInput', {
metadata: {
defaultAggregation: 'foo',
aggregations: {
foo: { type: 'sap.m.SuggestionItem', multiple: true, singularName: 'suggestionItem' },
_searchField: { type: 'sap.m.SearchField', multiple: false, visibility: 'hidden' }
}
}
});
DropDownListInput.prototype.init = function () {
var that = this;
this.onSuggestTerm = function (event) {
var oSource = event.getSource();
var oBinding = that.getAggregation('foo');
oBinding.filter(new sap.ui.model.Filter({
filters: new sap.ui.model.Filter('DISEASE_TERM', sap.ui.model.FilterOperator.Contains, ' Other')
}));
oBinding.attachEventOnce('dataReceived', function () {
oSource.suggest();
});
};
this.setAggregation('_searchField', new sap.m.SearchField({
id: 'UNIQUEID1',
enableSuggestions: true,
suggestionItems: that.getAggregation('foo'),
suggest: that.onSuggestTerm
}));
};
return DropDownListInput;
}, /* bExport= */true);
I'm not providing Renderer function for control here, but it exists and this is the most important excerpt from it:
oRM.write('<div');
oRM.writeControlData(oControl);
oRM.write('>');
oRM.renderControl(oControl.getAggregation('_searchField'));
oRM.write('</div>');
Passing the data to this control from the xml frontend:
<xx:DropDownListInput
id="diseaseTermUNIQUE"
foo='{path: db2>/RC_DISEASE_TERM/}'>
<foo>
<SuggestionItem text="{db2>DISEASE_TERM}"
key="{db2>DISEASE_TERM}" />
</foo>
</xx:DropDownListInput>
The code fails to run with this error Cannot route to target: [object Object] -
and I have no idea what's wrong here..
The problem is that you forgot to provide single quotes in your path:
foo="{path: 'db2>/RC_DISEASE_TERM/'}"
This works, but I need to use mounted(){} to initiate the function which I think can be avoided but not sure how.
<script>
export default {
data () {
return {
domains: [],
}
},
methods: {
fetchDomains() {
let _this = this;
api._get({url: 'api/domains'})
.then(function (response) {
_this.domains = response.data;
})
}
},
mounted() {
this.fetchDomains()
}
}
</script>
This code doesn't work, but I like to do something like this. Initiating the function in data(){} itself.
<script>
export default {
data () {
return {
domains: this.fetchDomains(),
}
},
methods: {
fetchDomains() {
let data = [];
api._get({url: 'api/domains'})
.then(function (response) {
data = response.data;
})
return data
}
}
}
</script>
Thanks in advance.
Your first code snippet is the correct way to do it.
You can't initialize domains with the data from the API response because it is an async operation which may or may not be resolved successfully at some point in the future, well after the component is mounted. You might also want to do other things like keeping track of the async operation with a loading property which you set to true for the duration of the request.
Your component will initially be in a loading state which does not yet have any domains data, and you need to account for this. Show a loading spinner or something during this time.
I agree with Decade Moon that your first approach is the better way to do it (though you could use created instead of mounted).
The reason your second approach doesn't work is that you return an array and then replace the local variable's value with a different array. What you need to do is populate the array you returned.
new Vue({
el: '#app',
data() {
return {item: this.getItem()}
},
methods: {
getItem() {
let val = [];
setTimeout(() => {
const result = ['first','second','third'];
val.push(...result);
}, 800);
return val;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">{{item}}</div>
I might be deviating slightly from the question (since it explicitly mentions the data property), but I think this might be helpful. Personally, if I want to provide some data with more complex logic I use the computed property. This is great in my opinion and you can read more about it in the docs. The problem in this case is that it doesn't work entirely as expected with asynchronous operations...
However, there is a lovely little module called vue-async-computed which can be found here. It solves this specific problem by providing an asyncComputed property and keeps the code really clean!
Say I had the following JSON file:
{
"farmer": [
{
"crops": "corn"
}
],
"activities":{
"hobbies": "swimming"
},
"name: Todd"
}
I would like to know how to make calls to them using React. My best attempt is as shown below.
componentDidMount: function(){
var selfish = this;
$.get('~~someurl~~', function(data){
selfish.setState(data);
});
},
render: function(){
return (<div>
<p>{this.state.name}</p>
<p>{this.state.activities.hobbies}</p>
<p>{this.state.farmer[0].crops}</p>
</div>)
}
I get that the first one {this.state.name} will run as it has been written. However, I am uncertain as to how to express the final two {this.state.activities.hobbies} and {this.state.farmer[0].crops} using the React syntax.
I have written them out in hopefully a way that expresses what I am trying to achieve with each call.
EDIT: The specific error that results from the latter two state that they are undefined.
EDIT: So I got the second one working by adding an empty initial state.
getInitialState: function(){
return {activities: '', farmer: ''}
}
However, this leaves the last one, which still returns an error.
The problem is that you are using componentDidMount when you should use componentWillMount. Check out the documentation on these lifecycle methods.
This is what the documentation says about componentDidMount
Invoked once, only on the client (not on the server), immediately
after the initial rendering occurs.
Which means when you first render your states are not declared unless you have used getInitialState before (another lifecycle method).
I simply did this:
componentWillMount: function () {
this.setState({
"farmer": [
{
"crops": "corn"
}
],
"activities":{
"hobbies": "swimming"
},
"name": "Todd"
});
},
and I was able to use this.state.farmer[0].crops in my render method
EDIT:
To be clear. If you need to retrieve the data after you rendered the component then you need to specify default values for the states that you use in render. This can be achieved using getInitialState:
getInitialState: function () {
return {
"farmer": [
{
"crops": DEFAULT_VALUE
}
],
"activities":{
"hobbies": DEFAULT_VALUE
},
"name": DEFAULT_VALUE
});
},
I am new to Angular, but the first thing I wanted to use was namespaces (as use them elsewhere)
I have read a number of posts regarding namespaces in Angular, and wanted to get an opinion on an approach I was going to perhaps take...
I was thinking of structuring very similar to what is suggested elswhere (ie have a main app.js, and then divide by features or "views".
For example, if I wanted to use a namespace such as TestModule, I would have each Feature/View and it's associated Controller in a separate js file...
eg such a file will look something like...
var TestModule = TestModule || {};
TestModule.View1 = function () {
// Private helpers
function cellTemplate(cellElement, cellInfo) {
var div = document.createElement('div');
......
};
function controller($scope) {
$scope.testText = "umm, hello";
$scope.gridData = [
{ col1: 'data 1', col2: 'data 2' },
{ col1: 'data 3', col2: 'data 4' },
];
$scope.cellTemplate = cellTemplate;
}
return {
// Controller
Controller: controller
}
}
And then in the main app.js. have something like..
"use strict";
var TestModule = TestModule || {};
var ngGridApp = angular.module("ngTestApp", ['dx']);
ngGridApp.controller("gridController1", TestModule.View1().Controller);
ngGridApp.controller("listController1", TestModule.View2().Controller);
etc
These are just my initial ideas, perhaps as I learn more I may find this approach won't be a good one, but it seems ok to me? I would be interested in any feedback on this 2 cents worth :-)
Cheers
My application has been growing for the last year or so and I have finally started to split out common components by extending existing ones. I've found this to be really helpful - increasing speed of development and maintenance. My problem is that I haven't grasped the concept of using custom parameters on my components and was hoping that someone could offer some assistance in the following example. Specifically I don't seem to be able to access the custom parameter (myCustomParam) inside the proxy declared in the initComponent function:
MyEmployeeGrid = Ext.extend(Ext.grid.GridPanel, {
myCustomParam: "CompanyX",
initComponent:function() {
//Store Reader
var myReader = new Ext.data.JsonReader({
root : 'objectsToConvertToRecords',
idProperty: 'id',
fields : [
{name: 'id'},
{name: 'employee', allowBlank:false},
{name: 'department', allowBlank:false}
]
});
//Store Proxy
var dwrProxy = new Ext.ux.data.DwrProxy({
apiActionToHandlerMap : {
read : {
dwrFunction : EmployeeService.readCompanyEmployees,
getDwrArgsFunction: function(request, recordDataArray, oldRecordDataArray) {
return [myCustomParam];
}
}
}
});
//Setup Params for the grid
Ext.apply(this, {
store: new Ext.data.Store({
proxy: dwrProxy,
reader: myReader,
autoLoad : true,
autoSave: true
}),
columns: [{header: "Employee", dataIndex: 'employee'},
{header: "Department", dataIndex: 'department'}]
});
MyEmployeeGrid.superclass.initComponent.apply(this, arguments);
} // eo function initComponent
,onRender:function() {
MyEmployeeGrid.superclass.onRender.apply(this, arguments);
} // eo function onRender
});
Ext.reg('myemployeegrid', MyEmployeeGrid);
myCustomParam is a property of the object in question. It is never declared as a variable in its own right. You can see this by running the code through jslint.
You want this.myCustomParam.
If you're trying to pass in parameters, however, you might want to try this way instead:
MyGrid = Ext.extend(Ext.grid.GridPanel, {
constructor: function(cfg) {
cfg = cfg || {};
// ... do stuff ...
MyGrid.superclass.constructor.call(this, cfg);
}
});
I have a strong feeling this is to do with scope around the point at which getDwrArgsFunction is called. It possibly doesn't know what myCustomParam is (despite the fact this should possibly be this.myCustomParam, and that you'd want to set this after you'd applied any config) because it's not declared in the DwrProxy object and has no reference back to its owning MyEmployeeGrid object.
I'd debug/attempt to fix in the following way:
Rename myCustomParam to
this.myCustomParam and test
Set scope and test
Create getDwrArgsFunction using
createDelegate
(http://dev.sencha.com/deploy/dev/docs/source/Function.html#method-Ext.util.Functions-createDelegate)
and test
DISCLAIMER: Some of those will be no good depending on your DwrProxy etc (which we can't see the code for)
The proxy cannot access the variable because it is not defined in the scope of the proxy itself. I have been struggling with things like this in the past, I thought that components would act like closures and keep track of the variable, but they dont.
You can solve your problem by handing the scope of the grid to the handler function explicitly by making it a delegate, what you do have to change for that is making the variable you would like to access an instance variable instead of just a local variable.
Changes:
MyEmployeeGrid = Ext.extend(Ext.grid.GridPanel, {
myCustomParam: "defaultValue",
initComponent:function() {
...
//Store Proxy
var dwrProxy = new Ext.ux.data.DwrProxy({
apiActionToHandlerMap : {
read : {
dwrFunction : EmployeeService.readCompanyEmployees,
getDwrArgsFunction: function(request, recordDataArray, oldRecordDataArray) {
return [this.myCustomParam];
}.createDelegate(this)
}
}
});
var myGridInstance = new MyEmployeeGrid({ myCustomParam: 'ValueForThisInstance' });
Hope this helps you, good luck