In stages, I setup my .Net MVC solution and ensured both Angular JS and KendoUI are working independently.
app.js:
var app = angular.module("app", ['kendo.directives']);
and in my controller, I have the following defined:
app.controller('contentTypesController', ['$scope', '$log', 'contentTypesRepository',
function ($scope, $log, contentTypesRepository) {
var a = {};
$scope.status;
$scope.contentTypes;
$scope.contentTypeOptions;
// for testing purposes, but not used - used for navigation properties
$scope.users;
getContentTypes();
function getContentTypes() {
// call the data repository
contentTypesRepository.getList()
.success(function (contentTypes) {
//console.log(contentTypes.value[0].Description);
//$log.log(contentTypes.value[0].Description)
$scope.contentTypes = contentTypes;
$scope.contentTypeOptions = {
dataSource: {
data: contentTypes
},
dataTextField: "Description",
dataValueField: "ContentTypeId",
optionLabel: "Select a Content Type"
};
})
.error(function (error) {
$scope.status = 'Unable to load data: ' + error.message;
});
}
$scope.updateContentTypes = function (id) {
var contentType;
for (var i = 0; i < $scope.contentTypes.length; i++) {
var currentType = $scope.contentTypes[i];
if (currentType.ID === id) {
contentType = currentType;
break;
}
}
};
$scope.insertContentType = function () {
// get contentType description from the client
var contentType = { 'Description': $scope.newContentType };
contentTypesRepository.insert(contentType)
.success(function () {
$scope.status = 'Added new content type. Refreshing list.';
// add the new content type to the client-side collection
$scope.contentTypes.value.push(
{ 'Description': $scope.newContentType }
);
$scope.newContentType = "";
})
.error(function (error) {
$scope.status = 'Unable to insert new content type: ' + error.message;
});
};
$scope.deleteContentType = function(id) {
contentTypesRepository.remove(id)
.success(function () {
$scope.status = 'Deleted content type. Refreshing list.';
for (var i = 0; i < $scope.contentTypes.length; i++) {
var contentType = $scope.contentTypes[i];
if (contentType.ID === id) {
// remove the content type from the client-side collection
$scope.contentTypes.splice(i, 1);
break;
}
}
// navigation properties = null
// $scope.xxxx = null;
})
.error(function (error) {
$scope.status = 'Unable to delete content type: ' + error.message;
});
};
// get some navigation property
//$scope.getCustomerOrders = function (id) {
// dataFactory.getOrders(id)
// .success(function (orders) {
// $scope.status = 'Retrieved orders!';
// $scope.orders = orders;
// })
// .error(function (error) {
// $scope.status = 'Error retrieving customers! ' + error.message;
// });
//};
$scope.addContentType = function () {
//return $scope.newContentType.$save();
$scope.contentTypes.value.push(
{ 'Description': $scope.newContentType }
);
$scope.newContentType = "";
}
In following the Angluar/Kendo examples here, I added code related to $scope.contentTypeOptions.
In my view:
<select kendo-drop-down-list k-options="contentTypeOptions"></select>
Which displays a dropdown, but no data.
I am able to view the data in a ng-repeater:
<ul>
<li ng-repeat="contentType in contentTypes.value">
{{ contentType.Description }}
</li>
</ul>
And the raw data by {{ contentTypeOptions }}.
Since the repeater uses contentTypes.value, I tried this as well in
$scope.contentTypeOptions = {
dataSource: {
data: contentTypes.value // tried both contentTypes and contentTypes.value
},
dataTextField: "Description",
dataValueField: "ContentTypeId",
optionLabel: "Select a Content Type"
};
... which is based on the JSON data:
Ultimately, I would like to get all the CRUD hooked up for a grid (which I have done in the past with OData, but now adding AngularJS to the mix) and thought simply displaying the data in an Angular/Kendo mix would be a good start. I'm hoping that after getting this pinned down the rest will be simple, and appreciate any suggestions.
Your code is a bit confusing since methods like $scope.updateContentTypes treat $scope.contentTypes as an array, but at the same time contentTypes appears to be an object with a property value which is an array.
One thing to be aware of is that Kendo UI widgets will convert your array to a Kendo DataSource internally. This means that changes you make to $scope.contentTypes won't affect the items in your data source in $scope.contentTypeOptions.
Another issue is that there is no full two-way binding between widgets and the data source in angular-kendo, and until recently, the data source wouldn't update at all unless you specifically declared it as a DataSource. There have been some improvements lately, although it's still not fully integrated, as far as I can see.
(you can try creating a deep watch on the data yourself, but that may create performance problems; see related post here).
Your dropdown doesn't show the data because you replace $scope.contentTypeOptions after creating the widget, and there is no $watch on that property that would update the widget with these options.
You can either create a DataSource explicitly and update that with:
$scope.contentTypeOptions.dataSource.data(contentType.value);
or you can use the attribute:
k-data-source="contentTypes"
which will create a $watch on $scope.contentTypes, so when you replace it, the widget will update as well.
Maybe this basic (although admittedly a bit messy) example will help you somewhat (I set up the 2nd dropdown in the same way you did; the "change" button updates the data source).
You will need to use the Angular Kendo bindings from Kendo labs.
Here is a an article with live demo and full source code:
http://blog.longle.io/2014/05/01/angularjs-kendo-ui-using-angular-kendo-with-asp-net-mvc-5-web-api-odata
Related
I'am trying to import a user's gmail contacts using Angular Js. The code is working fine in plain javascript but giving error in angular js.
HTML Code..
<a class="btn btn-primary btn-simple" ng-click="importgoogle()"><u>Import Gmail Friends</u></a>
Angular Code..
var clientId = 'Client ID';
var scopes = 'https://www.googleapis.com/auth/contacts.readonly';
$scope.importgoogle = function(){
window.setTimeout(authorize); //calls authorize()
}
var authorize = function(){
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthorization); //calls handleAuthorization()
}
var handleAuthorization = function(){
if (authorizationResult && !authorizationResult.error) {
$.get("https://www.google.com/m8/feeds/contacts/default/thin?alt=json&access_token=" + authorizationResult.access_token + "&max-results=500&v=3.0",
function(response){
console.log(response);
});
}
}
After entering a user's Id & password the following error message is displayed in console..
Uncaught ReferenceError: authorizationResult is not defined
Can't understand where I'm going wrong as this code is working in Javascript.Please help..
Here is the working example using Angular Js:
app.controller("importGCCtrl", function($scope, $http) {
$scope.config = {
'client_id': 'Client ID',
'scope': 'https://www.google.com/m8/feeds'
};
$scope.inviteContacts = function() {
gapi.auth.authorize($scope.config, function() {
$scope.fetch(gapi.auth.getToken());
});
}
$scope.fetch = function(token) {
$http.get("https://www.google.com/m8/feeds/contacts/default/full?access_token=" + token.access_token + "&alt=json").then(function(response) {
console.log(response);
//console.log(response.data.feed.entry);
//$scope.contacts = response.data.feed.entry; // to assign data
});
}
});
*NOTE: Please make sure you have included the API - <script src="https://apis.google.com/js/client.js"></script> on the page
The problem lies in the handleAuthorization function. The correct way of implementing that function is..
var handleAuthorization = function(authorizationResult){ //authorizationResult needs to be passed as an arguement.
if (authorizationResult && !authorizationResult.error) {
$.get("https://www.google.com/m8/feeds/contacts/default/thin?alt=json&access_token=" + authorizationResult.access_token + "&max-results=500&v=3.0",
function(response){
console.log(response);
});
}
}
After making this change the Angular Js code is now working properly.
So, i'm using AngularJS with X-Editable to make an easier way to edit my data.
I have a table with all the information of a client such as name, phone, address, etc.. And I could apply X-Editablejust fine until the moment I need to actually save the edit on the database.
Also, this page just show one single client, is an individual client page, with only his details.
This is the code I'm using:
page.html
<table fixed-header class="detcli_table" ng-init="get_detcliente()">
<thead>
<tr>
<th>Campo</th>
<th>Informação</th>
</tr>
</thead>
<tbody>
<tr>
<td>Código</td>
<td>{{cliente.id}}</td>
</tr>
<tr>
<td>Nome</td>
<td><span editable-text="cliente.nm_cliente" onaftersave="updatePerson(cliente.nm_cliente)">{{cliente.nm_cliente || "Empty"}}</span></td>
</tr>
<tr>
<td>Tel.</td>
<td><span editable-text="cliente.num_tel" onaftersave="updatePerson(cliente.num_tel)">{{cliente.num_tel || "Empty"}}</span></td>
</tr>
[... more code ...]
</tbody>
</table>
app.js
myApp.controller('DetClientesCtrl', ['$scope', '$http', '$routeParams', function ($scope, $http, $routeParams) {
var clienteId = $routeParams.id;
$scope.get_detcliente = function() {
var url = 'scripts/php/db.php?action=get_cliente';
return $http.get(url).success(httpSuccess).error(function() {
alert("Oops, erro!");
});
}
httpSuccess = function(response) {
$scope.detRes = response;
}
function getById(arr, id) {
for (var d = 0, len = arr.length; d < len; d += 1) {
if (arr[d].id === id) {
return arr[d];
}
}
}
$scope.get_detcliente().then(function(){
$scope.cliente = getById($scope.detRes,$routeParams.id);
});
//Update Client
$scope.updatePerson = function() {
$http.post('scripts/php/db.php?action=upd_cliente',
{
'id': $routeParams.id,
'nm_cliente' : $scope.nm_cliente,
'num_tel' : $scope.num_tel
}
).success(function (data, status, headers, config) {
$scope.get_detcliente();
console.log("efeutou o post!");
}).error(function (data, status, headers, config) {
console.log("Algo deu errado!");
});
};
}]);
control.php
This is the method i'm using to add new data, delete and, in this case, to update an existing data
function upd_cliente() {
$data = json_decode(file_get_contents("php://input"));
$id = $data->id;
$nm_cliente = $data->nm_cliente;
$num_tel = $data->num_tel;
$qry = "update cad_cliente set nm_cliente = '$nm_cliente', num_tel = '$num_tel' where cd = '$id'";
}
When I run the code, I get no errors at all. The console.log I'm using is showing properly in the console, the editing I do, is working fine on the screen but when I refresh the page, there is no data saved, it goes back to the previous data.
What may be wrong?
And also I don't know if this is the best way to do it, since I have a table with about 10 to 15 lines of information, so if I edit just 1 or 5 lines, the code will have to run for each edit I make.
Is there a better way to process it?
Well, after some research and a lot of try/fail i cam up with the solution.
in the page page.html i needed to remove the remove the code inside the (), so it is going to be like this:
page.html
<td><span editable-text="cliente.nm_cliente" onaftersave="updatePerson()">{{cliente.nm_cliente || "Empty"}}</span></td>
And on the app.js i needed to use $scope.cliente.nm_cliente instead of $scope.nm_cliente. So the code will be like this:
app.js
$scope.updatePerson = function() {
$http.post('scripts/php/db.php?action=upd_cliente',
{
'id': $routeParams.id,
'nm_cliente' : $scope.cliente.nm_cliente,
'num_tel' : $scope.cliente.num_tel
}
).success(function (data, status, headers, config) {
//Success code here
}).error(function (data, status, headers, config) {
//Error code here
});
};
Then, on the php file i just need to write the other fields i need to update on the database, in my case, it will be more than 15 fields to be possible to update.
Note: As far as i know, this code only works with the option onaftersave="" because if we use the option onbeforesave="", like the name itself, the data will not be passed since it's being executed before the new data is passed to the $scope.
I'm sorry if any of my information is wrong, i'm starting learning AngularJS right now. But this is working for me.
Also, i don't know if there is a better way to achieve this result, so, if anyone knows, please share it with us!
Ok so the scenario is currently I am populating a drop down list from my model with the following code
ViewBag.LeaseCompanyID = new SelectList(ContractModelEntity.system_supplier.Where(x => x.Type == "Lease"), "CompanyID", "Name", data.LeaseCompanyID);
This works perfectly, however on my form I have a button located next to the drop down list which adds another option in the database, using ajax and a modal popup.
The controller code for this is here
[HttpPost]
public JsonResult AddSupplier([Bind(Include="Name,Type")] system_supplier data)
{
if (ModelState.IsValid)
{
ContractModelEntity.system_supplier.Add(data);
ContractModelEntity.SaveChanges();
return Json(0, JsonRequestBehavior.AllowGet);
}
return Json(1, JsonRequestBehavior.AllowGet);
}
When the new option is added into the database I then need to refresh my dropdownlist to get this new data (currently if I refresh the page I can see the new option). I am using minimalect plugin for the drop downs.
Does anybody know a way of updating this minimalect list, there must be a way of building the list through an ajax call which returns some JSON data.
Thanks in advance for your help
OK so after doing a bit of research here is my solution, hopefully it will help other poeple. Someone might even have a cleaner solution.
I first created a jsonresult controller method which looked like this
[HttpGet]
public JsonResult RetreiveSuppliers(string contractType)
{
var supplierData = ContractModelEntity.system_supplier.Where(x => x.Type == contractType);
var result = new List<object>();
foreach (var x in supplierData)
{
result.Add(new { Id = x.CompanyID, Name = x.Name });
}
return Json(result, JsonRequestBehavior.AllowGet);
}
That got me the data from the database. then I created a javascript on the page which looks like this
$("body").on("click", "#btn_InsertNewSupplier", function () {
var supForm = $("#addSupData");
$.ajax({
url: "#Url.Action("AddLeaseSupplier", "Contract")",
data: supForm.serialize(),
type: "POST",
success: function (result) {
if (result === 0) {
var inst = $.remodal.lookup[$('[data-remodal-id=modal_AddSupplier]').data('remodal')];
inst.close();
NotificationAlert("success", "New Supplier Created");
GetNewSupplierList();
} else {
NotificationAlert("error", "Failed Adding New Supplier");
}
}
});
});
function GetNewSupplierList() {
var actionurl = "#Url.Action("RetreiveSuppliers", "Contract", new { contractType = "Lease"})";
$.getJSON(actionurl, tempdata);
}
function tempdata(response) {
if (response != null) {
var html = "";
for (var i = 0; i < response.length; i++) {
html += '<option value="' + response[i].Id + '">' + response[i].Name + '</option>';
}
$("#LeaseCompanyID").html(html);
}
}
So once the ajax call is successful it will trigger the GetNewSupplierList function which calls my controller method and returns some JSON data. Once that is returned it calls tempdata, which builds the new HTML for my select picker, once that is built it updates the html on the selectpicker id.
Works like a charm!!!
Thanks to everyone who took a look at this post.
Can't find the solution to this anywhere and I really hope it's possible.
I'm writing a Jira gadget and I have a configuration screen with 2 fields. One is the quickfind project picker; you type and it finds projects and you click the one you want.
The second field is Component. You can select the component of the project that you wish to filter by. However the components are different for each project so the Component field is populated with a AJAX call specified in the gadget in the "args" section of the "config" part.
Only problem is that this AJAX only gets called when the gadget is first loaded; i.e.: before a project is selected so the result is always "Select A Project".
I need a way to rerun this AJAX call on the event of the selected project being changed.
Is this possible? Or is there an alternative solution? I've tried timers to check for changes but there were a few problems there also; mainly that I couldn't access/alter the Component drop-box field. The gadget would just refuse to load.
Update: Below is the Javascript for the gadget. As you can see I added a refreshComponents() Javascript method which can retrieve the Components given a project ID however I have no way to attach this to the appropriate event. Also I can't seem to directly alter any components on the the page with the likes of jQuery or ordinary JS
<div id="chart_div" style="overflow: auto;"></div>
<script type="text/javascript">
var gadget = this;
google.load('visualization', '1.0', {'packages':['corechart']});
var oldProject = "initiated";
var globalGadget;
function timedComponentUpdate()
{
//alert(globalGadget.getPref("projectId"));
//setTimeout("timedComponentUpdate()",3000);
}
function refreshComponents(idString)
{
//refetch components
var url = "__ATLASSIAN_BASE_URL__/rest/severity-gadget/1.0/severity-issues/getComponents.json";
url += "?projectId=" + idString;
alert(url);
var xmlhttp;
if (window.XMLHttpRequest)
xmlhttp=new XMLHttpRequest();
else
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4)
{
//document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
alert(xmlhttp.responseText);
}
}
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function drawChart(args, bugtype, comp) {
//setup for whether were getting opened or closed
var axisTitle;
var compTitle;
var chart;
if(bugtype == "Bug")
axisTitle = "Bug";
else
axisTitle = "Issue";
if(comp == "All")
compTitle = "";
else
compTitle = " - Component: " + comp;
var wVar = gadgets.window.getViewportDimensions().width-20;
var hVar = wVar/3;
var hVar = hVar*2;
// Create the data table.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Issues');
data.addColumn('number', 'Trivial');
data.addColumn('number', 'Minor');
data.addColumn('number', 'Major');
data.addColumn('number', 'Critical');
data.addColumn('number', 'Blocker');
AJS.$(args.weeks).each(function() {
data.addRow(["Week "+this.number,
parseInt(this.issues[0]),
parseInt(this.issues[1]),
parseInt(this.issues[2]),
parseInt(this.issues[3]),
parseInt(this.issues[4])
]);
});
var options = {'title':'Weekly '+axisTitle+' Backlog' + compTitle,
'width':wVar,
'height':hVar,
axisFontSize:4,
isStacked:true,
fontName: '"Arial"'
};
chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
var gadget = AJS.Gadget(
{
baseUrl: "__ATLASSIAN_BASE_URL__",
useOauth: "/rest/gadget/1.0/currentUser",
config: {
descriptor: function(args)
{
document.getElementById("chart_div").innerHTML = "";
var gadget = this;
var projectPicker = AJS.gadget.fields.projectOrFilterPicker(gadget, "projectId", args.projectOptions);
//bh
oldProject = this.getPref("projectId");
//refreshComponents(this.getPref("projectId"));
return {
theme : function()
{
if (gadgets.window.getViewportDimensions().width < 450)
{
return "gdt top-label";
}
else
{
return "gdt";
}
}(),
fields: [
AJS.gadget.fields.nowConfigured(),
projectPicker,
AJS.gadget.fields.days(gadget, "weeksPrevious"),
{
userpref: "issueType",
label: "Issue Type:",
description:"Choose which issue type you would like",
type: "select",
selected: this.getPref("issueType"),
options:[
{
label:"Any",
value:"Any"
},
{
label:"Bug",
value:"Bug"
}
]
},
{
userpref: "component",
label: "Component:",
description:"Choose which issue type you would like",
type: "select",
selected: this.getPref("component"),
options:args.components
}
]
};
},
args: function()
{
return [
{
key: "components",
ajaxOptions: function() {
var ajaxProject = this.getPref("projectId");
if(ajaxProject == "")
ajaxProject = "null";
return {
url: "/rest/severity-gadget/1.0/severity-issues/getComponents.json",
data:
{
projectId : ajaxProject
}
}
}
}
];
}()
},
view: {
onResizeReload: true,
onResizeAdjustHeight: true,
template: function(args) {
var gadget = this;
gadget.getView().empty();
drawChart(args.issueData, this.getPref("issueType"), this.getPref("component"));
gadget.resize();
},
args: [{
key: "issueData",
ajaxOptions: function() {
return {
url: "/rest/severity-gadget/1.0/severity-issues.json",
data: {
projectId : gadgets.util.unescapeString(this.getPref("projectId")),
weeksPrevious: this.getPref("weeksPrevious"),
issueType: this.getPref("issueType"),
component: this.getPref("component"),
backlog: true
}
};
}
}]
}
}
);
</script>
I think you'll need to turn your component field into a Callback Builder.
Inside the callback function you'll need to do a few things:
retrieve the options via an AJAX request
render the dropdown
attach an event handler to refresh the list when a particular event occurs
Your new component field might look something like this... I've assumed you've got jQuery available for brevity.
{
userpref: "component",
label: "Component",
id: "component_field_id"
description: "Choose which issue type you would like",
type: "callbackBuilder",
callback: function(parentDiv){
function renderOptions(options){
// Remove elements from the parentDiv and replace them
// with new elements based on the options param
// You can use gadget.getPref('component') to ensure you
// mark the right option as selected
}
function getOptions(){
$.ajax({
url: "__ATLASSIAN_BASE_URL__/rest/severity-gadget/1.0/severity-issues/getComponents.json",
data: {
// You might be able to get hold of the selected value
// from the gadget object instead of like this
projectId: $("#filter_projectOrFilterId_id").val()
}
}).done(renderOptions);
}
// Retrieve and render options on gadget load
getOptions();
// Retrieve and render options on an event
$(parentDiv).on("update-options", getOptions);
}
}
Additionally, you'll need to trigger an event when the project select field value changes. Somewhere else in your JS code (not inside the gadget definition) you'll need to put code like this, but you'll need to confirm the CSS selector for the project/filter selector:
$(document).on("change", "#filter_projectOrFilterId_id", function(){
$("#component_field_id").trigger("update-options");
});
I've not tested this but that's how I'd attempt to achieve what you're asking for.
I have a YUI dialog that submits a form to a Java servlet. The servlet returns html and javascript. I take the response and put it into a div on the page and then eval the javascript that is within the div.
My problem is that I get an error in the firebug console saying "YAHOO is not defined" as soon as the servlet returns.
I do not include the YUI js files in the servlet as I didn't think I would need them, I would expect the files included in the head of the main page would be sufficient.
If I remove all references to YUI from the javascript returned by my servlet then everything works well.
What should I do to stop getting this error as soon as my servlet returns?
My Servlet returns something along the lines of:
<div id="features">some html to display</div>
<script id="ipadJS" type='text/javascript'>
var editButton1 = new YAHOO.widget.Button('editButton1', { onclick: { fn: editButtonClick, obj: {id: '469155', name : 'name 1'} } });
var editButton2 = new YAHOO.widget.Button('editButton2', { onclick: { fn: editButtonClick, obj: {id: '84889', name : 'name 2'} } });
</script>
Here is the code that I used to create the dialog, i use the handleSuccess function to put my response from my servlet into the page (note that even though im not actively putting the javascript into the page it still throws the 'YAHOO not defined' error.):
YAHOO.namespace("ipad");
YAHOO.util.Event.onDOMReady(function () {
// Remove progressively enhanced content class, just before creating the module
YAHOO.util.Dom.removeClass("createNewFeature", "yui-pe-content");
// Define various event handlers for Dialog
var handleSubmit = function() {
this.submit();
};
var handleCancel = function() {
this.cancel();
};
var handleSuccess = function(o) {
var response = o.responseText;
var div = YAHOO.util.Dom.get('features');
div.innerHTML = response;
};
var handleFailure = function(o) {
alert("Submission failed: " + o.status);
};
// Instantiate the Dialog
YAHOO.ipad.createNewFeature = new YAHOO.widget.Dialog("createNewFeature",
{ width : "450px",
fixedcenter : true,
visible : false,
constraintoviewport : true,
buttons : [ { text:"Submit", handler:handleSubmit, isDefault:true },
{ text:"Cancel", handler:handleCancel } ]
});
// Validate the entries in the form to require that both first and last name are entered
YAHOO.ipad.createNewFeature.validate = function() {
var data = this.getData();
return true;
};
YAHOO.ipad.createNewFeature.callback = { success: handleSuccess,
failure: handleFailure,
upload: handleSuccess };
// Render the Dialog
YAHOO.ipad.createNewFeature.render();
var createNewFeatureShowButton = new YAHOO.widget.Button('createNewFeatureShow');
YAHOO.util.Event.addListener("createNewFeatureShow", "click", YAHOO.ipad.clearFeatureValues, YAHOO.ipad.clearFeatureValues, true);
var manager = new YAHOO.widget.OverlayManager();
manager.register(YAHOO.ipad.createNewFeature);
});
I don't know your use case exactly, but if you just need to create some buttons on the fly based on server response, than it would IMO be better to return JSON or XML data with the variable data and then create the buttons. Something like
var reply = eval('(' + o.responseText + ')');
var editButton1 = new YAHOO.widget.Button('editButton1',
{ onclick: { fn: editButtonClick,
obj: {id: reply[id], name : reply[name]}
} })
And if you really want to append a script node, then the following approach should work:
var response = o.responseText;
var snode = document.createElement("script");
snode.innerHTML = response;
document.body.appendChild(snode);