ExtJS 6.01 How to update tpl after store loaded - asynchronous

Let's say I have a component with tpl in HTML format, which has a variable named testname, default value is null.
A listener of beforerender calls to a function in viewController, and in this function, I need to load a store and a callback function to update the variable testname in the view, based on the store record returned.
The problem I met is the order of code execution. The function in the viewController always ended first before the store.load callback get executed. Therefore the front view will never get updated. I have tried to setup the asynchronousLoad to false but it doesn't help.
Here is the code in the viewController.
onViewRendering: function(theView) {
console.log('setp 1');
var theStore = Ext.create('App.store.Test');
theStore.load({
asynchronousLoad: false,
callback: function(record) {
console.log('setp 2');
if (record) {
theView.data.testname = 'updated';
}
}
});
console.log('setp 3');
}
Console log displays in step 1, 3, 2.
And here is the code in the View:
Ext.define('App.view.TestView', {
extend: 'Ext.component',
data:{
testname:null
},
tpl:'<p>{testname}</p>',
listeners: {
beforerender: 'onViewRendering'
}
})
Here is the store code:
Ext.define('App.store.Test', {
extend: 'Ext.data.Store',
alias: 'store.test',
autoLoad: true,
remoteFilter: true,
fields: ['id', 'name'],
proxy: {
type: 'direct',
directFn: 'test.getTest'
}
})
I am new to Extjs and really need some help here, thanks in advance!

To update tpl after store load, you have to call setData method like below:
Code snippet:
onViewRendering: function(theView) {
console.log('setp 1');
var theStore = Ext.create('App.store.Test');
theStore.load({
asynchronousLoad: false,
callback: function(record) {
console.log('setp 2');
if (record) {
theView.setData({testname: 'updated'}); //setData method
}
}
});
console.log('setp 3');
}

Related

Displaying name and selecting id on select for Twitter Typeahead

I am trying to implement twitter typeahead into my project, having remote as the source. I am able to make the connection between the front end query text and the sql. The return response looks like this:
[
{
id: 1,
name: 'user one'
},
{
id: 2,
name: 'user two'
}
..
]
The typeahead displays the matching items but it includes the id along with the names in the selection, instead of just the name. Second, I want to get the id value on select but the :select always gives the name value instead of the id.
here is my code:
var source = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url:"{{ path('user_typeahead') }}"+'?string=%QUERY', // twig path
wildcard: '%QUERY',
filter: function (results) {
// Map the remote source JSON array to a JavaScript object array
return $.map(results, function (result) {
return {
value: result
};
});
}
}
});
// Initialize the Bloodhound suggestion engine
source.initialize();
$('#typeahead').typeahead(null, {
display: 'value',
source: source.ttAdapter(),
limit:5,
highlight: true,
hint: true
});
$('#typeahead').bind('typeahead:select', function(ev, suggestion) {
console.log(suggestion);
});
Try this,
Display should be set to the property of the json object. display: 'id'
Chain the custom event. Access your id from suggestion.id.
$('#typeahead').typeahead(null, {
display: 'id',
source: source.ttAdapter(),
limit:5,
highlight: true,
hint: true
}).bind('typeahead:select', function(ev, suggestion) {
console.log(suggestion);
});

Populating SELECT options from database in SimpleSchema

Using SimpleSchema in Meteor with the AutoForm + Select2 plugins , i am trying to generate the Options for a Select field from the database.
The 'occupation' collection is published, and a collection 'Occupation' is defined in Meteor.
In SimpleSchema I have this:-
occupations: {
type: [String],
optional:true,
label: 'Occupation',
autoform:{
type:"select2",
placeholder: 'Comma spaced list of occupations',
options: function () {
Meteor.subscribe('occupations');
return Occupations.find({});
}
}
},
But it does not return the collection results, and crashes the application without an error message.
It seems the best way to handle this is to supply the options list through a helper.
{{> afQuickField name='occupations' multiple=true tags=true options=listOccupations}}
Where listOccupations is a helper within the template containing the form.
Template.myForm.helpers({
listOccupations: function () {
Meteor.subscribe('occupations');
return Occupations.find({}).fetch();
}
});
And we remove the options object from the schena
occupations: {
type: [String],
optional:true,
label: 'Occupation',
autoform:{
type:"select2",
placeholder: 'Comma spaced list of occupations',
}
},
Have you tried, this approach:
autoform: {
options: {
var occupations = [];
Occupations.find().map(function(occ) {
occupations.push(
{label: occ.description, value: occ._id}
);
});
return occupations;
}
}
hope this helps..
I had the same issue. I'm defining my collections schema in /lib/collections folder, and that is running on both server and client side. Given that, the console.log that I had was printing the correct values on server side and an empty array on client side.
What I did was:
if (Meteor.isClient){
Meteor.subscribe('service-categories-list', {
onReady: function(){
const serviceCategories = ServiceCategories.find({}).map(function(item, index) {
return {
label: item.name,
value: item.slug
};
});
Schema2._schema.type.autoform.options = serviceCategories;
}
})
}
I know that using the _schema is not a good idea, but I accept suggestions :)

How do you add values to AutoForm's form object without using a form?

I've got a form created through AutoForm.
As far as data sources, I can fill in parts of the form and use:
AutoForm.getFormValues('form-id').insertDoc // returns the contents of the form
When I validate the form I can do:
var formValues = AutoForm.getFormValues('form-id').insertDoc;
var isValid = MyCollection.simpleSchema().namedContext("myContext").validate(formValues);
// if isValid returns true, then I enable the Submit button
Instead of filling in parts of the form, I want to manually add information into whatever object Autoform uses for validation and submission to a collection.
For example, there are data fields in the schema that simply don't need to appear in the form itself.
Take a shopping cart:
ShoppingCartSchema = {
totalPrice: {
type: Number,
optional: false
},
itemsSelected: {
type: [Object],
optional: false
}
};
The data for itemsSelected is obviously provided through user input on the form.
The data for totalPrice is something that should not be from a form input. It's generated in the code.
But totalPrice still needs to be validated as a required field before AutoForm submits the form to a collection.
So how do you add totalPrice onto the object that Autoform eventually validates?
You could use an autovalue if you wanted to.
ShoppingCartSchema = new SimpleSchema({
'items': {
type: [Object],
},
'items.$.name': {
type: String
},
'items.$.price': {
type: Number
},
totalPrice: {
type: Number,
autoValue: function () {
if (this.field('items').isSet) {
let total = this.field('items').value.reduce(function (sum, item) {
return sum + item.price;
}, 0);
if (this.isInsert) {
return total;
} else {
return { $set: total };
}
}
}
},
});
Autoform Hooks can help you manipulate the data before you save it into the Collection.
In your case .
AutoForm.hooks({
form-id: { // The Autoform ID
onSubmit: function (insertDoc, updateDoc, currentDoc) {
if (customHandler(insertDoc)) { // Your Logic here
this.done(); // This is Required
} else {
this.done(new Error("Submission failed"));
}
return false;
}
}
});
For More Information Please refer Autoform Readme

Session object inside global template helpers

Session.set('coursesReady', false); on startup.
UPDATE:
I made it into a simpler problem. Consider the following code.
Inside router.js
Router.route('/', function () {
Meteor.subscribe("courses", function() {
console.log("data ready")
Session.set("coursesReady", true);
});
}
and inside main template Main.js
Template.Main.rendered = function() {
if (Session.get('coursesReady')) {
console.log("inject success");
Meteor.typeahead.inject();
}
The message "inject success" is not printed after "data ready" is printed. How come reactivity does not work here?
Reactivity "didn't work" because rendered only executes once (it isn't reactive). You'd need to wrap your session checks inside of a template autorun in order for them to get reevaluated:
Template.Main.rendered = function() {
this.autorun(function() {
if (Session.get('coursesReady')) {
console.log("inject success");
Meteor.typeahead.inject();
}
});
};
Probably a better solution is to wait on the subscription if you want to ensure your data is loaded prior to rendering the template.
Router.route('/', {
// this template will be rendered until the subscriptions are ready
loadingTemplate: 'loading',
waitOn: function () {
// return one handle, a function, or an array
return Meteor.subscribe('courses');
},
action: function () {
this.render('Main');
}
});
And now your rendered can just do this:
Template.Main.rendered = function() {
Meteor.typeahead.inject();
};
Don't forget to add a loading template.
To Solve Your Problem
Template.registerHelper("course_data", function() {
console.log("course_data helper is called");
if (Session.get('coursesReady')) {
var courses = Courses.find().fetch();
var result = [ { **Changed**
name: 'course-info1',
valueKey: 'titleLong',
local: function() {
return Courses.find().fetch();
},
template: 'Course'
}];
Session.set('courseResult', result); **New line**
return Session.get('courseResult'); **New line**
,
Explanation
The answer is at the return of the helper function needs to have be associated with reactivity in order for Blaze, template renderer, to know when to rerender.
Non-reactive (Doesn't change in the DOM as values changes)
Template.Main.helpers({
course_data: UI._globalHelpers.course_data ** Not reactive
});
Essentially: UI._globalHelpers.course_data returns an array of objects which is not reactive:
return [
{
name: 'course-info1',
valueKey: 'titleLong',
local: function() {
return Courses.find().fetch();
},
template: 'Course'
},
Reactive
From Meteor Documentation:
http://docs.meteor.com/#/full/template_helpers
Template.myTemplate.helpers({
foo: function () {
return Session.get("foo"); ** Reactive
}
});
Returning Session.get function to Blaze is reactive; thus, the template will change as the values changes.

$scope issue on ngGridEventEndCellEdit event when i rollback data

I probably misunderstood something but here is my problem on plunker.
I put the relevant code here anyway:
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope) {
var cellNameEditable =
'<cell-template model=COL_FIELD input=COL_FIELD entity=row.entity></cell-template>';
var cellNameDisplay =
'<div class="ngCellText" ng-class="col.colIndex()">{{row.getProperty(col.field)}}</div>';
$scope.myData= [{"id":1,"code":"1","name":"Ain"},{"id":2,"code":"2","name":"Aisne"},{"id":3,"code":"3","name":"Allier"},{"id":4,"code":"5","name":"Hautes-Alpes"},{"id":5,"code":"4","name":"Alpes-de-Haute-Provence"},{"id":6,"code":"6","name":"Alpes-Maritimes"},{"id":7,"code":"7","name":"Ardèche"},{"id":8,"code":"8","name":"Ardennes"},{"id":9,"code":"9","name":"Ariège"},{"id":10,"code":"10","name":"Aube"}];
$scope.gridOptions = {
data: 'myData',
multiSelect: false,
enableCellSelection: true,
enableRowSelection: false,
enableCellEditOnFocus: false,
rowHeight: 100,
columnDefs: [
{field:'id', displayName:'Id', visible: false},
{field:'code', displayName:'Code', enableCellEdit:true},
{
field:'name', displayName:'Name', enableCellEdit:true,
cellTemplate: cellNameDisplay,
editableCellTemplate: cellNameEditable
}
]
};
});
app.directive('cellTemplate', function () {
var cellTemplate =
'<div><form name="myForm" class="simple-form" novalidate>' +
'<input type="text" name="myField" ng-input="localInput" ng-model="localModel" entity="entity" required/>' +
'<span ng-show="myForm.myField.$error.required"> REQUIRED</span>' +
'localModel = {{localModel}} localInput = {{localInput}} entity = {{entity}}' +
'</form></div>';
return {
template: cellTemplate,
restrict: 'E',
scope: {
localModel:'=model',
localInput:'=input',
entity:'=entity'
},
controller: function ($scope) {
$scope.$on('ngGridEventStartCellEdit', function (event) {
console.log('cellTemplate controller - ngGridEventStartCellEdit fired');
$scope.oldEntity = angular.copy(event.currentScope.entity);
$scope.oldValue = angular.copy(event.currentScope.localModel);
});
$scope.$on('ngGridEventEndCellEdit', function(event) {
console.log('ngGridEventEndCellEdit fired');
if(event.currentScope.myForm.$valid) {
if(!angular.equals($scope.oldEntity, event.currentScope.entity)) {
alert('data saved !');
}
} else {
$scope.localModel = angular.copy($scope.oldValue);
$scope.localInput = angular.copy($scope.oldValue);
$scope.entity = angular.copy($scope.oldEntity);
}
});
}
};
});
Then explanations:
I have a ng-grid and based on the official example named "Excel-like Editing
Example" but with enableCellEditOnFocus option turned to false.
The cell "name" is defined in a directive containing a form to handle
data validation before updating the model.
I want to implement this behavior: When a user put invalid data, the
directive display error message and when the user leave the field, the
directive rollback data. If everything ok then I let the data updated.
The rollback part does not work. On the given plunker line 67 to 72 (last block on the code given here) it
fails to retore data. But my binding is with "=" so it should. Or maybe
because I am on the ngGridEventEndCellEdit event it breaks the links ?
I really don't understand why it fail.
So to reproduce my issue: enter in modification on a name cell, delete
all the data, REQUIRED is shown, then go out from the cell -> model is
not rolled back.
If you use a custom template, you should emit ngGridEventEndCellEdit event.

Resources