AngularJS $watch, wait variable declaration to trigger - javascript

I have the below code :
variable declaration :
$scope.updateUploadView =
{
ready:{
inview:false,
text: "starting"
},
uploading:{
inview:false,
text: "uploading"
},
done:{
inview:false,
text: "done !"
}
};
$watch :
angular.forEach($scope.updateUploadView, function(element, key) {
$scope.$watch(function () {
return element;
}, function() {
// do stuff
});
},true);
});
The problem here is that i want to ignore $watch to trigger on the undefined property because it cause the view to change 3 times on the start of the application. Is there any possibility to wait the variable declaration ?
http://jsfiddle.net/A8Vgk/2200/

Just check for undefined in your watch function.
$scope.$watch(function () {
return element;
}, function(newValue, oldValue) {
if(!angular.isDefined(newValue) && angular.isDefined(oldValue)) {
// do stuff
}
});

Related

Vue 2 component prop getting wrong value

I am trying to build a menu between categories. If a category has a sub-category it returns a value that says has_subCategory as boolean 0/1.
<template>
<select><slot></slot></select>
</template>
<script>
export default {
props: ['value',
'hasSubCat'],
watch: {
value: function(value, hasSubCat) {
this.relaod(value);
this.fetchSubCategories(value, hasSubCat);
}
},
methods: {
relaod: function(value) {
var select = $(this.$el);
select.val(value || this.value);
select.material_select('destroy');
select.material_select();
},
fetchSubCategories: function(value, hasSubCat) {
var mdl = this;
var catID = value || this.value;
var has_subCat = hasSubCat || this.hasSubCat;
console.log("has_subCat:" + has_subCat);
mdl.$emit("reset-subcats");
if (catID) {
if (has_subCat == 0) {
if ($('.subdropdown').is(":visible") == true) {
$('.subdropdown').fadeOut();
}
} else {
axios.get(URL.API + '/subcategories/' + catID)
.then(function(response) {
response = response.data.subcatData;
response.unshift({
subcat_id: '0',
subcategory_name: 'All Subcategories'
});
mdl.$emit("update-subcats", response);
$('.subdropdown').fadeIn();
})
.catch(function(error) {
if (error.response.data) {
swal({
title: "Something went wrong",
text: "Please try again",
type: "error",
html: false
});
}
});
}
} else {
if ($('.subdropdown').is(":visible") == true) {
$('.subdropdown').fadeOut();
}
}
}
},
mounted: function() {
var vm = this;
var select = $(this.$el);
select
.val(this.value)
.on('change', function() {
vm.$emit('input', this.value);
});
select.material_select();
},
updated: function() {
this.relaod();
},
destroyed: function() {
$(this.$el).material_select('destroy');
}
}
</script>
<material-selectcat v-model="catId" name="category" #reset-subcats="resetSubCats" #update-subcats="updateSubCats" id="selcat">
<option v-for="cat in cats" :value="cat.cat_id" :hasSubCat="cat.has_subCat" v-text="cat.category_name"></option>
</material-selectcat>
The data looks like this:
cat_id:"0"
category_name:"All Subcategories"
has_subCat:0
What I dont understand is that console.log("has_subCat:" + hasSubCat); prints out different values each time I change the select. It should only display 0 or 1
Watcher in vue.js is supposed to be used in order to watch one value, but you can fulfill your requirement with help of computed.
export default {
props: ['value',
'hasSubCat'],
watch: {
/* without this, watcher won't be evaluated */
watcher: function() {}
},
computed: {
watcher: function() {
this.reload(this.value);
this.fetchSubCategories(this.value, this.hasSubCat);
}
},
...
}
I also made a simplified working fiddle, you can have a look.
You are assuming that the second parameter of your watch function is hasSubCat which is not the case. While the first parameter of the value watch function represents the new value of the property, the second parameter is actually the old value of the watched property. Try this out to understand more.
watch: {
value: function(value, oldValue) {
console.log('new value:', value)
console.log('old value:', oldValue)
}
}
So to watch both of value and hasSubCat, you can do something like this:
watch: {
value: function(newValue) {
this.reload(newValue)
this.fetchSubCategories(newValue, this.hasSubCat)
},
hasSubCat: function(newHasSubCat) {
this.reload(this.value)
this.fetchSubCategories(this.value, newHasSubCat)
}
}

How to use segment.io's analytics.js in a knockout custom bindings

I am using knockout to make a custom binding for analytics.track, but it seems to be having trouble. It seems if the analytics.track is nested in more than 2 functions the track call fails silently. It doesn't hit the callback and it doesn't report in segments debugger. I have provided 2 examples demonstrating the problem here:
Without Closure (works):
function sendTrack(event, props) {
console.log("Enter sendTrack");
analytics.track('Signed Up', {
plan: 'Enterprise'
}, {}, function () {
console.log('track callback logged');
});
}
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor) {
console.log("Init");
var value = ko.unwrap(valueAccessor());
ko.applyBindingsToNode(element, { click: sendTrack });
}
};
ko.applyBindings({});
With Closure (doesn't work):
(function(ko, $, analytics){
'use strict';
function sendTrack(event, props) {
console.log("Enter sendTrack");
analytics.track('Signed Up', {
plan: 'Enterprise'
}, {}, function () {
console.log('track callback logged');
});
}
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor) {
console.log("Init");
var value = ko.unwrap(valueAccessor());
ko.applyBindingsToNode(element, { click: sendTrack });
}
};
ko.applyBindings({});
})(window.ko, window.jQuery, window.analytics);
Edit1: Also note this works with if I move the analytics.track to init:
(function(ko, $, analytics){
'use strict';
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor) {
console.log("Init");
analytics.track('Signed Up', {
plan: 'Enterprise'
}, {}, function () {
console.log('track callback logged');
});
}
};
ko.applyBindings({});
})(window.ko, window.jQuery, window.analytics);
Please advise
This is very likely because of the order things load / are initialized on the window object. Because the iife executes immediately, the analytics variable will be set to whatever window.analytics is the moment the iife is encountered by the browser. In the first case, window.analytics will be resolved when the code is run.
Put differently: the closure captures window.analytics in a scoped analytics variable at the time the iife executes.
Here's a demo showing the problem.
Without closure:
function sendTrack() {
console.log("Tracking...");
analytics.track("stuff");
}
ko.bindingHandlers.segmentTrack = {
init: function(element) {
console.log("Init");
ko.applyBindingsToNode(element, { click: sendTrack });
}
}
ko.applyBindings({ });
// Simulate loading analytics now:
window.analytics = { track: function(txt) { console.log("Tracking " + txt); } };
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div data-bind="segmentTrack: true">CLICK ME</div>
vs with closure:
(function(ko, analytics) {
function sendTrack() {
console.log("Tracking...");
analytics.track("stuff");
}
ko.bindingHandlers.segmentTrack = {
init: function(element) {
console.log("Init");
ko.applyBindingsToNode(element, { click: sendTrack });
}
}
ko.applyBindings({});
})(window.ko, window.analytics); // window.analytics isn't quite okay yet!
// Simulate loading analytics now:
window.analytics = { track: function(txt) { console.log("Tracking " + txt); } };
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<div data-bind="segmentTrack: true">CLICK ME</div>
True, in my examples the second scenario throws an error whereas you mention in the question nothing happens, but then again the question doesn't contain an actual repro so it's hard to tell where that difference lies.
So analytics.js asynchronously loads its self in the page. In the mean time it queues all calls to the API with a snub version of the object. Once analytics.js loads it executes all the calls in the queue. Then redefines its self, breaking all refs to the original window.analytics. So any calls that are encountered fast enough to My only work around for this is to make my exposer a function call that returns the current version of the window.analytics.
(function (ko, $, analytics) {
function sendTrack(event, props) {
analytics().track(event, props);
}
ko.bindingHandlers.segmentTrack = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var value = ko.unwrap(valueAccessor());
ko.applyBindingsToNode(element, { click: function () { sendTrack(value.event, value.options) }});
}
}
})(window.ko, window.jQuery, function () { return window.analytics; });

ExtJS 5: Initial ViewModel data not firing formula

I have a ViewModel that contains some initial data... this initial data is based off of a global variable that I have created. In the ViewModel, I have a formula that does some logic based on the data set from the global variable. The interesting thing is, this formula does not fire when the ViewModel is created. I'm assuming this is because the Something.Test property does not exist, so the ViewModel internals have some smarts to not fire the method if that property does not exist.
If the property doesn't exist, how do I fire the formula anyway? I know I could look for Something check to see if it has the property Test, but I'm curious why this example wouldn't work. Here's the example:
Ext.application({
name : 'Fiddle',
launch : function() {
// Define global var Something
Ext.define('Something', {
singleton: true
});
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myView',
data: {
Something: window.Something
},
formulas: {
testSomething: function(getter) {
console.log('here', getter('Something.Test'));
return getter('Something.Test');
},
myTitle: function(getter) {
return 'My Title';
}
}
});
Ext.define('MyView', {
extend: 'Ext.panel.Panel',
bind: {
title: '{myTitle}'
},
viewModel: {
type: 'myView'
}
});
var view = Ext.create('MyView', {
renderTo: Ext.getBody()
});
// This will fire the ViewModel formula
//view.getViewModel().set('Something', window.Something);
console.log(Something, window.Something)
}
});
You can workout some logic to handle when Something.Test is not available, something like:
data: {
Something: window.Something && window.Something.Test || {Test: null}
},
formulas: {
testSomething: function(get) {
var val = get('Something.Test');
console.log('Test');
return val;
},
myTitle: function(getter) {
return 'My Title';
}
}

Sencha store's each function the console.log not work

I want to display the records, but when I test it to display the data on console use record.get(''), it not work . even I tap the static code console.log('some thing'). It also cant display on my console.
The code in my controller:
it near the //-------here it is
Ext.define('ylp2p.controller.addtab',{
extend: 'Ext.app.Controller',
config: {
refs: {
myTabPanel: '.makemoney #tabfirst',
},
control: {
myTabPanel: {
activate: 'onActivateTabPanel',
activeitemchange: 'onActiveItemChangeTabPanel'
}
}
},
launch: function(){
var products = Ext.getStore('productstore');
products.filterBy(function(record, id){
return record.get('loanname') === 'xixi22';
});
},
onActivateTabPanel: function(newActiveItem, viewport, oldActiveItem, eOpts) {
//test
console.log('hello the activatetabpanel is fire!');
//end test success
var tabs = Ext.getStore('loanliststore');
tabs.each(function(record) {
console.log('hello come on');//---------------------here it is
newActiveItem.add({
title: record.get('loanname')
});
console.log('');
});
},
onActiveItemChangeTabPanel: function(cmp, value, oldValue, eOpts) {
console.log('hello this is the activechangepanel is fire!');
var products = value.getStore();
products.clearFilter(true);
products.filterBy(function(record, id) {
return record.get('loanname') === value.getTitle();
});
}
});
Check by tabs.getCount() if it is greater then 0 then it should work. If not means there is no data populated in your store.

How to call a function outside $ionicActionSheet on buttonClicked?

I am trying to use $ionicActionSheet to execute a function. Moreover, in the example below, I want to call doSomethingElse() when the user clicks on first option to "do something else". I can execute commands within the function buttonClicked() succesfully, but it does not want to call the functions outside.
// within controller
$scope.doSomethingElse = function(textStr) {
window.alert(textStr)
}
$scope.showActions = function(friendName) {
$ionicActionSheet.show({
buttons: [
{ text: 'do something else },
{ text: 'do something' },
],
destructiveText: 'View all',
titleText: '<h4>' + friendName + ' </h4>',
cancelText: 'Cancel',
buttonClicked: function(index, $scope) {
if(index==0) {
// window.alert("Hello"); works fine
$scope.doSomethingElse("Index 0")
}
if(index==1) {
// window.alert("Hello"); works fine too
$scope.doSomethingElse("Index 1")
}
return true;
},
destructiveButtonClicked: function() {
window.alert("Hey All");
return true;
}
});
}
Replace the following:
buttonClicked: function(index, $scope) {
With:
buttonClicked: function(index) {
Or rename the second argument to for example:
buttonClicked: function(index, button) {
Otherwise it will overwrite the $scope variable. The second argument that gets passed to the buttonClicked function is not the $scope, it's the button object.

Categories

Resources