I am trying to create within knockout mapping some computed variables. The problem is when adding new elements to the children's children after the mapping, my computed variables don't update with new elements added to the observableArray
Please could you help?
Here is the code, also attached a jsFiddle
Thanks
https://jsfiddle.net/r3pasftb/18/
var data = {id: 45, Name: "Whatever Name",
Stages: [{stageID: 1, StageName: "Stage1", Team: [{Name: "Team1", FeesAllocated: 500}, {Name: "Team2", FeesAllocated: 200}]}, {stageID: 1, StageName: "Stage1", Team: []}]};
mappingTeam = function(options){
var teamSelf = options != null ? options : this;
teamSelf.Name = ko.observable(options != null ? options.Name : 0);
teamSelf.FeesAllocated = ko.observable(options != null ? options.FeesAllocated : 0);
return teamSelf;
};
mappingStages = function(options){
var stageSelf = options != null ? options : this;
stageSelf.StageID = ko.observable(options != null ? options.StageID : 0);
stageSelf.StageName = ko.observable(options != null ? options.StageName : 0);
stageSelf.Team = ko.observableArray(ko.utils.arrayMap(stageSelf.Team, mappingTeam ));
stageSelf.Length = ko.computed(function(){
return stageSelf.Team().length;
});
//I need this value to be updated, but do nothing with new elements created after the mapping
stageSelf.TotalStageFees = ko.computed(function(){
var total = 0;
ko.utils.arrayForEach(stageSelf.Team(), function (team) {
total += team.FeesAllocated();
});
return total;
});
return stageSelf;
};
var mappingOptions = {
create: function (options) {
return new function () {
var self = options.data;
//Here map Stage Array
self.Stages = ko.observableArray(ko.utils.arrayMap(self.Stages, mappingStages ));
ko.mapping.fromJS(self, {}, this);
};
}
};
//Map my object with child and subchild
var self = ko.mapping.fromJS(data, mappingOptions);
console.log(self.Stages()[0].TotalStageFees())
console.log(self.Stages()[0].Length())
//Add new TeamMember to first Stage
self.Stages()[0].Team.push(new mappingTeam({Name: "Team3", FeesAllocated: 50}));
//As you see, non TotalStageFees or Lenght are updated
console.log(self.Stages()[0].TotalStageFees())
console.log(self.Stages()[0].Team())
console.log(self.Stages()[0].Length())
Related
The code below will get the URL variables..
For example: http://www.example.com/index.php?id=1&image=awesome.jpg
Calling getQueryVariable(“id”) – would return “1”
Calling getQueryVariable(“image”) – would return “awesome.jpg”
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
What if i want to return default value with the URL without query parameters? http://www.example.com/index.php
I still want to return “id” value = “predefined value"
You can do something like:
function getQueryVariable(variable)
{
default_values= {'id':-1,'image':'123.jpg'}
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(default_values[variable] || false);
}
Two suggestions:
1) set a default options object and return the desired value of that key if the url contains no parameters.
function getQueryVariable(variable)
{
var defaultValues = {
id: 'predefined valie',
image: 'defaultimage.jpg'
}
var query = window.location.search.substring(1);
var vars = query.split("&");
if (vars.length == 0) return defaultValues[variable] || false;
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
getQueryVariable('id'); //will return query parameter id or 'predefined value' if the url contains no parameters
2) or set the desired default value when calling the function.
function getQueryVariable(variable, defaultValue)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
if (vars.length == 0) return defaultValue || false;
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
getQueryVariable('id', 'predefined value'); //will return query parameter id or 'predefined value' if the url contains no parameters
getQueryVariable('image', 'defaultimage.jpg'); //will return query parameter image or 'defaultimage.jpg' if the url contains no parameters
I have create a group in phaserjs
this.fruitsOutline = this.game.add.group();
After that I have added few sprites in it. Everything is working correctly. Now if want to access this.fruitsOutline, from inside of a onDragStart event handler, it is giving undefined
var GameState = {
init:function(){
this.physics.startSystem(Phaser.Physics.ARCADE);
},
create: function () {
var innerImgPos = {x:150,y:200};
var outlineImgPos = {x:460,y:200};
var FIXED_DISTANCE_Y = 150;
var gameData = [
//...some data
];
this.background = this.game.add.sprite(0, 0, 'background');
this.overlapHappen = false;
this.startPos = {x:150,y:197};
this.fruitsOutline = this.game.add.group();
this.fruitsInner = this.game.add.group();
for(var i=0; i<gameData.length; i++){
fruitOuter = this.fruitsOutline.create(outlineImgPos.x,((outlineImgPos.y+25)*i)+FIXED_DISTANCE_Y,gameData[i].fruit_outline.img);
fruitOuter.name = gameData[i].fruitName;
fruitOuter.anchor.setTo(.5);
fruitOuter.customParams = {myName:gameData[i].fruit_outline.name};
this.game.physics.arcade.enable(fruitOuter);
fruitOuter.body.setSize(100,100,50,50);
fruitInner = this.fruitsInner.create(innerImgPos.x,((innerImgPos.y+25)*i)+FIXED_DISTANCE_Y,gameData[i].fruit_inner.img);
fruitInner.name = gameData[i].fruitName;
fruitInner.anchor.setTo(0.5);
fruitInner.inputEnabled = true;
fruitInner.input.enableDrag();
fruitInner.input.pixelPerfectOver = true;
fruitInner.originalPos = {x:fruitInner.position.x,y:fruitInner.position.y};
this.game.physics.arcade.enable(fruitInner);
fruitInner.body.setSize(100,100,50,50);
fruitInner.customParams = {myName:gameData[i].fruit_inner.name,targetKey:fruitOuter,targetImg:gameData[i].fruit_outline.name};
fruitInner.events.onDragStart.add(this.onDragStart);
fruitInner.events.onDragStop.add(this.onDragStop,this);
}
},
update: function () {
},
onDragStart:function(sprite,pointer){
console.log(this.fruitsInner) //this gives undefined I expect an array
},
onDragStop:function(sprite,pointer){
var endSprite = sprite.customParams.targetKey;
console.log(endSprite);
this.stopDrag(sprite,endSprite)
},
stopDrag:function(currentSprite,endSprite){
var currentSpriteRight = currentSprite.position.x + (currentSprite.width / 2);
if (!this.game.physics.arcade.overlap(currentSprite, endSprite, function() {
var currentSpriteTarget = currentSprite.customParams.targetImg;
var endSpriteName = endSprite.customParams.myName;
console.log(currentSpriteTarget,endSpriteName);
if(currentSpriteTarget === endSpriteName){
currentSprite.input.draggable = false;
currentSprite.position.copyFrom(endSprite.position);
currentSprite.anchor.setTo(endSprite.anchor.x, endSprite.anchor.y);
}
console.log(currentSprite.width);
})) {
//currentSprite.position.copyFrom(currentSprite.originalPosition);
currentSprite.position.x = currentSprite.originalPos.x;
currentSprite.position.y = currentSprite.originalPos.y;
}
},
render:function(){
game.debug.body(this.fruitsInner);
//game.debug.body(this.orange_outline);
}
}
You're missing the context when adding the drag callback.
Try that (adding this as the second argument):
fruitInner.events.onDragStart.add(this.onDragStart, this);
Oh, and inside the callback (or anywhere in the state) this.fruitsInner will be an instance of Phaser.Group, not an array like that comment says. The array you're looking for is probably this.fruitsInner.children.
I'm using the SuiteTalk (API) service for NetSuite to retrieve a list of Assemblies. I need to load the InventoryDetails fields on the results to view the serial/lot numbers assigned to the items. This is the current code that I'm using, but the results still show those fields to come back as NULL, although I can see the other fields for the AssemblyBuild object. How do I get the inventory details (serials/lot#'s) to return on a transaction search?
public static List<AssemblyBuildResult> Get()
{
var listAssemblyBuilds = new List<AssemblyBuildResult>();
var service = Service.Context();
var ts = new TransactionSearch();
var tsb = new TransactionSearchBasic();
var sfType = new SearchEnumMultiSelectField
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new string[] { "_assemblyBuild" }
};
tsb.type = sfType;
ts.basic = tsb;
ts.inventoryDetailJoin = new InventoryDetailSearchBasic();
// perform the search
var response = service.search(ts);
response.pageSizeSpecified = true;
// Process response
if (response.status.isSuccess)
{
// Process the records returned in the response
// Get more records with pagination
if (response.totalRecords > 0)
{
for (var x = 1; x <= response.totalPages; x++)
{
var records = response.recordList;
foreach (var t in records)
{
var ab = (AssemblyBuild) t;
listAssemblyBuilds.Add(GetAssemblyBuildsResult(ab));
}
if (response.pageIndex < response.totalPages)
{
response = service.searchMoreWithId(response.searchId, x + 1);
}
}
}
}
// Parse and return NetSuite WorkOrder into assembly WorkOrderResult list
return listAssemblyBuilds;
}
After much pain and suffering, I was able to solve this problem with the following code:
/// <summary>
/// Returns List of AssemblyBuilds from NetSuite
/// </summary>
/// <returns></returns>
public static List<AssemblyBuildResult> Get(string id = "", bool getDetails = false)
{
// Object to populate and return results
var listAssemblyBuilds = new List<AssemblyBuildResult>();
// Initiate Service and SavedSearch (TransactionSearchAdvanced)
var service = Service.Context();
var tsa = new TransactionSearchAdvanced
{
savedSearchScriptId = "customsearch_web_assemblysearchmainlist"
};
// Filter by ID if specified
if (id != "")
{
tsa.criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
internalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] {
new RecordRef() {
type = RecordType.assemblyBuild,
typeSpecified = true,
internalId = id
}
}
}
}
};
}
// Construct custom columns to return
var tsr = new TransactionSearchRow();
var tsrb = new TransactionSearchRowBasic();
var orderIdCols = new SearchColumnSelectField[1];
var orderIdCol = new SearchColumnSelectField();
orderIdCols[0] = orderIdCol;
tsrb.internalId = orderIdCols;
var tranDateCols = new SearchColumnDateField[1];
var tranDateCol = new SearchColumnDateField();
tranDateCols[0] = tranDateCol;
tsrb.tranDate = tranDateCols;
var serialNumberCols = new SearchColumnStringField[1];
var serialNumberCol = new SearchColumnStringField();
serialNumberCols[0] = serialNumberCol;
tsrb.serialNumbers = serialNumberCols;
// Perform the Search
tsr.basic = tsrb;
tsa.columns = tsr;
var response = service.search(tsa);
// Process response
if (response.status.isSuccess)
{
var searchRows = response.searchRowList;
if (searchRows != null && searchRows.Length >= 1)
{
foreach (SearchRow t in searchRows)
{
var transactionRow = (TransactionSearchRow)t;
listAssemblyBuilds.Add(GetAssemblyBuildsResult(transactionRow, getDetails));
}
}
}
// Parse and return NetSuite WorkOrder into assembly WorkOrderResult list
return listAssemblyBuilds;
}
private static string GetAssemblyBuildLotNumbers(string id)
{
var service = Service.Context();
var serialNumbers = "";
var tsa = new TransactionSearchAdvanced
{
savedSearchScriptId = "customsearch_web_assemblysearchlineitems"
};
service.searchPreferences = new SearchPreferences { bodyFieldsOnly = false };
tsa.criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
internalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] {
new RecordRef() {
type = RecordType.assemblyBuild,
typeSpecified = true,
internalId = id
}
}
}
}
};
// Construct custom columns to return
var tsr = new TransactionSearchRow();
var tsrb = new TransactionSearchRowBasic();
var orderIdCols = new SearchColumnSelectField[1];
var orderIdCol = new SearchColumnSelectField();
orderIdCols[0] = orderIdCol;
tsrb.internalId = orderIdCols;
var serialNumberCols = new SearchColumnStringField[1];
var serialNumberCol = new SearchColumnStringField();
serialNumberCols[0] = serialNumberCol;
tsrb.serialNumbers = serialNumberCols;
tsr.basic = tsrb;
tsa.columns = tsr;
var response = service.search(tsa);
if (response.status.isSuccess)
{
var searchRows = response.searchRowList;
if (searchRows != null && searchRows.Length >= 1)
{
foreach (SearchRow t in searchRows)
{
var transactionRow = (TransactionSearchRow)t;
if (transactionRow.basic.serialNumbers != null)
{
return transactionRow.basic.serialNumbers[0].searchValue;
}
}
}
}
return serialNumbers;
}
private static AssemblyBuildResult GetAssemblyBuildsResult(TransactionSearchRow tsr, bool getDetails)
{
if (tsr != null)
{
var assemblyInfo = new AssemblyBuildResult
{
NetSuiteId = tsr.basic.internalId[0].searchValue.internalId,
ManufacturedDate = tsr.basic.tranDate[0].searchValue,
SerialNumbers = tsr.basic.serialNumbers[0].searchValue
};
// If selected, this will do additional NetSuite queries to get detailed data (slower)
if (getDetails)
{
// Look up Lot Number
assemblyInfo.LotNumber = GetAssemblyBuildLotNumbers(tsr.basic.internalId[0].searchValue.internalId);
}
return assemblyInfo;
}
return null;
}
What I learned about pulling data from NetSuite:
Using SavedSearches is the best method to pull data that doesn't automatically come through in the API objects
It is barely supported
Don't specify an ID on the SavedSearch, specify a criteria in the TransactionSearch to get one record
You will need to specify which columns to actually pull down. NetSuite doesn't just send you the data from a SavedSearch automatically
You cannot view data in a SavedSearch that contains a Grouping
In the Saved Search, use the Criteria Main Line = true/false to read data from the main record (top of UI screen), and line items (bottom of screen)
I use this function to watch an array of objects for changes:
$scope.$watch('Data', function (newVal) { /*...*/ }, true);
How can I get an object in which property has been changed so that I can push it in an array?
For example:
var myApp = angular.module("myApp", []);
myApp.factory("Data", function(){
var Data = [{id:1, property: "Random"}, {id:2, property: "Random again"}];
return Data;
});
var myBigArray = [];
function tableCtrl($scope, Data){
$scope.TheData = Data;
$scope.$watch("TheData", function() {
//Here an object should be pushed
myBigArray.push(">>Object in which property has been changed <<<");
}, true);
}
I don't see a way currently in Angular to get the changed object... I suspect you might need to traverse the new array and try to find the differences with the old array...
Edit: Note that this solution turns out to be a bad practice as it is adding a lot of watchers, which is something you do not want because it has a performance penalty.
=======
I eventually came up with this solution:
items.query(function (result) {
_(result).each(function (item, i) {
$scope.items.push(item);
$scope.$watch('items[' + i + ']' , function(){
console.log(item); // This is the item that changed.
}, true);
});
});
There is still no option like this for $watch, but you can use jQuery plugin for that, http://archive.plugins.jquery.com/project/jquery-diff
I implemented undo/redo with AngularJS using $watch, mb this can help
//History Manager Factory
.factory('HistoryManager', function () {
return function(scope) {
this.container = Array();
this.index = -1;
this.lock = false;
//Insert new step into array of steps
this.pushDo = function() {
//we make sure that we have real changes by converting to json,
//and getting rid of all hash changes
if(this.container.length == 0 || (angular.toJson(scope.widgetSlider) != angular.toJson(this.container[this.index][0]))) {
//check if current change didn't came from "undo" change'
if(this.lock) {
return;
}
//Cutting array, from current index, because of new change added
if(this.index < this.container.length-1) {
this.container = this.container.slice(0, this.index+1);
}
var currentStepSlider = angular.copy(scope.widgetSlider);
var selectedWidgetIndex = scope.widgetSlider.widgets.indexOf(scope.widgetCurrent);
//Initialising index, because of new "Do" added
this.index = this.container.length;
this.container.push([currentStepSlider, selectedWidgetIndex]);
if (this.onDo) {
this.onDo();
}
}
}
//Upon undo returns previous do
this.undo = function() {
this.lock = true;
if(this.index>0){
this.index--;
scope.widgetSlider = angular.copy(this.container[this.index][0]);
var selectedWidgetIndex = this.container[this.index][1];
scope.widgetCurrent = scope.widgetSlider.widgets[selectedWidgetIndex];
}
this.lock = false;
}
//Upon redo returns next do
this.redo = function() {
if(this.index < this.container.length-1) {
this.index++;
scope.widgetSlider = angular.copy(this.container[this.index][0]);
var selectedWidgetIndex = this.container[this.index][1];
scope.widgetCurrent = scope.widgetSlider.widgets[selectedWidgetIndex];
}
}
}
})
;
I have a method that calls stored procedure and returns the data after executing DataReader.
I am trying to test the method using mock. I am not sure how to return value?
Anyone did this? Appreciate your responses.
Here is my code:
// Call the StoredProcedure
public List<string> GetCompletedBatchList(int fileId)
{
List<string> completedBatches = new List<string>();
StoredProcedure sp = new StoredProcedure("GetDistributedBatches", this.dataProvider);
sp.Command.AddParameter("FileID", fileId, DbType.Int32, ParameterDirection.Input);
sp.Command.AddParameter("Result", null, DbType.Int32, ParameterDirection.InputOutput);
using (var rdr = sp.ExecuteReader())
{
while (rdr != null && rdr.Read())
{
if (rdr[0] != null)
{
completedBatches.Add(rdr[0].ToString());
}
}
}
return completedBatches;
}
Here is the Test Method:
[Test]
public void Can_get_completedBatches()
{
var file = new File() { FileID = 1, DepositDate = DateTime.Now };
repo.Add<File>(file);
CompletedBatches completedBatches = new CompletedBatches(provider.Object);
//Here I am not sure how to Return
provider.Setup(**x => x.ExecuteReader(It.IsAny<QueryCommand>())).Returns** =>
{
cmd.OutputValues.Add(0);
});
var completedBatchesList = completedBatches.GetCompletedBatchList(file.FileID);
Assert.AreEqual(0, completedBatchesList.Count());
}
If you want to create a DataReader of a certain shape and size then I suggest you look at DataTable.CreateDataReader DataTable.CreateDataReader. You can then setup the ExecuteReader in your example to return this datareader.
The following link helped me...
How to mock an SqlDataReader using Moq - Update
I used MockDbDataReader method to mock the data
[Test]
public void Can_get_completedBatches_return_single_batch()
{
var date = DateTime.Now;
var file = new File() { FileID = 202, DepositDate = DateTime.Now };
var batch1 = new Batch() { FileID = 202, BatchID = 1767, LockboxNumber = "1", IsLocked = true, LockedBy = "testUser" };
var transaction1 = new Transaction() { BatchID = 1767, TransactionID = 63423, CheckAmount = 100.0 };
var distribution1 = new Distribution() { TransactionID = 63423, InvoiceNumber = "001", Amount = 100.0, DateCreated = date, DateModified = date, TransType = 2 };
repo.Add<File>(file);
repo.Add<Batch>(batch1);
repo.Add<Transaction>(transaction1);
repo.Add<Distribution>(distribution1);
CompletedBatches completedBatches = new CompletedBatches(provider.Object);
provider.Setup(x => x.ExecuteReader(It.IsAny<QueryCommand>())).Returns(MockDbDataReader());
var completedBatchesList = completedBatches.GetCompletedBatchList(202);
Assert.AreEqual(1, completedBatchesList.Count());
}
// You should pass here a list of test items, their data
// will be returned by IDataReader
private DbDataReader MockDbDataReader(List<TestData> ojectsToEmulate)
{
var moq = new Mock<DbDataReader>();
// This var stores current position in 'ojectsToEmulate' list
int count = -1;
moq.Setup(x => x.Read())
// Return 'True' while list still has an item
.Returns(() => count < ojectsToEmulate.Count - 1)
// Go to next position
.Callback(() => count++);
moq.Setup(x => x["BatchID"])
// Again, use lazy initialization via lambda expression
.Returns(() => ojectsToEmulate[count].ValidChar);
return moq.Object;
}