Facing issue with knockout binding - javascript

I am having multiple tabs(A,B,C) and on load of 'C' tab,the model properties should data-bind to tab 'c'.
I am facing issue with data-bind.
The three tabs(A,B,C) are inside another view View A.
Here is my VIEW A
<div id="tabMain" style="width:1230px;height:auto;overflow:auto;">
<ul>
<li>A</li>
<li>B</li>
<li data-bind="click:loProvision">C
Here is my Tab 'c'
<div id="SubC">
<ul>
<li>tabc1</li>
</ul>
<div id="tabC1" style="overflow:auto;">
<div>
#Html.HiddenFor(m => m.UID)
<div style="width:500px;height:20px;margin-left:30px">
<div >#Html.NCheckBoxFor(m => m.EnablePD, new { id = "cbPD", style = "vertical-align:middle;", data_bind = "checked:enablePD" }, Model.IsReadOnly)</div>
<div >
<label for="cbOrgDetailsPD">#Res.OrganizationStrings.Ect</label>
</div>
</div>
<div style="width:100%;margin-top:10px;margin-left:30px">
<div style="width:22%;">
<label for="ddPD">Strings.PlaceUsersInGroup</label>
</div>
<div style="width:45%">#Html.NDropDownListFor(m => m.PD, Model.groups, new { id = "ddPD", style = "width:270px;height:25px;vertical-align:middle;", , data_bind = "enable: enablePD"
}, Model.IsReadOnly)</div>
</div>
<div style="margin-top:20px;margin-left:30px">
<div >#Html.NCheckBoxFor(m => m.ProType, new { id = "cbType", style = "vertical-align:middle;", data_bind = "checked:enableing" }, Model.IsReadOnly)</div>
<div >
<label for="cbByType">#Res.Strings.Enableing</label>
</div>
</div>
</div>
</div>
The View which containing the tabs is having the viewmodel and i want to bind the tab 'c' components when the tab 'c' loaded.
Here is my Javascript code:
function Intialize {
var model = new MainviewModel();
ko.applyBindings(model, document.getElementById(orgDetailsTab.getId()));
}
function MainviewModel() {
this.loadvision = function() {
if (isvisionsLoaded === false) {
var autoUrl = '/Org/OrgView?UID=' + UID + '&isReadOnly=' + isReadOnly;
loadvision();
}
};
}
var CAPDModel;
function loadvision() {
try {
$('#C').empty();
$.ajaxSetup({
cache: false
});
$('#C').load(visionUrl, function(responseText, textStatus, XMLHttpRequest) {
$("#SubC").tabs().addClass("ui-tabs-vertical ui-helper-clearfix");
isLoaded = true;
CModel = new organizationViewModel();
ko.applyBindings(CModel, document.getElementById(tabC1));
function orgViewModel() {
this.enablePD = ko.observable($('#cbOrgPD').is(':checked'));
this.enableCing = ko.observable($('#cbType').is(':checked'));
this.enableLicense = ko.observable($('#cbOrgDetailsLicenses').is(':checked'));
this.cansee = ko.observable(false);
this.canRemove = ko.observable(false);
}
}
I am getting exception at
ko.applyBindings(CModel, document.getElementById(tabC1));
My requirement is on the load of Tab 'tabC1' the html attributes should be data-binded ( disabling and enabling of html fields )should happen.
I intially placed the CAPD related properties in 'MainviewModel' but the binding is not happening.
So i moved the C propeties from 'MainviewModel' to 'tabC1' load fucntion,but still the data-bind is not happening.
So i created a new viewmodel inside the 'tabC1' div load fucntion and trying to apply bindings on the load of div 'tabC1'.
Can some one guide if the approach is wrong or let me know how can i acheive it.
Here is the error details i am facing at
ko.applyBindings(CModel, document.getElementById(tabC1));
Unhandled exception at line 26076, column 17 in eval code
0x800a139e - JavaScript runtime error: Unable to parse bindings.
Message: ReferenceError: 'loadvision' is undefined;
Bindings value: click:loadvision

I found the solution for the above issue,
I changed the line
ko.applyBindings(CAPDModel, document.getElementById(tabC1)); changed to
ko.applyBindings(CAPDModel, $("#tabC1")[0]);

Related

How to get element by id which is dynamically created by ng-repeat in angularjs

I am only posting the necessary code and solving this much will clear rest of my doubts. I am new to angularjs, so kindly forgive if I am asking something stupid.
I am using ng-repeat to generate a list which uses an array defined in the controller scope. When I click on 'Add Another' button, a new element is created. I want to get access of this element to add a class to it. But when I use 'getElementById' function in the same function 'addNewForm' I get 'null'.
However, when I call function 'fn' by hitting 'Find Untitled' button, I get the correct element. Could anybody explain and solve this? Any help is appreciated. Thanks in advance!
I am posting the code below:
HTML:
<div ng-controller="myctrl3">
<ul id ="menu_Ul">
<li ng-repeat="x in list">
<button id="{{ 'navAppsButtonID-' + $index }}">{{x}}</button>
<br>
</li>
<li>
<button ng-click="addNewForm()">Add another</button>
</li>
</ul>
<button ng-click="fn()">Find Untitled</button>
</div>
JS:
.controller("myctrl3", function($scope) {
var list = ['abcd', 'efgh', 'ijkl', 'mnop'];
$scope.list = list;
$scope.abc = function () {
var listPush = function () {
$scope.list.push("Untitled Menu");
for(var i = 0;i<$scope.list.length-1;i++) {
var element = document.getElementById('navAppsButtonID-'+i);
element.classList.remove('current');
}
};
var listLen = $scope.list.length;
if($scope.list[listLen-1] === undefined) {
listPush();
}
else if ($scope.list[listLen-1] == "Untitled Menu") {
alert("Cannot open more than one Untitled Menu at the same time.");
}
else {
listPush();
}
};
$scope.addNewForm = function() {
$scope.abc();
console.log("Element is: ", document.getElementById('navAppsButtonID-'+($scope.list.length-1)));
};
$scope.fn = function () {
console.log("Element is: ", document.getElementById('navAppsButtonID-'+($scope.list.length-1)));
};
})
You're thinking too much jQuery and too little angular. If the goal is to add a class to the last element of ng-repeat, this is how you do that:
<li ng-repeat="x in list">
<button ng-class="{ current: $last }">{{ x }}</button>
</li>
$last is a variable available inside ng-repeat, and if it's true, ng-class will set the class current on the element.
You don't assign unique ids to elements to getElementById from somewhere else when working in angular.

Angular two-way binding

I have an Angular directive, which reads in the contents of a file <input> and populates a form. Currently, it seems to update the $scope variables within the controller, but the form elements bound to those $scope variables are not being updated in the view. Why is this?
The code snippets below are as follows:
my directive, which i use to read in from a file <input> and call the controller's $scope['modify_form'], which allows me to update the $scope variables corresponding to my form.
the HTML (view-side) for my directive
my controller logic for $scope['modify_form'], which calls a bunch of helper functions, one of which is shown below.
a snippet of the HTML of my view for my form; this is where the fundamental problem lies, since it does not get updated when i call $scope['modify_form']
Here is my directive, for reading in content:
app.directive('modifyBtn', function($compile) {
return {
restrict: 'E',
scope: {
modifyJSON:'&modifyJson',
},
link: function(scope, element, attrs) {
var fileElem = angular.element(element.find("#file_input2"));
console.log(fileElem);
var showUpload = function() {
// display file input element
var file = fileElem[0].files[0];
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(e) {
var uploadedJSON;
try {
uploadedJSON = JSON.parse(reader.result);
} catch(e) {
// should display help block (or warning)
return;
}
console.log(uploadedJSON); // #debug
for (var key in uploadedJSON) { // only one key
if (uploadedJSON.hasOwnProperty(key)) {
sessionStorage[MODEL] = key;
sessionStorage[NAMESPACE] = uploadedJSON[key][NAMESPACE]
var fields = [FIELDS, LINKS, PRIMARY_KEY, TABLE, SQL, VALIDATIONS];
fields.forEach(function(field) {
if (uploadedJSON[key].hasOwnProperty(field)) {
sessionStorage[field] = JSON.stringify(uploadedJSON[key][field]);
}
});
// trigger modification without reloading page
scope.modifyJSON();
}
}
}
};
$(fileElem).on('change', showUpload);
}
}
});
My view (HTML), for the directive is as follows:
<modify-btn modify-json="modifyForm()">
<li class="active">
<span class="btn creation-btn" style="background-color: #d9534f;" ng-click="fileshow = true">
Upload JSON
</span>
</li>
<li><input type="file" id="file_input2" ng-show="fileshow" /></li>
</modify-btn>
In my controller, here is where I update the $scope variables bound to the form:
$scope['modifyForm'] = function() { // filling in elements if reading in JSON
modify_data();
modify_fields();
modify_links();
modify_pkeys();
modify_table();
modify_sql();
modify_validations();
sessionStorage.clear();
}
function modify_data() {
var overall = [MODEL, NAMESPACE];
overall.forEach(function(elem) {
if (exist(sessionStorage[elem])) {
$scope['data'][elem] = sessionStorage[elem];
}
});
}
And, here, in the view is how my form elements are bound.
<div class="form-group">
<label class="col-sm-4">{{myConstants["model"]}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" ng-model="data[myConstants['model']]" id="model" />
</div>
</div>
<div class="form-group">
<label class="col-sm-4">{{myConstants["namespace"]}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" ng-model="data[myConstants['namespace']]" id="namespace" />
</div>
</div>

How to Refresh Knockout ViewModel Using Mapping Plugin

I have the following Knockout ViewModel:
var EditorViewModel = function () {
var self = this;
self.addData = function (_data) {
ko.mapping.fromJS(_data, {}, self);
};
};
which is used as such:
var viewModel = new EditorViewModel();
viewModel.addData(model);
ko.applyBindings(viewModel);
For info, the data for viewModel.addData() comes from an AJAX call.
The View/ViewModel is populated with no problem the first time round when ko.applyBindings is called. However, if I later pull new data from the server via AJAX and then call either:
viewModel.addData(someNewAjaxData)
or
ko.mapping.fromJS(someNewAjaxData, {}, viewModel);
Then Knockout does not update the view with the new data. Hopefully this is a trivial problem...?!
KnockoutJS Mapping has the ability to specify a key for an item. This lets KnockoutJS know when an item needs to be created versus updated.
var ItemViewModel = function(d){
var self = this;
self.id = ko.observable(d.id);
self.text = ko.observable(d.text);
// We assign it when the object's created to demonstrate the difference between an
// update and a create.
self.lastUpdated = ko.observable(new Date().toUTCString());
}
var EditorViewModel = function(){
var self = this;
self.items = ko.observableArray();
self.addData = function(d){
ko.mapping.fromJS(d, {
'items': {
'create': function(o){
return new ItemViewModel(o.data);
},
'key': function(i){
return ko.utils.unwrapObservable(i.id);
}
}
}, self);
console.info(self);
}
self.addMoreData = function(){
self.addData({
'items': [
{id:1,text:'Foo'},
{id:3,text:'Bar'}
]
});
}
}
var viewModel = new EditorViewModel();
viewModel.addData({
items:[
{id:1,text:'Hello'},
{id:2,text:'World'}
]
});
ko.applyBindings(viewModel);
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Hello, world!</h3>
</div>
<ul class="list-group">
<!-- ko foreach:items -->
<li class="list-group-item">
<span class="text-muted" data-bind="text:id"></span>
<span data-bind="text:text"></span>
<span class="text-danger pull-right" data-bind="text:lastUpdated"></span>
</li>
<!-- /ko -->
<li class="list-group-item" data-bind="visible:!items().length">
<span class="text-muted">No items.</span>
</li>
</ul>
<div class="panel-footer">
Update
</div>
</div>
</div>
</div>
</div>
Observe, in the attached snippet, how the first item's lastUpdated value is assigned when it's created, but is left unchanged when it's updated. This is because the mapper sees the ID already exists in items and simply updated the properties. But, for item with the id 3, a new model is created.
All knockout bibdings were subscribed to the first created model. You recreate the bound model on every ajax response. Your problem seems similar that was discussed in this stackoverflow article. The same osbervable should be bound to the markup.
Try this (may be you should support an empty model binding):
// in the start of app
var observableModel = ko.observable();
ko.applyBindings(observableModel);
// on every ajax call
var viewModel = new EditorViewModel();
viewModel.addData(model);
observableModel(viewModel);

AngularJS - Not able to update model programatically

When I update my model through method, model does not update. Below is my AngularJS code:
var app = angular.module('MyApp',[]);
app.controller('businessCtrl', function($scope, Business) {
$scope.businesses = Business.query(); // getting data using REST
$scope.currentBusinessIndex = 0;
$scope.changeBusinessIndex = function (indx) {
if (indx) {
$scope.currentBusinessIndex = indx;
} else {
$scope.currentBusinessIndex = 0;
}
};
});
Below is the HTML:
<ul class="signups">
<li ng-controller="businessCtrl" ng-repeat="business in businesses">
<div class="user pull-left" ng-click="changeBusinessIndex($index)">
<img ng-src="http://localhost:8081{{business.logo.uri}}" alt="business logo" >
</div>
<div class="info" ng-click="changeBusinessIndex($index)">
<h6 ng-cloak>{{business.name}}</h6>
<small ng-cloak>{{business.streetAddress + ',' + business.city + ',' + business.country}}</small>
</div>
</li>
</ul>
<div id="business-image" class="main-container" ng-controller="businessCtrl">
<img ng-src="http://localhost:8081{{businesses[currentBusinessIndex].logo.uri}}" alt="Business Logo">
</div>
Problem:
In changeBusinessIndex() method, when I modify currentBusinessIndex, it does not modify and as a result image in "business-image" div is not updating.
You are creating two different instances of businessCtrl, one for the li (let's call it businessCtrl1) and one for the business-image div (let's call that businessCtrl2). When you are calling changeBusinessIndex() in the li-instance it will update currentBusinessIndex for businessCtrl1, but not for the other instance.
Set ng-controller="businessCtrl" on an element that wraps both the li and the business-image divs to get a single instance of the controller, something like:
<div ng-controller="businessCtrl>
<li ng-repeat ... etc ></li>
<div id="business-image" ... ></div>
</div>
First of all it seems strange that you call twice ng-controller="businessCtrl" (with the same name). But if you interesting to use two different controllers I would store currentBusinessIndex into the Service and use it as storage. Since service is singleton, you can register the service to both controllers and by this way your logic should work.
HTML
<div ng-controller="MyCtrl_1">
<button ng-click="showMe();">press me</button>
<pre>{{myStorage.getIndex()}}</pre>
</div>
<div ng-controller="MyCtrl_2">
<pre>{{myStorage.getIndex()}}</pre>
</div>
JS
var myApp = angular.module('myApp', []);
function MyCtrl_1($scope, myStorage) {
$scope.myStorage = myStorage;
$scope.showMe = function(){
myStorage.setIndex(3);
}
}
function MyCtrl_2($scope, myStorage) {
$scope.myStorage = myStorage;
}
myApp.factory('myStorage', function () {
var currentBusinessIndex = 0;
return {
getIndex: function () {
return currentBusinessIndex;
},
setIndex: function (index) {
currentBusinessIndex = index;
}
}
});
Demo Fiddle

define Picked Files from File picker as Listview datasource in windows 8

I am trying to Set files picked using filepiker as List view datasource.
In Windows 8 using Javascript.
HTML:
<div id="smallListIconTextTemplate" data-win-control="WinJS.Binding.Template">
<div class="smallListIconTextItem">
<img src="#" class="smallListIconTextItem-Image" data-win-bind="src: picture" />
<div class="smallListIconTextItem-Detail">
<h3 data-win-bind="innerText: title"></h3>
<h6 data-win-bind="innerText: text"></h6>
</div>
</div>
</div>
<div id="listView_Id" data-win-control="WinJS.UI.ListView" data-win-options="{
itemDataSource: FileData.itemList.dataSource,
itemTemplate: select('#smallListIconTextTemplate'),}">
JS:
var dataArray = new Array();
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
openPicker.fileTypeFilter.replaceAll([".epub"]);
var file;
openPicker.pickMultipleFilesAsync().then(function (files)
{
if (files.size > 0)
{
for (var i = 0; i < files.size; i++)
{
dataArray.push({
title: files[i].displayName,
text: "Author",
picture: "/images/BookImage.jpg"
});
var dataList = new WinJS.Binding.List(dataArray);
var publicMembers =
{
itemList: dataList
};
WinJS.Namespace.define("FileData", publicMembers);
I have array with file data. But unable to get in ListView
There is a binding issue in the code. FileData.itemList is not initialized at the time of page ui initialization. Right way to do that will be to have a view model observable class with itemList member and bind it to the DOM. Data Binding topic might help.
otherwise putting this should make it work. You may have to fix up your template to get the display right.
listView_Id.winControl.itemDataSource = dataList.dataSource;

Categories

Resources