The Problem
I just cannot figure out the view model in NativeScript
I am having a hard time understanding how view-models work in NativeScript. I understand the high level concept - that the MVVM pattern allows us to create observable objects - and our UI is updated when values change.
Here is a simple example:
main-page.js
var createViewModel = require("./main-view-model").createViewModel;
function onNavigatingTo(args) {
var page = args.object;
page.bindingContext = createViewModel();
}
exports.onNavigatingTo = onNavigatingTo;
main-view-model.js
var Observable = require("tns-core-modules/data/observable").Observable;
function getMessage(counter) {
if (counter <= 0) {
return "Hoorraaay! You unlocked the NativeScript clicker achievement!";
} else {
return counter + " taps left";
}
}
function createViewModel() {
var viewModel = new Observable();
viewModel.counter = 42;
viewModel.message = getMessage(viewModel.counter);
viewModel.onTap = function() {
this.counter--;
this.set("message", getMessage(this.counter));
}
return viewModel;
}
exports.createViewModel = createViewModel;
I understand , some what, what is happening. But not everything.
Questions I Have ...
How would you add a new function , for instance, an email validation function? Would it go into the View Model page, or just plain Javscript page?
Let's say I added a new textfield to the UI. I have a tap function. Where does my function go?
So in this case, everything related to the UI should go in the createViewModel function? Is that correct?
I have also seen in sample apps, where the developer doesn't use view models at all - it appears he just creates it as an observable object.
Thank you for looking. I know I am close to understanding, but that bindingContext and the viewmodel has me a bit confused. [ I have read everything in NS docs ]
John
The answer is either of it should work. You may put the validation or tap function in view model or in the code behind file, it's upto you to decide which works best for you.
If you put it in the view model, you will use event binding (tap="{{ functionName }}" Or if you put it in code behind file, you will just export the function name and simply refer the function name on XML (tap="functionName").
By giving this flexibility you are allowed to separate your code, keep the files light weighted.
Related
I am using Vue.Draggable in my Vue project and trying to drag(clone) a button from sider into a draggable component.
I want to modify the data when I clone the button into the component. But I find that when I modify the data in the list which is binded with the component, the original list that sider used got changed automatically.
Is there some kind of synchronization mechanism in Vue.Draggable or something? I want to change the object data in the component only.
I tried to modify the object in the list2 manually by using a vue extension in Chrome browser. And it still happens. So I think maybe it's not bug in my code.
addEntity (conditionID, entity) {
if (!entity.forChoose) {
}
else {
let variable = 0;
for (let i = 0, len = this.whens.length; i < len; i++) {
if (this.whens[i].entity[0].id == entity.id) {
variable++;
}
}
this.whens[conditionID].entity[0].forChoose = false;
this.whens[conditionID].entity[0].variable = variable;
this.whens[conditionID].entity[0].name = entity.name + '-fake';
}
},
The code above is the event when I drag the data into the component, and changed some variable.
Although I did nothing to the original data in the Sider where I cloned the data from, it still got changed as well.
Is there a way to change the dragged data but do not affect the original data?
Please help me, thank you!
I think this is happening because of the immutability.
so can you try using spread operator to create new shallow copy of your list before you change it.
how to use spread operator (triple dot)
let newArrayOfWhens = [...this.whens]
then use the "newArrayOfWhens" array in your code
You can create a deep clone as well if you want, but try to avoid it if it is not necessary.
you can use a library call "lodash" to do it very easily
deepClone
I have a small problem.
I have index.js
var loc = require('location');
function doClick (){
loc.doIt();
}
in location.js I have these
var dee = 12;
exports.doIt = function() {
alert(dee);
};
Which means that when I click on the button I can get the alert, however, I want to reach these information without a need of click - onLoad - besides I want to return two values not only one.
How I can fix this maybe it has really an easy solution but because I have been working for a while my mind stopped working :)
regards
you should move your location.js to inside app/lib (as module). for example :
// app/lib/helper.js
exports.callAlert = function(text) {
alert('hello'+ text);
}
and then call it in your controller like this :
var helper = require("helper"); // call helper without path and .js extension
helper.callAlert('Titanium');
and your problem should be solved :)
This was originally posted on discuss.emberjs.com. See:
http://discuss.emberjs.com/t/what-is-the-proper-use-of-store-filter-store-find-for-infinite-scrolling/3798/2
but that site seems to get worse and worse as far as quality of content these days so I'm hoping StackOverflow can rescue me.
Intent: Build a page in ember with ember-data implementing infinite scrolling.
Background Knowledge: Based on the emberjs.com api docs on ember-data, specifically the store.filter and store.find methods ( see: http://emberjs.com/api/data/classes/DS.Store.html#method_filter ) I should be able to set the model hook of a route to the promise of a store filter operation. The response of the promise should be a filtered record array which is a an array of items from the store filtered by a filter function which is suppose to be constantly updated whenever new items are pushed into the store. By combining this with the store.find method which will push items into the store, the filteredRecordArray should automatically update with the new items thus updating the model and resulting in new items showing on the page.
For instance, assume we have a Questions Route, Controller and a model of type Question.
App.QuestionsRoute = Ember.Route.extend({
model: function (urlParams) {
return this.get('store').filter('question', function (q) {
return true;
});
}
});
Then we have a controller with some method that will call store.find, this could be triggered by some event/action whether it be detecting scroll events or the user explicitly clicking to load more, regardless this method would be called to load more questions.
Example:
App.QuestionsController = Ember.ArrayController.extend({
...
loadMore: function (offset) {
return this.get('store').find('question', { skip: currentOffset});
}
...
});
And the template to render the items:
...
{{#each question in controller}}
{{question.title}}
{{/each}}
...
Notice, that with this method we do NOT have to add a function to the store.find promise which explicitly calls this.get('model').pushObjects(questions); In fact, trying to do that once you have already returned a filter record array to the model does not work. Either we manage the content of the model manually, or we let ember-data do the work and I would very much like to let Ember-data do the work.
This is is a very clean API; however, it does not seem to work they way I've written it. Based on the documentation I cannot see anything wrong.
Using the Ember-Inspector tool from chrome I can see that the new questions from the second find call are loaded into the store under the 'question' type but the page does not refresh until I change routes and come back. It seems like the is simply a problem with observers, which made me think that this would be a bug in Ember-Data, but I didn't want to jump to conclusions like that until I asked to see if I'm using Ember-Data as intended.
If someone doesn't know exactly what is wrong but knows how to use store.push/pushMany to recreate this scenario in a jsbin that would also help too. I'm just not familiar with how to use the lower level methods on the store.
Help is much appreciated.
I just made this pattern work for myself, but in the "traditional" way, i.e. without using store.filter().
I managed the "loadMore" part in the router itself :
actions: {
loadMore: function () {
var model = this.controller.get('model'), route = this;
if (!this.get('loading')) {
this.set('loading', true);
this.store.find('question', {offset: model.get('length')}).then(function (records) {
model.addObjects(records);
route.set('loading', false);
});
}
}
}
Since you already tried the traditional way (from what I see in your post on discuss), it seems that the key part is to use addObjects() instead of pushObjects() as you did.
For the records, here is the relevant part of my view to trigger the loadMore action:
didInsertElement: function() {
var controller = this.get('controller');
$(window).on('scroll', function() {
if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
controller.send('loadMore');
}
});
},
willDestroyElement: function() {
$(window).off('scroll');
}
I am now looking to move the loading property to the controller so that I get a nice loader for the user.
I'm learning with Titanium to make iPhone/Android apps. I'm using Alloy MVC framework. I never used javascript before, apart from simple scripts in HTML to access the DOM or something like that, so I never needed to structure the code before.
Now, with Titanium, I must use a lot of JS code and I was looking for ways to structure my code. Basically I found 3 ways to do it: prototype, namespace and functions inside functions.
Simple example for each:
Prototype:
NavigationController = function() {
this.windowStack = [];
};
NavigationController.prototype.open = function(windowToOpen) {
//add the window to the stack of windows managed by the controller
this.windowStack.push(windowToOpen);
//grab a copy of the current nav controller for use in the callback
var that = this;
windowToOpen.addEventListener('close', function() {
if (that.windowStack.length > 1)
{
that.windowStack.pop();
}
});
if(Ti.Platform.osname === 'android') {
windowToOpen.open();
} else {
this.navGroup.open(windowToOpen);
}
};
NavigationController.prototype.back = function(w) {
//store a copy of all the current windows on the stack
if(Ti.Platform.osname === 'android') {
w.close();
} else {
this.navGroup.close(w);
}
};
module.exports = NavigationController;
Using it as:
var NavigationController = require('navigator');
var navController = new NavigationController();
Namespace (or I think is something like that, coz the use of me = {}):
exports.createNavigatorGroup = function() {
var me = {};
if (OS_IOS) {
var navGroup = Titanium.UI.iPhone.createNavigationGroup();
var winNav = Titanium.UI.createWindow();
winNav.add(navGroup);
me.open = function(win) {
if (!navGroup.window) {
// First time call, add the window to the navigator and open the navigator window
navGroup.window = win;
winNav.open();
} else {
// All other calls, open the window through the navigator
navGroup.open(win);
}
};
me.setRightButton = function(win, button) {
win.setRightNavButton(button);
};
me.close = function(win) {
if (navGroup.window) {
// Close the window on this nav
navGroup.close(win);
}
};
};
return me;
};
Using it as:
var ui = require('navigation');
var nav = ui.createNavigatorGroup();
Functions inside functions:
function foobar(){
this.foo = function(){
console.log('Hello foo');
}
this.bar = function(){
console.log('Hello bar');
}
}
// expose foobar to other modules
exports.foobar = foobar;
Using it as:
var foobar = require('foobar').foobar
var test = new foobar();
test.bar(); // 'Hello bar'
And now my question is: which is the better to maintain code clean and clear? It seems that prototype is clear an easy to read/mantain. Namespace confuses me a bit but only needs to execute the initial function to be "available" (no use of new while declaring it, I suppose because it returns the object?namespace? "me"). Finally, functions inside functions is similar to the last, so I don't know exactly the difference, but is useful to export only the main function and have all the inside functions available for use it later.
Maybe the last two possibilities are the same, and I'm messing concepts.
Remember that I'm searching for a good way to structure the code and have functions available to other modules and also inside the own module.
I appreciate any clarification.
In the examples that they release, Appcelerator appears to follow the non-prototype approach. You can see it in the examples they have released: https://github.com/appcelerator/Field-Service-App.
I've seen a lot of different approaches to structuring applications in Titanium before Alloy. Since Alloy, I've found following the development team's examples helpful to me.
With that being said, it seems to me that all of this is still under interpretation and open to change and community development. Before Alloy there were some great community suggestions on structuring an app and I believe that it is still open with Alloy. Often when I find someone's example code I see something they did with it that appears to organize it a bit better than I thought of. It seems to make it a bit easier.
I think you should structure your application in a way that makes sense to you. You may stumble on to a better and easier way of developing applications with Alloy, because you are looking at it critically.
I haven't found a lot of extensive Alloy examples, but Field-Service-App makes sense to me. They have a nice separation of the elements in the application beyond MVC. Check it out.
I am rather new to sencha touch, I've done a lot of research and tutorials to learn the basics but now that I am experimenting I have run into a problem that I can't figure out.
I have a basic DataList which gets its data from a store which displays in a xtemplate.
Within this template I have created a member function which requires store field data to be parsed as a parameter.
I would like to make a thumbnail image (that's source is pulled from the store) execute the member function on click/tap.
I can't find any information on this within the docs, does anyone know the best way to go about this?
Here is a code example (pulled from docs as I can't access my actual code right now).
var tpl = new Ext.XTemplate(
'<p>Name: {name}</p>'
{
tapFunction: function(name){
alert(name);
}
}
);
tpl.overwrite(panel.body, data);
I want to make the paragraph clickable which will then execute the tapFunction() member function and pass the {name} variable.
Doing something like onclick="{[this.tapFunction(values.name)]} " does not seem to work.
I think functions in template are executed as soon as the view is rendered so I don't think this is the proper solution.
What I would do in your case is :
Add a unique class to your < p > tag
tpl : '<p class="my-p-tag">{name}</p>'
Detect the itemtap event on the list
In your dataview controller, you add an tap event listener on your list.
refs: {
myList: 'WHATEVER_REFERENCE_MATCHES_YOUR_LIST'
},
control: {
myList: {
itemtap: 'listItemTap'
}
}
Check if the target of the tap is the < p > tag
To do so, implement your listItemTap function like so :
listItemTap: function(list,index,target,record,e){
var node = e.target;
if (node.className && node.className.indexOf('my-p-tag') > -1) {
console.log(record.get('name'));
}
}
Hope this helps