Add target blank to a link in a props in Vue.js - vue.js

I use the ReadMore plugin to crop articles in a page. The plugin provides a props to redirect to a http link when the "read more" is clicked. But I need to display the link in a new tab. The props receives the link in a string.
I tried several ways to add the target blank attribute to this string before passing it to the props. With no success. Like:
http://www.example.com/page-to-see.html target=_"blank"
I used it with or without quotes but in any case, the link works but the attribute is skipped.
Is there a way to intercept this and add a target blank later?
I saw in other questions the use of router-link but I don't know how to manipulate the props content in the first place.
Any clue would be warmly welcomed
Edit: adding more code to give a clearer explanation of the problem I try to solve:
In the template:
<read-more more-str="read more" :text="dwId.texte" :link="dwId.link" less-str="less" :max-chars="540"></read-more>
I get the values from a DB with Axios. The props are specified by the plugin documentation.
The :link must be a string and it's what it gets from the DB. It works. But as I explained, I need to open in a new tab.
Edit: what I tried:
I try to make a computed property that would add the target blank to a string and use it in the read-more props:
computed: {
target: function() {
return this.dwIds.filter((dwId) => {
return dwId.link + target="_blank"
});
},
}
I have two issues here: first , the result is an object and the props requires a string. Furthermore, the way I add the target blank is not correct but I can't find the right syntax yet.

You need to use it as a directive, and override parts of the initial element you're passing. Otherwise there is no way to "intercept" it. Here's the code to the "component" version that won't do the trick for you.

Related

Initially hide first group in Vue-Formulate repeatable group

I'm using Vue-Formulate's Repeatable Groups. For my requirements:
A group is optional to add
If a button is pressed to add a group, then the fields in the group must be validated
The form should not initially show the group; it should show the button to add a group
For example, the desired initial appearance is in the following screenshot, which I generated after clicking the "remove" / X button in the linked codesandbox:
I've mocked this up at codesandbox here: Vue Formulate Group with Button to Start
Is this possible? If so, how?
May 2021 UPDATED WORKAROUND
In #braid/vue-formulate#2.5.2, the workaround below (in Research: A hack that seems to UPDATE: USED TO work) no longer works, using a slot to override the close button, save a ref, and trigger a click does. See also the related feature request at https://github.com/wearebraid/vue-formulate/issues/425.
<script>
export default {
// ... fluff omitted
async mounted() {
await this.$nextTick();
if (!this.hasMessages) {
// See also feature request at https://github.com/wearebraid/vue-formulate/issues/425
this.$refs.closeButton.click();
}
},
};
</script>
<template>
<FormulateInput
type="group"
name="rangeMessages"
:minimum="0"
repeatable>
<!-- See https://vueformulate.com/guide/inputs/types/group/#slots -->
<template #remove="{removeItem}">
<button ref="closeButton" #click.prevent="removeItem"/>
</template>
</FormulateInput>
</template>
Research - Vue-Formulate's Docs
In Vue-Formulate's docs on input with type="group"'s props and slots, there is a minimum prop. I've set that to zero, but that doesn't change the initial appearance. I do see multiple slots, but I'm not quite sure which one to use or if I could use any of them to achieve what I want; it seems like default, grouping, and repeatable might be useful in preventing the initial display of the first group.
Research - Vue-Formulate's Tests
I see that FormulateInputGroup.test.js tests that it('repeats the default slot when adding more', so the default slot is the content that gets repeated. According to the docs, the default slot also receives the index as a slot prop, so that could be useful.
Research - Vue Debugger
The item which I want to initially remove is at FormulateInputGroup > FormulateGrouping > FormulateRepeatableProvider > FormulateRepeatable > FormulateInput:
When I remove the initial item to match the desired initial layout, the group hierarchy changes to:
<FormulateInput><!-- the input type="group" -->
<FormulateInputGroup>
<FormulateGrouping/>
<FormulateAddMore>...</FormulateAddMore>
</FormulateInputGroup>
</FormulateInput>
Based on this change, I would expect that I need to modify FormulateGrouping to get the desired initial appearance, but I haven't found in the source what items are available to me there.
Research: A hack that seems to UPDATE: USED TO work
This hack worked in v2.4.5, but as of 2.5.2, it no longer works. See top of post for an updated workaround.
In the mounted hook, when I first render the form, I can introspect
into the formValues passed to v-model to see if the group lacks an
initial elements that is filled out. If so, then I can make use of a
ref msgs on the FormulateInput of type group to then call
this.$refs.msgs.$children[0].$children[0].removeItem(), which
triggers an initial remove of the (empty) item. This is super hacky,
so I'd prefer a better way, but it kind of works. The only problem is
that it validates the fields when clicking on the button, before any
input has been entered.
This is a fair question, and Vue Formulate used to support the behavior of just using an empty array to begin with, however it became clear that it was confusing to users that their fields would not show up without an empty object [{}] when they bound the model, so a change was made to consider an initial value of an empty array an "empty" field and pre-hydrate it with a value. Once that initial hydration is completed however, you can easily re-assign it back to an empty array and you're good to go. This is easily done in the mounted lifecycle hook:
...
async mounted () {
await this.$nextTick()
this.formData.groupData = []
}
...
Here's a fork of your code sandbox: https://codesandbox.io/s/vue-formulate-group-with-button-to-start-forked-32jly?file=/src/components/Reproduction.vue
Provided solutions weren't working for me but thanks to previous answer I've managed to find this one:
mounted(){
Vue.set(this.formData, "groupData", [])
},
which does same effect as
data(){
formData: {
groupData: [],
},
},
mounted(){
this.formData.groupData = []
},

Nuxt.js ignore (Is there any way to ignore original tags)

I am creating a web page using Nuxt.js. (Static file built with the
nuxt generate command)
As a requirement,I need to write the original tag in the static file
that I built. The original tag is for displaying the data obtained
on the server side. Writing an original tag in a vue file naturally
gives an error.
For example, in the image below,
Is there a way to ignore original tags in the vue file?
vue file(example)
// disabled-next-line
{{^ server-side-display-data}}
<button> button </ button>
// disabled-next-line
{{/ server-side-display-data}}
Build file(example)
{{^ server-side-display-data}}
<button> button </ button>
{{/ server-side-display-data}}
Tried
It seemed possible to ignore certain "files" like below, but I haven't found a way to ignore certain "tags"
https://ja.nuxtjs.org/api/configuration-ignore/
Please help
I had the same issue and was looking for a solution... you can try with this solution i've used.
in your nuxt.config.js
...
vue: {
config: {
ignoredElements: [string or regexp],
},
},
...
see this documentation for more details

vue router: go up one (variable) level

seems nobody had this question before...? (or i just don't know the right words for it :D )
i want to have a very basic heading on every page of my app, which allows the user to go "up". simple as that.
example: user is on page.com/users/browse/, clicks on the heading, gets navigated to page.com/users/, clicks again, gets navigated to page.com. nothing more. it just has to be variable, so i can put it in a component and have it just work everywhere
how can i do that? i tried vue.$route, but there is only a string of the complete url. is there any convenient way to do it? or do i HAVE to split the url by / and build the route myself?
thanks :)
Try using this: this.$router.replace("./");
I don't know how it is working though. The single dot(./) will go up one path and double dots(../) will go up 2 paths!
This functionality has a name "Breadcrumbs".
Check this: 1, 2, 3 or look for this in your framework.
Try adding a method for going up one level by getting the route's path and then splitting it. Here's what worked for me:
upOneLevel(){
var upperPath = this.$route.path.split("/")
if(upperPath.length > 0) {
upperPath.splice(upperPath.length-1)
this.$router.push(upperPath.join("/"))
}
}
Then you can add a button that calls this method to go up by one level but no further than the root. This method will work regardless of whether or not your routes were nested properly.
this.$router.replace("./") will help if the user clicks on the heading only once, if he try to clicks again nothing is happen because vue-router avoid redundant navigation to the same location.
You can catch this error then navigate to the root, (or to a named route) if you have only two levels in your routes.
NOTE :
This function works only for two levels (see the example of this Question)
goUp() {
this.$router.replace("./").then(err =>{
if(err)
this.$router.replace("/");
// or : this.$router.replace({name:"home"}); for named routes
})
}
You can get the current path using this.$route.path and than remove the last party by splitting/splicing it:
goUp(){
var upperPath = this.$route.path.split("/")
if (upperPath.length > 2) {
upperPath.splice(upperPath.length - 1)
this.$router.push(upperPath.join("/"))
} else {
this.$router.push('/');
}
}
The already mentioned this.$router.replace("./"); will add a trailing slash.
If this is not your intention, use this.$router.replace(".");
Moreover, if you want to make a new history event, use this.$router.push(".");

Aurelia: Trigger update on one custom element when a value in another custom element changes?

I've just recently asked a question ( Refreshing i18n translated string interpolated values in Aurelia ) regarding how to refresh i18n string interpolated values when a select input field (with language ids) changes. I received a great answer, however now I realized that there was another requirement.
It's not only string interpolated values that needs to be refreshed.
In my page-specific templates I have some select fields (custom elements), which gets their option values from injecting a "service" class. That service is responsible for doing the http fetch request, and returning a promise to the select field (custom element).
Now here's the problem. When the global (language) select field from my site-wide nav-bar custom element changes, and i18n refreshing happens using the notification approach proposed in the link above. How would I then also re-fetch my different select input custom elements inside the template, but with the new language id from the language select in the nav-bar?
The only solution I know so far is to do window.location (router.navigate(sameroute) didn't trigger a refresh) and refresh the current page whenever the language select changes, however that's obviously not a great way to handle this.
I'll try to see if I can put together a plunkr, since all this may sound a little confusing.
#chrismbeckett from in https://gitter.im/Aurelia/Discuss gave me this hint:
"For those types of inter-component syncs, use the EventAggregator.
Pub a 'lang-changed' event and let any component do what it needs to
update itself"
So in nav-bar.js i did this:
let payload = { 'lngId': this.appLngId};
this.eventAggregator.publish('lang_changed', payload);
and in the custom element which were to be refreshed I put this inside the attached() function:
this.eventAggregator.subscribe('lang_changed', payload => {
alert(payload.lngId)
self.myTodosService.getMyTodos(payload.lngId);
.then(function(data){
self.myTodos = data;
})
});

Ng-grid with external data and TypeScript: compile error "Cannot set property 'gridDim' of undefined"

Update #1: after the fix I commented about, now my app starts but the grid is not rendered except for its bounding box and filter button and popup. Yet, I get no error from the console, and as far as I can arrive with the debugger, I can see that data got from the server are OK. If I use Batarang, I can see the scope corresponding to my model, correctly filled with items. I updated the downloadable repro solution accordingly. Could anyone explain why ng-grid is not updating here?
I'm starting to play with ng-grid and TypeScript and I'm finding issues as soon as my test app starts up. See the bottom of this post for a link to a full test solution. Surely I have made tons of errors even in these few files, but I'd like to have something to start with and learn more step by step.
The MVC app has two client-side applications:
app.js for the default view (Home/Index). No typescript here, and the whole code is self-contained in this single file. The code is derived from the paging example in the ng-grid documentation and tries to stay as simplest as possible.
MyApp.js for the more realistic sample in another view (Home/Model). This sample uses services, models and controllers and its JS code is compiled from TypeScript. To keep things simple, I'm just storing these components under Scripts/App, in folders for Controllers, Models and Services, and each file contains just a single class or interface. The generated JS files are manually included in the view.
app.js works, except that it has issues with filtering. I posted about these here:
Server-side filtering with ng-grid: binding issue?
MyApp.js has startup issues with ng-grid. As soon as the app starts, a TypeError is thrown in the grid binding:
TypeError: Cannot set property 'gridDim' of undefined
at ngGridDirectives.directive.ngGridDirective.compile.pre (http://localhost:55203/Scripts/ng-grid-2.0.7.js:2708:37)
at nodeLinkFn (http://localhost:55203/Scripts/angular.js:4392:13)
at compositeLinkFn (http://localhost:55203/Scripts/angular.js:4015:15)
at nodeLinkFn (http://localhost:55203/Scripts/angular.js:4400:24)
at compositeLinkFn (http://localhost:55203/Scripts/angular.js:4015:15)
at publicLinkFn (http://localhost:55203/Scripts/angular.js:3920:30)
at resumeBootstrapInternal (http://localhost:55203/Scripts/angular.js:983:27)
at Object.$get.Scope.$eval (http://localhost:55203/Scripts/angular.js:8057:28)
at Object.$get.Scope.$apply (http://localhost:55203/Scripts/angular.js:8137:23)
at resumeBootstrapInternal (http://localhost:55203/Scripts/angular.js:981:15) <div ng-grid="gridOptions" style="height: 400px" class="ng-scope"> angular.js:5754
The only similar issue I found by googling is https://github.com/angular-ui/ng-grid/issues/60, but it does not seem to be related to my case as there the grid options were setup too late.
The server side just has an API RESTful controller returning server-paged, sorted and filtered items.
You can find the full repro solution here (just save, unzip and open; all the dependencies come from NuGet); see the readme.txt file for more information:
http://sdrv.ms/167gv0F
Just start the app and click MODEL in the upper right corner to run the TypeScript app throwing the error. The whole app is composed of 1 controller, 1 service and 1 model.
For starters like me, it would be nice to have a simple working example like this one. Could anyone help?
This error means gridOptions has not yet been defined by the time that Angular attempts to parse ng-grid="yourArray", where yourArray is the same array supplied to gridOptions. I had the same problem after refactoring a previously working ng-grid.
So gridOptions must be defined before the element which has ng-grid="yourArray" attribute applied to it (rather than within that element's own controller).
I resolved this by defining gridOptions in an outer element somewhere (on global/app scope, for instance).
P.S. Maybe there is a better way, but this has worked for me.
Where you are adding data to your grid?
If you are writing $scope.myGrid={data:"someObj"}; in a success call then it won't work.
See the below reason:(which is listed in https://github.com/angular-ui/ng-grid/issues/60)
You can't define the grid options in the success call. You need to define
them on the scope in your controller and then set the data or column
definitions, etc... from the success call.
What you have to do?, First is to see how this made ​​your project and revizar if your queries or data access, the beams through a service, if so this I must add the file that manages routes app, the client side.
remain so.
'use strict';
angular.module('iseApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
**'ngGrid',**
'campaignServices',
'dialinglistServices',
'itemServices'
])
.config(function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
As you are adding your ng-grid in a directive, you have to make sure the grid options are loaded before it tries to parse your html.
You could set a boolean in your link function :
scope.isDirectiveLoaded=true;
And then, in your template, use a ng-if :
<div ng-if="isDirectiveLoaded">
<div ng-grid="myGrid"/>
</div>
I got to the same issue, empty grid was rendered.
The way I got to it in the end was to setup my this.gridOptions in the constructor of the controller, within the component. In the options everything is referenced with $ctrl like this. So the data references $ctrl.gridData. gridData is specified as a property in my component controller. $ctrl is not defined as a property.
This was done in the constructor before the data was loaded. this.gridData was defined after in the constructor and then populated later in another function. The options were defined first, I think this is important from some things I read.
For the event hooks pass null instead of $scope.
this.gridOptions = {
enableGridMenu: true,
minRowsToShow: 25,
rowHeight: 36,
enableRowHashing: true,
data: '$ctrl.gridData',
rowTemplate: this.$rootScope.blockedRowTemplate,
onRegisterApi: ($ctrl) => {
this.gridApi = $ctrl;
this.gridApi.colMovable.on.columnPositionChanged(null, (colDef, originalPosition, newPosition) => {
this.saveState();
});
this.gridApi.colResizable.on.columnSizeChanged(null, (colDef, deltaChange) => {
this.saveState();
});
this.gridApi.core.on.columnVisibilityChanged(null, (column) => {
this.saveState();
});
this.gridApi.core.on.sortChanged(null, (grid, sortColumns) => {
this.saveState();
});
this.gridApi.core.on.filterChanged(null, (grid, sortColumns) => {
this.saveState();
});
}
};
In the row template I was referencing functions defined in my component. Before conversion to a component I referenced functions like this:
ng-click="grid.appScope.jumpToExport(row.entity);"
After conversion to the component I needed to add the $ctrl before the function name like this
ng-click="grid.appScope.$ctrl.jumpToExport(row.entity);"
And this is how the component is referenced in the html
<div ui-grid="$ctrl.gridOptions" ng-if="$ctrl.gridData.length != undefined && $ctrl.gridData.length > 0" class="data-grid" ui-grid-save-state ui-grid-resize-columns ui-grid-move-columns></div>