Cannot get deactivate function to fire in durandal with deeplinking - durandal

I am new to durandal and single page apps, I am having issues getting the deactivate and canDeactivate function to fire. I am using some custom code to achieve deep linking, which is probably what is causing my issue.
I followed the example here: https://github.com/evanlarsen/DurandalDeepLinkingExample
please also see Durandal Subrouting (Hottowel)
Any help would be most appreciated.
Here is the viewmodel code I am calling:
define([''], function () {
var vm = {
activate: function () {
alert('In activate!');
},
deactivate: function () {
alert('In deactivate!');
},
canDeactivate: function () {
alert('In candeactivate!');
}
}
return vm;
});
Here is the viewhtml
<div class="container-fixed">
<div>
<header>
<!-- ko compose: {view: 'Main/Users/UsersNav'} -->
<!-- /ko-->
</header>
<section id="content" class="in-users">
<!--ko compose: {
model: inUsers, afterCompose: router.afterCompose,
transition: 'entrance',
activate: true
} -->
<!--/ko-->
</section>
</div>
</div>
Here is the calling code:
define(['durandal/system', 'durandal/viewModel', 'durandal/plugins/router'],
function (system, viewModel, router) {
var App = {
router: router,
activate: activate,
showPage: showPage,
isPageActive: isPageActive,
inUsers: viewModel.activator(),
};
return App;
var defaultPage = '';
function activate(activationData) {
defaultPage = 'ManageUsers';
App.inUsers(convertSplatToModuleId(activationData.splat));
router.activeItem.settings.areSameItem = function (currentItem, newItem, data) {
if (currentItem != newItem) {
return false;
}
else {
App.inUsers(convertSplatToModuleId(data.splat));
return true;
}
};
}
function showPage(name) {
return function () {
router.activate('#/Users/' + name);
//router.navigateTo('#/Users/' + name);
App.inUsers(convertNameToModuleId(name));
};
}
function isPageActive(name) {
var moduleName = convertNameToModuleId(name);
return ko.computed(function () {
return App.inUsers() === moduleName;
});
}
// Internal methods
function convertNameToModuleId(name) {
return 'Main/Users/' + name + '/' + name;
}
function convertSplatToModuleId(splat) {
if (splat && splat.length > 0) {
return convertNameToModuleId(splat[0]);
}
return convertNameToModuleId(defaultPage);
}
});
EDIT: (Main master page)
function activate() {
// my convention
router.autoConvertRouteToModuleId = function (url) {
return 'Main/' + url + '/index';
};
return router.activate('Home');
}
Nav HTML for master:
<div class="btn-group">
HOME
RESOURCES
USERS
</div>
Main master:
<div class="container-fixed">
<div>
<header>
<!-- ko compose: {view: 'Main/masterNav'} -->
<!-- /ko-->
</header>
<section id="content" class="main">
<!--ko compose: {model: router.activeItem,
afterCompose: router.afterCompose,
transition: 'entrance'} -->
<!--/ko-->
</section>
<footer>
<!--ko compose: {view: 'Main/masterFooter'} --><!--/ko-->
</footer>
</div>
</div>

The issue you are running into about not being able to deactivate your sub-routed views is because the viewmodel.activator() observable, that is returned from that method, enforces the activator pattern in durandal. That observable is expecting a amd module and not a string.
Even though the string works fine because the compose binding knows how to load modules based off of the string.. the viewmodel activator doesn't know how to load modules from strings.
So, you need to pass the actually module to the observable.
The example I created before just used a string so it will compose the view.. but the activator pattern doesnt work if there is just a string. So, instead you will have to require all your sub-route amd modules into your calling code and then instead of using the
convertSplatToModuleId method.. create a new method that returns the correct module.
So, something like this:
define(['durandal/system', 'durandal/viewModel', 'durandal/plugins/router'],
function (system, viewModel, router) {
var App = {
router: router,
activate: activate,
showPage: showPage,
isPageActive: isPageActive,
inUsers: viewModel.activator(),
};
return App;
var defaultPage = '';
function activate(activationData) {
defaultPage = 'ManageUsers';
convertSplatToModuleId(activationData.splat).then(function(activeModule){
App.inUsers(activeModule);
})
router.activeItem.settings.areSameItem = function (currentItem, newItem, data) {
if (currentItem != newItem) {
return false;
}
else {
convertSplatToModuleId(data.splat).then(function (module) {
App.inUsers(module);
});
return true;
}
};
}
function showPage(name) {
return function () {
router.activate('#/Users/' + name);
//router.navigateTo('#/Users/' + name);
convertNameToModuleId(name).then(function(module){
App.inUsers(module);
});
};
}
// Internal methods
function convertNameToModuleId(name) {
return system.acquire('Main/Users/' + name + '/' + name);
}
function convertSplatToModuleId(splat) {
if (splat && splat.length > 0) {
return convertNameToModuleId(splat[0]);
}
return convertNameToModuleId(defaultPage);
}
});

Related

Vue 3 reusable error handling and handleSubmit in reusable 'useForm' function using the composition api

In a recent web app we have a lot of forms with the same submit structure:
Disable the form and submit button based on an isSubmitting variable
Validate the input fields (we're using Yup)
If validation fails: Set isSubmitting back to false + set and show validationErrors on the input fields
If validation succeed: Send post request with form data to api
Show general error if api is down or returns an error
I've tried to something using the composition api in vue 3.
Login.vue
<template>
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<h1 class="text-3xl text-center text-gray-900">{{ t('sign_in_account', 1) }}</h1>
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<form #submit.prevent="handleSubmit">
<fieldset :disabled="isSubmitting" class="space-y-6">
<MessageBox v-if="errors.general" :title="errors.general" :messages="errors.messages" />
<Input :label="t('email', 1)" type="text" id="email" v-model="user.email" :error="errors.email" />
<Password :label="t('password', 1)" type="password" id="password" v-model="user.password" :error="errors.password" />
<div class="text-sm text-right">
<router-link class="font-medium text-indigo-600 hover:text-indigo-500" :to="forgotPassword">{{ t('forgot_password', 1) }}</router-link>
</div>
<SubmitButton class="w-full" :label="t('sign_in', 1)" :submittingLabel="t('sign_in_loader', 1)" :isSubmitting="isSubmitting" />
</fieldset>
</form>
</div>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
import { useStore } from 'vuex';
import { useI18n } from 'vue-i18n';
import useForm from '#/use/useForm';
import { validateEmail, LoginValidationSchema } from '#/utils/validators';
export default {
setup() {
const store = useStore();
const { t } = useI18n({ useScope: 'global' });
const user = ref({
email: '',
password: '',
});
const { handleSubmit, isSubmitting, errors } = useForm(user, LoginValidationSchema, handleLogin);
async function handleLogin(values) {
try {
return await store.dispatch('auth/login', values);
} catch (error) {
if (error.response) {
console.log(error.reponse);
if (error.response.status == 422) {
errors.value = {
general: `${t('unable_to_login', 1)}<br /> ${t('fix_and_retry', 1)}`,
messages: Object.values(error.response.data.errors).flat(),
};
} else if (error.response.data.message) {
errors.value = {
general: error.response.data.message,
};
} else {
errors.value = {
general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
};
}
} else if (error.request) {
console.log(error.request);
errors.value = {
general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
};
} else {
errors.value = {
general: `${t('unknown_error', 1)}<br /> ${t('please_try_agin', 1)}`,
};
}
return;
}
}
return { t, user, handleSubmit, isSubmitting, errors };
},
computed: {
forgotPassword() {
return validateEmail(this.user.email) ? { name: 'forgotPassword', query: { email: this.user.email } } : { name: 'forgotPassword' };
},
},
};
</script>
useForm.js
import { ref, watch } from 'vue';
export default function useForm(initialValues, validationSchema, callback) {
let values = ref(initialValues);
let isSubmitting = ref(false);
let errors = ref({});
async function handleSubmit() {
try {
errors.value = {};
await validationSchema.validate(values.value, { abortEarly: false });
isSubmitting.value = true;
} catch (err) {
console.log('In the catch');
isSubmitting.value = false;
err.inner.forEach((error) => {
errors.value = { ...errors.value, [error.path]: error.message };
});
}
}
watch(isSubmitting, () => {
if (Object.keys(errors.value).length === 0 && isSubmitting.value) {
callback(values);
isSubmitting.value = false;
} else {
isSubmitting.value = false;
}
});
return { handleSubmit, isSubmitting, errors };
}
This is somehow working but I'm missing two things. In useForm I want to wait till the callback is done (succeed or failed) to set isSubmitting back to false. Is a promise a good way to do this of is there a better way? Secondly I want a reusable way to handle the errors in Login.vue. Any suggestion how to handle this?
Regarding your first question - try..catch statements have a third statement called finally which always executes after the try statement block has completed.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
To answer your second question - promises are a great way of handling async logic, including your case when the API you're sending the request to returns an error response, and you can then decide how you're going to handle the UX in such scenario.
I'm not quite clear on what you mean by handle the errors in Login.vue in a reusable way, but perhaps you could simple pass in an empty array prop to your useForm called formErrors and have your useForm.js emit an update:modelValue event to get two way binding.

Vue watch value from parent is in reverse

I have a custom component that lets users type in text and sends it to the backend where I do some computation and spit the new text back out with html in it.
My problem is when the user types into this textarea, it reverses all the text and keeps the cursors at the beginning of the textarea. So now 'foo bar' becomes 'rab oof'... This only has happened since I added in watch. I could delete the watcher, but I need it (or need another way) to apply my updates to the textarea, via the foo variable when I set foo equal to something from the parent.
console.log(v) writes out the reverse text.
Any idea how to change this?
Custom componet:
<template>
<div contenteditable="true" #input="updateHTML" class="textareaRoot"></div>
</template>
<script>
export default {
name: 'htmlTextArea',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
this.$el.innerHTML = v; //v is the reverse text.
}
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
}
</script>
Parent that uses custom component:
<htmlTextArea id="textarea" v-model="foo"></htmlTextArea>
...
<script>
...
methods: {
triggerOnClick() {
this.foo = 'something';//Without the watcher, when I change this.foo to something the actual textarea does not display the new data that I assigned to foo. But in Vue dev tools I can see the new change.
}
UPDATE:
Vue.component('html-textarea',{
template:'<div contenteditable="true" #input="updateHTML"></div>',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
this.$el.innerHTML = v;
}
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
});
new Vue({
el: '#app',
data () {
return {
foo: '',
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="app">Type here:
<html-textarea spellcheck="false" id="textarea" v-model="foo"> </html-textarea>
</div>
The problem is that when you set the innerHTML of a contenteditable element, you lose the selection (cursor position).
So you should perform the following steps when setting:
save the current cursor position;
set the innerHTML;
restore the cursor position.
Saving and restoring is the tricky part. Luckily I got these two handy functions that do the job for latest IE and newer. See below.
function saveSelection(containerEl) {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(containerEl);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
}
function restoreSelection(containerEl, savedSel) {
var charIndex = 0, range = document.createRange();
range.setStart(containerEl, 0);
range.collapse(true);
var nodeStack = [containerEl],
node, foundStart = false,
stop = false;
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
Vue.component('htmltextarea', {
template: '#hta',
name: 'htmlTextArea',
props:['value'],
mounted: function () {
this.$el.innerHTML = this.value;
},
watch: {
value(v) {
if (v === 'yes') {
let selection = saveSelection(this.$el);
this.$el.innerHTML = 'no!';
this.$emit('input', 'no!');
restoreSelection(this.$el, selection);
}
}
},
methods: {
updateHTML: function(e) {
this.$emit('input', e.target.innerHTML);
}
}
});
new Vue({
el: '#app',
data: {
foo: 'Clear this and type "yes" (without the quotes). It should become "no!".'
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<htmltextarea id="textarea" v-model="foo"></htmltextarea>
<hr>
Result: <pre>{{ foo }}</pre>
</div>
<template id="hta">
<div contenteditable="true" #input="updateHTML" class="textareaRoot"></div>
</template>
In your app, I recommend you place them in a dedicated .js file, just for better organization.

Angular2 Service which create, show and manage it's inner Component? How to implement js alert()?

I tried to find a way for having and manage an angular2 Component in a Service but with no success:
I need to create:
AlertService{
alertConfirm(msg): Promise;
}
alertConfirm will prompt an Confirmation window with 2 buttons (Ok, Cancel) and will return users' choise as a Promise.
In General, the idea is to implement the famous JavaScript alert() method
but with a designed UI window and with also a cancel button.
The method will return a Promise with a response of user's choice: "OK" or "Cancel".
I tried to find a way for holding an "anonymous" component, AlertComponent, in AlertService:
AlertComponent{
showMsgConfirm(msg): Promise;
}
The Promise will be set with a response when user close prompt window or click "OK" or "Cancel".
The question:
How to make "AlertService" to have an inner "AlertComponent" which can be managed by it's "alertOK" method?
I mean, I didn't find a way for "alertConfirm" to call "showMsgConfirm" method and to return it's Promise as a response.
for example, calling from main app component:
this.alertService.alertConfirm("Save changes?").then(res => {
if(res.ok){console.log("Can be saved");
}, err=> { });
Any ideas for this?
Thanks,
Update:2 different ideas for solution, but with no sucess to manage the AlertComponent:
import { Injectable, ViewContainerRef, ReflectiveInjector, ComponentFactoryResolver, ComponentRef } from '#angular/core';
import { AlertComponent } from './../components/modales/AlertComponent/AlertComponent.component';
#Injectable()
export class AlertService {
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
public createAlertComp(vCref: ViewContainerRef): ComponentRef<any> {
let factory = this.componentFactoryResolver.resolveComponentFactory(AlertComponent);
/*
//Option 1:
// vCref is needed cause of that injector..
let injector = ReflectiveInjector.fromResolvedProviders([], vCref.parentInjector);
// create component without adding it directly to the DOM
let comp = factory.create(injector);
// add inputs first !! otherwise component/template crashes ..
comp.instance.model = modelInput;
// all inputs set? add it to the DOM ..
vCref.insert(comp.hostView);
return comp;
*/
//Option 2:
var componentRef: ComponentRef<AlertComponent> = vCref.createComponent(factory);
return null;
}
}
And the answer is... :
The Service:
_counter is used for each modal to have a unique name.
comp.instance.close is a property of inner component for subscribing for EventEmitter.
.
import { Injectable, ViewContainerRef, ReflectiveInjector, ComponentFactoryResolver, ComponentRef, EventEmitter } from '#angular/core';
import { CtmAlertComponent } from './ctmAlert/ctmAlert.component';
#Injectable()
export class AlertCtmService {
private _vcr: ViewContainerRef;
private _counter: number = 0;
constructor(private componentFactoryResolver: ComponentFactoryResolver, public viewRef: ViewContainerRef) {
console.log("AlertCtmService.constructor:");
//TODO: Consider appending to this.viewRef: "#alertCtmServiceContainer" as a Dom elemnt perent container which will hold all AlertModals:
// Maybe by:
// this.viewRef.element.nativeElement.insertAdjacentHTML('beforeend', '<div class="alertCtmServiceContainer"></div>');
this._vcr = this.viewRef;
}
public alertOK(alertMsg: string): EventEmitter<any> {
return this.createEventEmitterComponent("CtmAlertComponent", alertMsg, false);
}
public alertConfirm(alertMsg: string): EventEmitter<any> {
return this.createEventEmitterComponent("CtmAlertComponent", alertMsg, true);
}
private createEventEmitterComponent(componentName: string, alertMsg: string, isConfirm: boolean): EventEmitter<any> {
console.log("AlertCtmService.createEventEmitterComponent:");
switch (componentName) {
case "CtmAlertComponent":
default:
var _component = CtmAlertComponent;
break;
}
let factory = this.componentFactoryResolver.resolveComponentFactory(_component);
// vCref is needed cause of that injector..
let injector = ReflectiveInjector.fromResolvedProviders([], this._vcr.parentInjector);
// create component without adding it directly to the DOM
let comp = factory.create(injector);
// add inputs first !! otherwise component/template crashes ..
comp.instance.close.subscribe(resp => {
console.log("AlertCtmService.createEventEmitterComponent: comp.instance.close.subscribe: resp=" + resp.ok);
comp.destroy();
})
comp.instance.alertBodyMsg = alertMsg;
comp.instance.isConfirm = isConfirm;
comp.instance.nameId = "Modal" +(++this._counter).toString();
// all inputs set? add it to the DOM ..
this._vcr.insert(comp.hostView);
//return null;
return comp.instance.close;
}
public init(vCref: ViewContainerRef): ViewContainerRef {
this._vcr = vCref;
return this._vcr;
}
}
Inner Component:
Using Bootstrap for handling display of Modal in UI: modal('show') \ modal('hide').
.
import { Component, AfterViewInit, Input, ViewChild, ElementRef, Renderer, NgZone, EventEmitter} from '#angular/core';
#Component({
selector: 'ctm-alert',
styles: [``],
templateUrl: '/app/shared/alertCtm/ctmAlert/CtmAlert.component.html',
styleUrls: ['./app/shared/alertCtm/ctmAlert/CtmAlert.component.css'],
providers: []
})
export class CtmAlertComponent implements AfterViewInit {
public ModalIsVisible: boolean;
//private static subscriptions: Object = {};
//enums = Enums;
close = new EventEmitter();
public nameId = "";
private isOk = false;
alertBodyMsg: string = "";
isConfirm = false;
constructor() {
console.log("CtmAlertComponent.constructor:");
}
ngAfterViewInit() {
this.showModal();
var attrId = this.getIdAttr();
$('#' + attrId).on('hidden.bs.modal', function () {
debugger;
console.log('CtmAlertComponent: #licenseModal_XXX.on(hidden.bs.modal)');
this.submitStatus();
}.bind(this) );
}
showModal() {
this.ModalIsVisible = true;
var attrId = '#' +this.getIdAttr();
$(attrId).modal('show');
}
hideModal() {
this.ModalIsVisible = false;
var attrId = '#' + this.getIdAttr();
$(attrId).modal('hide');
}
getIdAttr(): string {
return "ctmAlertModal_" + this.nameId;
}
submitStatus() {
var resp = { ok: (this.isOk == true) };
this.close.emit(resp);
}
submitOk() {
this.isOk = true;
this.hideModal();
}
submitCancel() {
this.isOk = false;
this.hideModal();
}
}
App's Declaration:
unfortunately, we must declare the anonymus component in our main-app module.
We must add a declaration of entryComponents: [CtmAlertComponent],
.
import { CtmAlertComponent } from './shared/alertCtm/ctmAlert/ctmAlert.component';
#NgModule({
imports: [
BrowserModule,
HttpModule,
AppRoutingModule,
...
],
declarations: [
CtmAlertComponent,
AppComponent,
...
],
entryComponents: [CtmAlertComponent],
providers: [
...
],
bootstrap: [AppComponent],
})
export class AppModule { }
enableProdMode();
Modal UI:
this html template is based on bootstrap's UI:
.
<div class="ctmAlertModal modal fade in" [id]="getIdAttr()" role="dialog">
<div class="modal-dialog modal-lg" [ngClass]="{'modal-lg-6': true }">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header" style="">
<div class="pull-right" style="position: relative;">
<span class="fa fa-times-circle" aria-hidden="true" style="color: #949494"></span>
</div>
</div>
<div class="modal-body">
<div class="modal-body-msg">
{{alertBodyMsg}}
</div>
<div class="modal-body-buttons">
<div style="margin: 0 auto;" [style.width]="(isConfirm)? '165px' : '70px' ">
<button type="button" *ngIf="isConfirm" class="btn-submit pull-left btn-cancel" [ngClass]="{'disabled': false }" [disabled]="false" (click)="submitCancel()">
<!--<img alt="End-Training" class="centering-me2" src="../../../contents/training_state_stop_white.svg">-->
Cancel
</button>
<button type="button" class="btn-submit pull-right" [ngClass]="{'disabled': false }" [disabled]="false" (click)="submitOk()">
<!--<img alt="Resume-Training" src="../../../contents/training_state_play_white.svg">-->
OK
</button>
</div>
</div>
</div>
</div>
</div>
</div>
.
Usage::
for example:
.
this.alertCtmService.alertOK("Save changes???").subscribe(function (resp) {
console.log("alertCtmService.alertOK.subscribe: resp=" + resp.ok);
this.saveData();
}.bind(this) );
**
An example I built : https://plnkr.co/qc1ZM6
**
sources:
building-angular-2-components-on-the-fly-a-dialog-box-example
angular2-ngmodule

sharepoint 2013 _api/$metadata error 'A nonnullable DataProperty cannot have a null defaultValue. Name: undefined'

I try to use sharepoint 2013 rest api with breezejs.
I get error
A nonnullable DataProperty cannot have a null defaultValue. Name: undefined
when breezejs validates returned metadata (_api/$metadata).
With breeze.EntityManager("/_vti_bin/listdata.svc/") all works.
What are the ways to fix it?
<script src="/_layouts/15/SharePointProject7/scripts/angular.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/q.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/datajs-1.1.1.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/breeze.min.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/breeze.toq.js"></script>
<script src="/_layouts/15/SharePointProject7/scripts/app.js"></script>
<div data-ng-app="app">
<div data-ng-controller="Ctrl2">
<ul>
<li data-ng-repeat="c in customers"><input type="text" data-ng-model="c.Title" /></li>
</ul>
<button type="button" data-ng-click="save()">Save</button>
</div>
</div>
var app = angular.module('app', []);
app.run(['$q', '$rootScope', function ($q, $rootScope) {
breeze.core.extendQ($rootScope, $q);
}]);
app.service('listData', function () {
breeze.config.initializeAdapterInstances({ dataService: "OData" });
var manager = new breeze.EntityManager("/_api/");
var changes;
this.getItems = function () {
var query = breeze.EntityQuery.from("Lists");
return manager.executeQuery(query).to$q();
};
this.saveItems = function () {
if (manager.hasChanges()) {
changes = manager.getChanges();
manager.saveChanges().to$q(saveSucceeded, saveFailed);
}
else {
alert("Nothing to save");
};
};
function saveSucceeded() {
alert("OK");
};
function saveFailed(error) {
alert(error.message);
};
});
app.controller('Ctrl2', function ($scope, listData) {
function initialize() {
listData.getItems().then(querySucceeded, _queryFailed);
};
function querySucceeded(data) {
$scope.customers = data.results;
}
function _queryFailed(error) {
alert(error.message);
}
$scope.save = function () {
listData.saveItems();
};
initialize();
});
It is better to hand write the entity configuration as the SP metadata can be huge and it is not a good idea to pull it down at the beginning of the app load.
First get the SharePoint breeze adapter from breeze labs.
Full details here http://www.andrewconnell.com/blog/getting-breezejs-to-work-with-the-sharepoint-2013-rest-api
That post goes step by step on getting this working.

Durandal 2.0: transition not triggered while staying on same view with new data

Here's my shell:
<div>
<div id="content" class="container-fluid page-host main">
<!--ko router: { transition:'entrance', alwaysTriggerAttach: 'true', cacheViews: 'true' }--><!--/ko-->
</div>
</div>
I always navigate to views using:
router.navigate(route);
Now, when being on a certain view (item), and calling the navigate function for another id (/#item/id), the data changes correctly, but the transition does not happen.
Any advice?
Thanks
Nicolas
Should transition be triggered is defined in this function of durandal/js/composition.js:
function shouldTransition(context) {
if (system.isString(context.transition)) {
if (context.activeView) {
if (context.activeView == context.child) {
return false;
}
if (!context.child) {
return true;
}
if (context.skipTransitionOnSameViewId) {
var currentViewId = context.activeView.getAttribute('data-view');
var newViewId = context.child.getAttribute('data-view');
return currentViewId != newViewId;
}
}
return true;
}
return false;
If you want to trigger transition then navigating within the same view, you can comment this if statement:
if (context.activeView == context.child) {
return false;